/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Extensions.CognitoAuthentication.Util;
using System;
using System.Security.Cryptography;
namespace Amazon.Extensions.CognitoAuthentication
{
///
/// Class used to carry out the HKDF protocol
/// Adapted from https://tools.ietf.org/html/rfc5869
///
internal class HkdfSha256
{
internal const int HLen = 32;
///
/// The pseudorandom key for the HKDF protocol. Prk is generated in the
/// constructor of Hkdf and once set cannot be changed.
///
internal byte[] Prk { get; private set; }
///
/// The HMACSHA256 hashing algorithm object to compute hashes while carrying out
/// the HKDF protocol
///
private HMACSHA256 HmacSha256 { get; set; }
///
/// Creates an instance of Hkdf and performs the "Extract" portion of the HKDF protocol
/// to generate the pseudorandom key property
///
/// The salt for the extraction hash
/// The input key material for the extraction hash
internal HkdfSha256(byte[] salt, byte[] ikm)
{
HmacSha256 = new HMACSHA256(salt);
Prk = HmacSha256.ComputeHash(ikm);
}
///
/// Performs the "Expand" portion of the Hkdf protocol to make
/// the ouput key material of the desired length
///
/// Contextual information for the expansion hash
/// The desired length of the output key material in bytes
/// Returns the output key material for the expansion protion of the HKDF protocol
internal byte[] Expand(byte[] info, int length)
{
if (length > HLen * 255)
{
throw new ArgumentException("Length must be <= " + HLen * 255);
}
byte[] outputKeyMaterial = new byte[length];
HmacSha256 = new HMACSHA256(Prk);
byte currentByte = 1;
byte[] hashedBlock = new byte[0];
byte[] currentBlock;
int bytesRemaining = length;
while(bytesRemaining > 0)
{
currentBlock = CognitoAuthHelper.CombineBytes(hashedBlock, info, new byte[] { currentByte });
hashedBlock = HmacSha256.ComputeHash(currentBlock);
Buffer.BlockCopy(hashedBlock, 0, outputKeyMaterial, length-bytesRemaining, Math.Min(hashedBlock.Length, bytesRemaining));
bytesRemaining -= hashedBlock.Length;
currentByte++;
}
return outputKeyMaterial;
}
}
}