THE SQL Server Blog Spot on the Web

Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | |
in Search

The Bit Bucket (Greg Low): IDisposable

Ramblings of Greg Low (SQL Server MVP, MCM and Microsoft RD) - SQL Down Under

Step one in producing Shared Access Signatures for SQL Server Data Files in Azure - Generating a SHA2 256 HMAC Hash using Native T-SQL

One of the projects I have been working on as time permits is to construct a way to create a shared access signature using native T-SQL. Shared access signatures are needed when working with SQL Server data files in Azure.

Mostly, DBAs would use a programmatic utility such as Azure Storage Explorer to generate these signatures.

It is also straightforward to do so with SQL CLR based code but this is not enabled in all environments.

So I set about trying to create the required signatures using native T-SQL. I’m not concerned about the slow cryptographic performance as this won’t be a regular operation.

I found some excellent code from Ryan Malayter on github: https://gist.github.com/rmalayter/3130462 that does a SHA –1 or SHA – 512 hash. I’ve modified and enhanced it a bit so that it does SHA2-256 and performs the functions I need.

After the function code, there is a set of test code that shows how the function satisfies the HMAC_SHA256 test vectors. Hope that helps someone. I’ll post more as I complete the next step in building a SAS generator.

USE tempdb;

GO

 

ALTER FUNCTION dbo.HMAC_SHA256

(

    @HashKey varbinary(8000),

    @ValueToHash varbinary(8000)

)

RETURNS binary(32)

AS

BEGIN

 

    -- Based on concept code from Ryan Malayter: https://gist.github.com/rmalayter/3130462

    DECLARE @k_ipad_partial bigint = CAST(0x3636363636363636 AS bigint);

    DECLARE @k_opad_partial bigint = CAST(0x5C5C5C5C5C5C5C5C AS bigint);

    

    IF LEN(@HashKey) > 64

    BEGIN

        SET @HashKey = CAST(HASHBYTES('SHA2_256', @HashKey) AS binary (64));

    END ELSE BEGIN

        SET @HashKey = CAST(@HashKey AS binary (64));

    END;

     

    DECLARE @k_ipad binary(64)

      = CAST((SUBSTRING(@HashKey, 1, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 9, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 17, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 25, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 33, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 41, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 49, 8) ^ @k_ipad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 57, 8) ^ @k_ipad_partial) AS binary(8));

   

   

    DECLARE @k_opad binary(64)

      = CAST((SUBSTRING(@HashKey, 1, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 9, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 17, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 25, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 33, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 41, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 49, 8) ^ @k_opad_partial) AS binary(8))

      + CAST((SUBSTRING(@HashKey, 57, 8) ^ @k_opad_partial) AS binary(8));

    

    RETURN HASHBYTES('SHA2_256', @k_opad + HASHBYTES('SHA2_256', @k_ipad + @ValueToHash));

END;

GO

 

-- Test with RFC4231 test vectors

 

DECLARE @KeyToUse varbinary(4000);

DECLARE @ValueToHash varbinary(4000);

DECLARE @HashedValue varbinary(32);

DECLARE @ExpectedResult varbinary(32);

 

-- Test 1

 

SET @KeyToUse = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b;

SET @ValueToHash = CAST('Hi There' AS varbinary(1000));

SET @ExpectedResult = 0xb0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7;

SET @HashedValue = dbo.HMAC_SHA256(@KeyToUse, @ValueToHash);

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 1 passed';

END ELSE BEGIN

    PRINT '>>Test 1 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

 

-- Test 2

 

SET @KeyToUse = CAST('Jefe' AS varbinary(4));

SET @ValueToHash = CAST('what do ya want for nothing?' AS varbinary(1000));

SET @ExpectedResult = 0x5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843;

SET @HashedValue = dbo.HMAC_SHA256(@KeyToUse, @ValueToHash);

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 2 passed';

END ELSE BEGIN

    PRINT '>>Test 2 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

 

-- Test 3

 

SET @KeyToUse = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

SET @ValueToHash = 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd;

SET @ExpectedResult = 0x773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe;

SET @HashedValue = dbo.HMAC_SHA256(@KeyToUse, @ValueToHash);

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 3 passed';

END ELSE BEGIN

    PRINT '>>Test 3 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

 

-- Test 4

 

SET @KeyToUse = 0x0102030405060708090a0b0c0d0e0f10111213141516171819;

SET @ValueToHash = 0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd;

SET @ExpectedResult = 0x82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b;

SET @HashedValue = dbo.HMAC_SHA256(@KeyToUse, @ValueToHash);

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 4 passed';

END ELSE BEGIN

    PRINT '>>Test 4 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

 

-- Test 5

 

SET @KeyToUse = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c;

SET @ValueToHash = CAST('Test With Truncation' AS varbinary(4000));

SET @ExpectedResult = 0xa3b6167473100ee06e0c796c2955552b;

SET @HashedValue = CONVERT(varbinary(16),dbo.HMAC_SHA256(@KeyToUse, @ValueToHash));

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 5 passed';

END ELSE BEGIN

    PRINT '>>Test 5 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

 

-- Test 6

 

SET @KeyToUse = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

SET @ValueToHash = CAST('Test Using Larger Than Block-Size Key - Hash Key First' AS varbinary(4000));

SET @ExpectedResult = 0x60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54;

SET @HashedValue = dbo.HMAC_SHA256(@KeyToUse, @ValueToHash);

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 6 passed';

END ELSE BEGIN

    PRINT '>>Test 6 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

 

-- Test 7

 

SET @KeyToUse = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

SET @ValueToHash = CAST('This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.' AS varbinary(4000));

SET @ExpectedResult = 0x9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2;

SET @HashedValue = dbo.HMAC_SHA256(@KeyToUse, @ValueToHash);

IF @HashedValue = @ExpectedResult

BEGIN

    PRINT '>>Test 7 passed';

END ELSE BEGIN

    PRINT '>>Test 7 failed';

    PRINT 'Expected 0x' + CONVERT(varchar(1000), @ExpectedResult, 2);

    PRINT 'Received ' + COALESCE('0x' + CONVERT(varchar(1000), @HashedValue, 2),'NULL');

    PRINT ' ';

END;

Published Wednesday, July 09, 2014 9:50 PM by Greg Low

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
(required) 
Submit

This Blog

Syndication

Tags

No tags have been created or used yet.
Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement