Skip to content
This repository has been archived by the owner on Jul 26, 2023. It is now read-only.

Commit

Permalink
Merge pull request #74 from AArnott/BCryptWork
Browse files Browse the repository at this point in the history
Add BCrypt types and test for CCM block chaining mode
  • Loading branch information
AArnott committed Nov 23, 2015
2 parents 6b4cd50 + e5dcf9a commit 5a3cf60
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 6 deletions.
78 changes: 78 additions & 0 deletions src/BCrypt.Tests/BCrypt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,84 @@ public void EncryptDecrypt_NoPadding()
}
}

/// <summary>
/// Demonstrates use of an authenticated block chaining mode
/// that requires use of several more struct types than
/// the default CBC mode for AES.
/// </summary>
[Fact]
public unsafe void EncryptDecrypt_AesCcm()
{
var random = new Random();

using (var provider = BCryptOpenAlgorithmProvider(AlgorithmIdentifiers.BCRYPT_AES_ALGORITHM))
{
BCryptSetProperty(provider, PropertyNames.ChainingMode, ChainingModes.Ccm);

byte[] plainText;
byte[] cipherText;

var nonceBuffer = new byte[12];
random.NextBytes(nonceBuffer);

var tagLengths = BCryptGetProperty<BCRYPT_AUTH_TAG_LENGTHS_STRUCT>(provider, PropertyNames.AuthTagLength);
var tagBuffer = new byte[tagLengths.MaxLength];

int blockSize = BCryptGetProperty<int>(provider, PropertyNames.BlockLength);
plainText = new byte[blockSize];
random.NextBytes(plainText);

byte[] keyMaterial = new byte[blockSize];
RandomNumberGenerator.Create().GetBytes(keyMaterial);

using (var key = BCryptGenerateSymmetricKey(provider, keyMaterial))
{
var authInfo = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Create();
fixed (byte* pTagBuffer = &tagBuffer[0])
fixed (byte* pNonce = &nonceBuffer[0])
{
authInfo.pbNonce = new IntPtr(pNonce);
authInfo.cbNonce = nonceBuffer.Length;
authInfo.pbTag = new IntPtr(pTagBuffer);
authInfo.cbTag = tagBuffer.Length;

var pAuthInfo = new IntPtr(&authInfo);
int cipherTextLength;
BCryptEncrypt(key, plainText, plainText.Length, pAuthInfo, null, 0, null, 0, out cipherTextLength, BCryptEncryptFlags.None).ThrowOnError();
cipherText = new byte[cipherTextLength];
BCryptEncrypt(key, plainText, plainText.Length, pAuthInfo, null, 0, cipherText, cipherText.Length, out cipherTextLength, BCryptEncryptFlags.None).ThrowOnError();
}

Assert.NotEqual<byte>(plainText, cipherText);
}

// Renew the key to prove we can decrypt it with a fresh key.
using (var key = BCryptGenerateSymmetricKey(provider, keyMaterial))
{
byte[] decryptedText;

var authInfo = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Create();
fixed (byte* pTagBuffer = &tagBuffer[0])
fixed (byte* pNonce = &nonceBuffer[0])
{
authInfo.pbNonce = new IntPtr(pNonce);
authInfo.cbNonce = nonceBuffer.Length;
authInfo.pbTag = new IntPtr(pTagBuffer);
authInfo.cbTag = tagBuffer.Length;

var pAuthInfo = new IntPtr(&authInfo);
int plainTextLength;
BCryptDecrypt(key, cipherText, cipherText.Length, pAuthInfo, null, 0, null, 0, out plainTextLength, BCryptEncryptFlags.None).ThrowOnError();
decryptedText = new byte[plainTextLength];
BCryptEncrypt(key, cipherText, cipherText.Length, pAuthInfo, null, 0, decryptedText, decryptedText.Length, out plainTextLength, BCryptEncryptFlags.None).ThrowOnError();
Array.Resize(ref decryptedText, plainTextLength);
}

Assert.Equal<byte>(plainText, decryptedText);
}
}
}

[Fact]
public void Hash()
{
Expand Down
36 changes: 36 additions & 0 deletions src/BCrypt/BCrypt+AuthModeFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.

namespace PInvoke
{
using System;

/// <content>
/// Contains the <see cref="AuthModeFlags"/> nested type.
/// </content>
public partial class BCrypt
{
/// <summary>
/// Flags for the <see cref="BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.dwFlags"/> field.
/// </summary>
[Flags]
public enum AuthModeFlags
{
/// <summary>
/// No flags.
/// </summary>
None = 0x0,

/// <summary>
/// Indicates that <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> function calls are being chained and that the MAC value will not be computed. On the last call in the chain, clear this value to compute the MAC value for the entire chain.
/// </summary>
BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG = 0x1,

/// <summary>
/// Indicates that this <see cref="BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO"/> structure is being used in a sequence of chained <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> or <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> function calls. This flag is set and maintained internally.
/// Note: During the chaining sequence, this flag value is maintained internally and must not be changed or the value of the computed MAC will be corrupted.
/// </summary>
BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG = 0x2,
}
}
}
138 changes: 138 additions & 0 deletions src/BCrypt/BCrypt+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.

namespace PInvoke
{
using System;
using System.Runtime.InteropServices;

/// <content>
/// Contains the <see cref="BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO"/> nested type.
/// </content>
public partial class BCrypt
{
/// <summary>
/// Used with the <see cref="BCryptEncrypt(SafeKeyHandle, byte[], IntPtr, byte[], BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], IntPtr, byte[], BCryptEncryptFlags)"/> functions
/// to contain additional information related to authenticated cipher modes.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
{
/// <summary>
/// The version of the <see cref="BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO"/> struct.
/// </summary>
public const uint BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = 1;

/// <summary>
/// The size, in bytes, of this structure.
/// Do not set this field directly. Use the <see cref="Create"/> method instead.
/// </summary>
public uint cbSize;

/// <summary>
/// The version number of the structure.
/// The only supported value is <see cref="BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION"/>.
/// Do not set this field directly. Use the <see cref="Create"/> method instead.
/// </summary>
public uint dwInfoVersion;

/// <summary>
/// A pointer to a buffer that contains a nonce.
/// The Microsoft algorithm providers for Advanced Encryption Standard (AES)
/// require a nonce for the Counter with CBC-MAC (CCM) and Galois/Counter Mode (GCM)
/// chaining modes, and will return an error if none is present.
/// If a nonce is not used, this member must be set to NULL.
/// </summary>
public IntPtr pbNonce;

/// <summary>
/// The size, in bytes, of the buffer pointed to by the <see cref="pbNonce"/> member.
/// If a nonce is not used, this member must be set to zero.
/// </summary>
public int cbNonce;

/// <summary>
/// A pointer to a buffer that contains the authenticated data.
/// This is data that will be included in the Message Authentication Code (MAC) but not encrypted.
/// If there is no authenticated data, this member must be set to NULL.
/// </summary>
public IntPtr pbAuthData;

/// <summary>
/// The size, in bytes, of the buffer pointed to by the <see cref="pbAuthData"/> member.
/// If there is no authenticated data, this member must be set to zero.
/// </summary>
public int cbAuthData;

/// <summary>
/// A pointer to a buffer.
/// The use of this member depends on the function to which the structure is passed.
/// For <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/>
/// the buffer will receive the authentication tag.
/// For <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/>
/// the buffer contains the authentication tag to be checked against.
/// If there is no tag, this member must be set to NULL.
/// </summary>
public IntPtr pbTag;

/// <summary>
/// The size, in bytes, of the <see cref="pbTag"/> buffer.
/// The buffer must be long enough to include the whole authentication tag.
/// Some authentication modes, such as CCM and GCM, support checking against a tag
/// with multiple lengths. To obtain the valid authentication tag lengths use
/// <see cref="BCryptGetProperty{T}(SafeHandle, string, BCryptGetPropertyFlags)"/> to query the <see cref="PropertyNames.AuthTagLength"/> property.
/// If there is no tag, this member must be set to zero.
/// </summary>
public int cbTag;

/// <summary>
/// A pointer to a buffer that stores the partially computed MAC between calls to <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> when chaining encryption or decryption.
/// If the input to encryption or decryption is scattered across multiple buffers, then you must chain calls to the <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> functions. Chaining is indicated by setting the <see cref="AuthModeFlags.BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG"/> flag in the <see cref="dwFlags"/> member.
/// This buffer must be supplied by the caller and must be at least as large as the maximum length of an authentication tag for the cipher you are using. To get the valid authentication tag lengths, use <see cref="BCryptGetProperty{T}(SafeHandle, string, BCryptGetPropertyFlags)"/> to query the <see cref="PropertyNames.AuthTagLength"/> property.
/// If <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> calls are not being chained, this member must be set to NULL.
/// </summary>
public IntPtr pbMacContext;

/// <summary>
/// The size, in bytes, of the buffer pointed to by the <see cref="pbMacContext"/> member.
/// If <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> calls are not being chained, this member must be set to zero.
/// </summary>
public int cbMacContext;

/// <summary>
/// The length, in bytes, of additional authenticated data (AAD) to be used by the <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> and <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> functions. This member is used only when chaining calls.
/// This member is used only when the <see cref="AuthModeFlags.BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG"/> flag in the <see cref="dwFlags"/> member is set.
/// On the first call to <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> or <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> you must set this field to zero.
/// Note: During the chaining sequence, this member is maintained internally and must not be changed or the value of the computed MAC will be corrupted.
/// </summary>
public int cbAAD;

/// <summary>
/// The length, in bytes, of the payload data that was encrypted or decrypted. This member is used only when chaining calls.
/// This member is used only when the <see cref="AuthModeFlags.BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG"/> flag in the <see cref="dwFlags"/> member is set.
/// On the first call to <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> or <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> you must set this field to zero.
/// Note: During the chaining sequence, this member is maintained internally and must not be changed or the value of the computed MAC will be corrupted.
/// </summary>
public long cbData;

/// <summary>
/// This flag is used when chaining <see cref="BCryptEncrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> or <see cref="BCryptDecrypt(SafeKeyHandle, byte[], int, IntPtr, byte[], int, byte[], int, out int, BCryptEncryptFlags)"/> function calls.
/// If calls are not being chained, this member must be set to zero.
/// </summary>
public AuthModeFlags dwFlags;

/// <summary>
/// Initializes a new instance of the <see cref="BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO"/> struct.
/// </summary>
/// <returns>An initialized instance.</returns>
public static BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO Create()
{
return new BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO)),
dwInfoVersion = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION,
};
}
}
}
}
50 changes: 50 additions & 0 deletions src/BCrypt/BCrypt+BCRYPT_AUTH_TAG_LENGTHS_STRUCT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.

namespace PInvoke
{
using System.Collections.Generic;
using System.Runtime.InteropServices;

/// <content>
/// Contains the <see cref="BCRYPT_AUTH_TAG_LENGTHS_STRUCT"/> nested type.
/// </content>
public partial class BCrypt
{
/// <summary>
/// defines the range of tag sizes that are supported by the provider. This structure is used with the <see cref="PropertyNames.AuthTagLength"/> property.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct BCRYPT_AUTH_TAG_LENGTHS_STRUCT
{
/// <summary>
/// The minimum length, in bytes, of a tag.
/// </summary>
public int MinLength;

/// <summary>
/// The maximum length, in bytes, of a tag.
/// </summary>
public int MaxLength;

/// <summary>
/// The number of bytes that the tag size can be incremented between dwMinLength and dwMaxLength.
/// </summary>
public int Increment;

/// <summary>
/// Gets a sequence of allowed tag sizes, from smallest to largest.
/// </summary>
public IEnumerable<int> TagSizes
{
get
{
for (int tagLength = this.MinLength; tagLength <= this.MaxLength; tagLength += this.Increment)
{
yield return tagLength;
}
}
}
}
}
}
5 changes: 5 additions & 0 deletions src/BCrypt/BCrypt+BCRYPT_KEY_DATA_BLOB_HEADER.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public partial class BCrypt
[StructLayout(LayoutKind.Sequential)]
public struct BCRYPT_KEY_DATA_BLOB_HEADER
{
/// <summary>
/// The version of the <see cref="BCRYPT_KEY_DATA_BLOB_HEADER"/> struct.
/// </summary>
public const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 1;

/// <summary>
/// The magic value for the key.
/// This member must be the following value.
Expand Down
2 changes: 1 addition & 1 deletion src/BCrypt/BCrypt+PropertyNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public static class PropertyNames
public const string BCRYPT_HASH_BLOCK_LENGTH = "HashBlockLength";

/// <summary>
/// The authentication tag lengths that are supported by the algorithm. This property is a BCRYPT_AUTH_TAG_LENGTHS_STRUCT structure. This property only applies to algorithms.
/// The authentication tag lengths that are supported by the algorithm. This property is a <see cref="BCRYPT_AUTH_TAG_LENGTHS_STRUCT"/> structure. This property only applies to algorithms.
/// </summary>
public const string BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength";

Expand Down
5 changes: 0 additions & 5 deletions src/BCrypt/BCrypt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ namespace PInvoke
/// </summary>
public static partial class BCrypt
{
/// <summary>
/// Version 1.
/// </summary>
public const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 1;

/// <summary>
/// Loads and initializes a CNG provider.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/BCrypt/BCrypt.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ItemGroup>
<Compile Include="BCrypt+AlgorithmIdentifiers.cs" />
<Compile Include="BCrypt+AsymmetricKeyBlobTypes.cs" />
<Compile Include="BCrypt+AuthModeFlags.cs" />
<Compile Include="BCrypt+BCryptBuffer.cs" />
<Compile Include="BCrypt+BCryptBufferDesc.cs" />
<Compile Include="BCrypt+BCryptCloseAlgorithmProviderFlags.cs" />
Expand All @@ -45,6 +46,8 @@
<Compile Include="BCrypt+BCryptSecretAgreementFlags.cs" />
<Compile Include="BCrypt+BCryptSetPropertyFlags.cs" />
<Compile Include="BCrypt+BCryptSignHashFlags.cs" />
<Compile Include="BCrypt+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.cs" />
<Compile Include="BCrypt+BCRYPT_AUTH_TAG_LENGTHS_STRUCT.cs" />
<Compile Include="BCrypt+BCRYPT_KEY_DATA_BLOB_HEADER.cs" />
<Compile Include="BCrypt+BCRYPT_KEY_LENGTHS_STRUCT.cs" />
<Compile Include="BCrypt+BufferType.cs" />
Expand Down

0 comments on commit 5a3cf60

Please sign in to comment.