diff --git a/src/BCrypt.Tests/BCrypt.cs b/src/BCrypt.Tests/BCrypt.cs index 497486a7..50809dff 100644 --- a/src/BCrypt.Tests/BCrypt.cs +++ b/src/BCrypt.Tests/BCrypt.cs @@ -123,6 +123,84 @@ public void EncryptDecrypt_NoPadding() } } + /// + /// Demonstrates use of an authenticated block chaining mode + /// that requires use of several more struct types than + /// the default CBC mode for AES. + /// + [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(provider, PropertyNames.AuthTagLength); + var tagBuffer = new byte[tagLengths.MaxLength]; + + int blockSize = BCryptGetProperty(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(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(plainText, decryptedText); + } + } + } + [Fact] public void Hash() { diff --git a/src/BCrypt/BCrypt+AuthModeFlags.cs b/src/BCrypt/BCrypt+AuthModeFlags.cs new file mode 100644 index 00000000..8b262f54 --- /dev/null +++ b/src/BCrypt/BCrypt+AuthModeFlags.cs @@ -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; + + /// + /// Contains the nested type. + /// + public partial class BCrypt + { + /// + /// Flags for the field. + /// + [Flags] + public enum AuthModeFlags + { + /// + /// No flags. + /// + None = 0x0, + + /// + /// Indicates that and 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. + /// + BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG = 0x1, + + /// + /// Indicates that this structure is being used in a sequence of chained or 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. + /// + BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG = 0x2, + } + } +} diff --git a/src/BCrypt/BCrypt+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.cs b/src/BCrypt/BCrypt+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.cs new file mode 100644 index 00000000..d0eff50c --- /dev/null +++ b/src/BCrypt/BCrypt+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.cs @@ -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; + + /// + /// Contains the nested type. + /// + public partial class BCrypt + { + /// + /// Used with the and functions + /// to contain additional information related to authenticated cipher modes. + /// + [StructLayout(LayoutKind.Sequential)] + public struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO + { + /// + /// The version of the struct. + /// + public const uint BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = 1; + + /// + /// The size, in bytes, of this structure. + /// Do not set this field directly. Use the method instead. + /// + public uint cbSize; + + /// + /// The version number of the structure. + /// The only supported value is . + /// Do not set this field directly. Use the method instead. + /// + public uint dwInfoVersion; + + /// + /// 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. + /// + public IntPtr pbNonce; + + /// + /// The size, in bytes, of the buffer pointed to by the member. + /// If a nonce is not used, this member must be set to zero. + /// + public int cbNonce; + + /// + /// 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. + /// + public IntPtr pbAuthData; + + /// + /// The size, in bytes, of the buffer pointed to by the member. + /// If there is no authenticated data, this member must be set to zero. + /// + public int cbAuthData; + + /// + /// A pointer to a buffer. + /// The use of this member depends on the function to which the structure is passed. + /// For + /// the buffer will receive the authentication tag. + /// For + /// the buffer contains the authentication tag to be checked against. + /// If there is no tag, this member must be set to NULL. + /// + public IntPtr pbTag; + + /// + /// The size, in bytes, of the 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 + /// to query the property. + /// If there is no tag, this member must be set to zero. + /// + public int cbTag; + + /// + /// A pointer to a buffer that stores the partially computed MAC between calls to and when chaining encryption or decryption. + /// If the input to encryption or decryption is scattered across multiple buffers, then you must chain calls to the and functions. Chaining is indicated by setting the flag in the 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 to query the property. + /// If and calls are not being chained, this member must be set to NULL. + /// + public IntPtr pbMacContext; + + /// + /// The size, in bytes, of the buffer pointed to by the member. + /// If and calls are not being chained, this member must be set to zero. + /// + public int cbMacContext; + + /// + /// The length, in bytes, of additional authenticated data (AAD) to be used by the and functions. This member is used only when chaining calls. + /// This member is used only when the flag in the member is set. + /// On the first call to or 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. + /// + public int cbAAD; + + /// + /// 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 flag in the member is set. + /// On the first call to or 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. + /// + public long cbData; + + /// + /// This flag is used when chaining or function calls. + /// If calls are not being chained, this member must be set to zero. + /// + public AuthModeFlags dwFlags; + + /// + /// Initializes a new instance of the struct. + /// + /// An initialized instance. + 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, + }; + } + } + } +} diff --git a/src/BCrypt/BCrypt+BCRYPT_AUTH_TAG_LENGTHS_STRUCT.cs b/src/BCrypt/BCrypt+BCRYPT_AUTH_TAG_LENGTHS_STRUCT.cs new file mode 100644 index 00000000..1fd5e9bc --- /dev/null +++ b/src/BCrypt/BCrypt+BCRYPT_AUTH_TAG_LENGTHS_STRUCT.cs @@ -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; + + /// + /// Contains the nested type. + /// + public partial class BCrypt + { + /// + /// defines the range of tag sizes that are supported by the provider. This structure is used with the property. + /// + [StructLayout(LayoutKind.Sequential)] + public struct BCRYPT_AUTH_TAG_LENGTHS_STRUCT + { + /// + /// The minimum length, in bytes, of a tag. + /// + public int MinLength; + + /// + /// The maximum length, in bytes, of a tag. + /// + public int MaxLength; + + /// + /// The number of bytes that the tag size can be incremented between dwMinLength and dwMaxLength. + /// + public int Increment; + + /// + /// Gets a sequence of allowed tag sizes, from smallest to largest. + /// + public IEnumerable TagSizes + { + get + { + for (int tagLength = this.MinLength; tagLength <= this.MaxLength; tagLength += this.Increment) + { + yield return tagLength; + } + } + } + } + } +} diff --git a/src/BCrypt/BCrypt+BCRYPT_KEY_DATA_BLOB_HEADER.cs b/src/BCrypt/BCrypt+BCRYPT_KEY_DATA_BLOB_HEADER.cs index 1839f6f1..805b8877 100644 --- a/src/BCrypt/BCrypt+BCRYPT_KEY_DATA_BLOB_HEADER.cs +++ b/src/BCrypt/BCrypt+BCRYPT_KEY_DATA_BLOB_HEADER.cs @@ -17,6 +17,11 @@ public partial class BCrypt [StructLayout(LayoutKind.Sequential)] public struct BCRYPT_KEY_DATA_BLOB_HEADER { + /// + /// The version of the struct. + /// + public const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 1; + /// /// The magic value for the key. /// This member must be the following value. diff --git a/src/BCrypt/BCrypt+PropertyNames.cs b/src/BCrypt/BCrypt+PropertyNames.cs index 5f1d7f59..41957803 100644 --- a/src/BCrypt/BCrypt+PropertyNames.cs +++ b/src/BCrypt/BCrypt+PropertyNames.cs @@ -101,7 +101,7 @@ public static class PropertyNames public const string HashBlockLength = "HashBlockLength"; /// - /// 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 structure. This property only applies to algorithms. /// public const string AuthTagLength = "AuthTagLength"; diff --git a/src/BCrypt/BCrypt.cs b/src/BCrypt/BCrypt.cs index 9df7afe7..cf04c250 100644 --- a/src/BCrypt/BCrypt.cs +++ b/src/BCrypt/BCrypt.cs @@ -13,11 +13,6 @@ namespace PInvoke /// public static partial class BCrypt { - /// - /// Version 1. - /// - public const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 1; - /// /// Loads and initializes a CNG provider. /// diff --git a/src/BCrypt/BCrypt.csproj b/src/BCrypt/BCrypt.csproj index bb92c56c..474b4187 100644 --- a/src/BCrypt/BCrypt.csproj +++ b/src/BCrypt/BCrypt.csproj @@ -25,6 +25,7 @@ + @@ -45,6 +46,8 @@ + +