private static void Encrypt_ShouldBeReversible_UsingSecureSymmetricKey(SymmetricAlgorithmSpecification algorithm, SecureSymmetricKeyDerivationMode derivationMode) { using (var randomnessProvider = RandomNumberGenerator.Create()) { // Arrange. var binarySerializer = new BinaryPassThroughSerializer(); var target = new SymmetricProcessor <Byte[]>(randomnessProvider, binarySerializer); var plaintextObject = new Byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; using (var key = SecureSymmetricKey.New(algorithm, derivationMode)) { // Act. var ciphertextResult = target.Encrypt(plaintextObject, key); // Assert. ciphertextResult.Should().NotBeNullOrEmpty(); ciphertextResult.Count(value => value == 0x00).Should().NotBe(ciphertextResult.Length); // Act. var plaintextResult = target.Decrypt(ciphertextResult, key); // Assert. plaintextResult.Should().NotBeNullOrEmpty(); plaintextResult.Length.Should().Be(plaintextObject.Length); plaintextResult.Count(value => value == 0x00).Should().NotBe(plaintextResult.Length); for (var i = 0; i < plaintextResult.Length; i++) { // Assert. plaintextResult[i].Should().Be(plaintextObject[i]); } } } }
private static void Encrypt_ShouldBeReversible_UsingSecureSymmetricKey(SymmetricAlgorithmSpecification algorithm) { using (var randomnessProvider = RandomNumberGenerator.Create()) { // Arrange. var target = new SymmetricStringProcessor(randomnessProvider); var plaintextObject = "䆟`ಮ䷆ʘ‣⦸⏹ⰄͶa✰ṁ亡Zᨖ0༂⽔9㗰"; using (var key = SecureSymmetricKey.New(algorithm)) { // Act. var ciphertext = target.Encrypt(plaintextObject, key); // Assert. ciphertext.Should().NotBeNullOrEmpty(); ciphertext.Count(value => value == 0x00).Should().NotBe(ciphertext.Length); // Act. var plaintext = target.Decrypt(ciphertext, key); // Assert. plaintext.Should().NotBeNullOrEmpty(); plaintext.Should().Be(plaintextObject); } } }
/* ================================================================================================================= * Twofish and Threefish are out-of-scope for MVP-01. * ================================================================================================================= * [TestMethod] * public void Encrypt_ShouldReproduceTestVector_ForTwofish128Ecb() * { * // Arrange. * var algorithm = SymmetricAlgorithmSpecification.Twofish128Ecb; * var blockLengthInBits = 128; * var cipherMode = CipherMode.ECB; * var key = new Byte[] { 0x9f, 0x58, 0x9f, 0x5c, 0xf6, 0x12, 0x2c, 0x32, 0xb6, 0xbf, 0xec, 0x2f, 0x2a, 0xe8, 0xc3, 0x5a }; * var initializationVector = (Byte[])null; * var plaintext = new Byte[] { 0xd4, 0x91, 0xdB, 0x16, 0xe7, 0xb1, 0xc3, 0x9e, 0x86, 0xcb, 0x08, 0x6b, 0x78, 0x9f, 0x54, 0x19 }; * var ciphertext = new Byte[] { 0x01, 0x9f, 0x98, 0x09, 0xde, 0x17, 0x11, 0x85, 0x8f, 0xaa, 0xc3, 0xa3, 0xba, 0x20, 0xfb, 0xc3 }; * * // Assert. * Encrypt_ShouldReproduceTestVector(algorithm, blockLengthInBits, cipherMode, key, initializationVector, plaintext, ciphertext); * }*/ private static void Encrypt_ShouldReproduceTestVector(SymmetricAlgorithmSpecification algorithm, Int32 blockLengthInBits, CipherMode cipherMode, Byte[] key, Byte[] initializationVector, Byte[] plaintext, Byte[] ciphertext) { using (var randomnessProvider = RandomNumberGenerator.Create()) { using (var secureKey = new SecureBuffer(key.Length)) { // Arrange. var blockLengthInBytes = (blockLengthInBits / 8); var target = new SymmetricBinaryProcessor(randomnessProvider); secureKey.Access(keyBuffer => { Array.Copy(key, keyBuffer, key.Length); }); // Act. var encryptResult = target.Encrypt(plaintext, secureKey, algorithm, initializationVector); // Assert. encryptResult.Should().NotBeNullOrEmpty(); // Arrange. var processedEncryptResult = cipherMode == CipherMode.CBC ? encryptResult.Skip(blockLengthInBytes).Take(ciphertext.Length).ToArray() : encryptResult.Take(ciphertext.Length).ToArray(); // Assert. processedEncryptResult.Length.Should().Be(ciphertext.Length); for (var i = 0; i < ciphertext.Length; i++) { // Assert. processedEncryptResult[i].Should().Be(ciphertext[i]); } } } }
/// <summary> /// Generates a new <see cref="SecureSymmetricKey" />. /// </summary> /// <param name="algorithm"> /// The symmetric-key algorithm that the generated key is derived to interoperate with. The default value is /// <see cref="SymmetricAlgorithmSpecification.Aes256Cbc" />. /// </param> /// <param name="derivationMode"> /// The mode used to derive the generated key. The default value is /// <see cref="SecureSymmetricKeyDerivationMode.XorLayeringWithSubstitution" />. /// </param> /// <returns> /// A new <see cref="SecureSymmetricKey" />. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="algorithm" /> is equal to <see cref="SymmetricAlgorithmSpecification.Unspecified" /> or /// <paramref name="derivationMode" /> is equal to <see cref="SecureSymmetricKeyDerivationMode.Unspecified" />. /// </exception> public static SecureSymmetricKey New(SymmetricAlgorithmSpecification algorithm, SecureSymmetricKeyDerivationMode derivationMode) { using (var keySource = new PinnedBuffer(KeySourceLengthInBytes, true)) { RandomnessProvider.GetBytes(keySource); return(New(algorithm, derivationMode, keySource)); } }
private T Decrypt(Byte[] ciphertext, PinnedBuffer key, SymmetricAlgorithmSpecification algorithm) { using (var cipher = algorithm.ToCipher(RandomnessProvider)) { using (var pinnedCiphertext = new PinnedBuffer(ciphertext, false)) { using (var plaintext = cipher.Decrypt(pinnedCiphertext, key)) { return(BinarySerializer.Deserialize(plaintext)); } } } }
/// <summary> /// Decrypts the specified ciphertext. /// </summary> /// <param name="ciphertext"> /// The ciphertext to decrypt. /// </param> /// <param name="key"> /// The key derivation bits used to transform the ciphertext. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the ciphertext. /// </param> /// <returns> /// The resulting plaintext object. /// </returns> /// <exception cref="SecurityException"> /// An exception was raised during decryption or deserialization. /// </exception> public T Decrypt(Byte[] ciphertext, SecureBuffer key, SymmetricAlgorithmSpecification algorithm) { try { var plaintext = default(T); key.Access(keyBuffer => { plaintext = Decrypt(ciphertext, keyBuffer, algorithm); }); return(plaintext); } catch { throw new SecurityException("The decryption operation failed."); } }
/// <summary> /// Encrypts the specified plaintext object. /// </summary> /// <param name="plaintextObject"> /// The plaintext object to encrypt. /// </param> /// <param name="key"> /// The key derivation bits used to transform the object. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the object. /// </param> /// <param name="initializationVector"> /// An initialization vector with length greater than or equal to the block size for the specified cipher (extra bytes are /// ignored), or <see langword="null" /> to generate a random initialization vector. /// </param> /// <returns> /// The resulting ciphertext. /// </returns> /// <exception cref="SecurityException"> /// An exception was raised during encryption or serialization. /// </exception> public Byte[] Encrypt(T plaintextObject, SecureBuffer key, SymmetricAlgorithmSpecification algorithm, Byte[] initializationVector) { try { var ciphertext = (Byte[])null; key.Access(keyBuffer => { ciphertext = Encrypt(plaintextObject, keyBuffer, algorithm, initializationVector); }); return(ciphertext); } catch { throw new SecurityException("The encryption operation failed."); } }
private SecureSymmetricKey(SymmetricAlgorithmSpecification algorithm, SecureSymmetricKeyDerivationMode derivationMode, PinnedBuffer keySource) : base(ConcurrencyControlMode.SingleThreadLock) { Algorithm = algorithm.RejectIf().IsEqualToValue(SymmetricAlgorithmSpecification.Unspecified, nameof(algorithm)); DerivationMode = derivationMode.RejectIf().IsEqualToValue(SecureSymmetricKeyDerivationMode.Unspecified, nameof(derivationMode)); KeySource = new SecureBuffer(KeySourceLengthInBytes); LazyPbkdf2Provider = new Lazy <Rfc2898DeriveBytes>(InitializePbkdf2Algorithm, LazyThreadSafetyMode.ExecutionAndPublication); // Gather information about the derived key. var keyBitLength = algorithm.ToKeyBitLength(); DerivedKeyLength = (keyBitLength / 8); BlockWordCount = (keyBitLength / 32); BlockCount = (KeySourceWordCount / BlockWordCount); // Copy in the key source bits. KeySource.Access(buffer => Array.Copy(keySource, buffer, buffer.Length)); }
private Byte[] Encrypt(T plaintextObject, PinnedBuffer key, SymmetricAlgorithmSpecification algorithm, Byte[] initializationVector) { var plaintext = BinarySerializer.Serialize(plaintextObject); var plaintextLength = plaintext.Length; using (var pinnedPlaintext = new PinnedBuffer(plaintextLength, true)) { Array.Copy(plaintext, pinnedPlaintext, plaintextLength); using (var cipher = algorithm.ToCipher(RandomnessProvider)) { switch (cipher.Mode) { case CipherMode.CBC: using (var processedInitializationVector = new PinnedBuffer(cipher.BlockSizeInBytes, true)) { if (initializationVector is null) { RandomnessProvider.GetBytes(processedInitializationVector); } else { Array.Copy(initializationVector.RejectIf(argument => argument.Length < cipher.BlockSizeInBytes, nameof(initializationVector)), processedInitializationVector, cipher.BlockSizeInBytes); } return(cipher.Encrypt(pinnedPlaintext, key, processedInitializationVector)); } case CipherMode.ECB: return(cipher.Encrypt(pinnedPlaintext, key, null)); default: throw new InvalidOperationException($"The specified cipher mode, {cipher.Mode}, is not supported."); } } } }
/// <summary> /// Generates a new <see cref="SecureSymmetricKey" />. /// </summary> /// <param name="algorithm"> /// The symmetric-key algorithm that the generated key is derived to interoperate with. The default value is /// <see cref="SymmetricAlgorithmSpecification.Aes256Cbc" />. /// </param> /// <param name="derivationMode"> /// The mode used to derive the generated key. The default value is /// <see cref="SecureSymmetricKeyDerivationMode.XorLayeringWithSubstitution" />. /// </param> /// <param name="keySource"> /// A buffer comprising 384 bytes (3,072 bits) from which the private key is derived. /// </param> /// <returns> /// A new <see cref="SecureSymmetricKey" />. /// </returns> /// <exception cref="ArgumentException"> /// <paramref name="keySource" /> is not 384 bytes in length. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="algorithm" /> is equal to <see cref="SymmetricAlgorithmSpecification.Unspecified" /> -or- /// <paramref name="derivationMode" /> is equal to <see cref="SecureSymmetricKeyDerivationMode.Unspecified" />. /// </exception> public static SecureSymmetricKey New(SymmetricAlgorithmSpecification algorithm, SecureSymmetricKeyDerivationMode derivationMode, PinnedBuffer keySource) { keySource.RejectIf(argument => argument.Length != KeySourceLengthInBytes, nameof(keySource), $"The key source is not {KeySourceLengthInBytes} bytes in length."); return(new SecureSymmetricKey(algorithm, derivationMode, keySource)); }
/// <summary> /// Generates a new <see cref="SecureSymmetricKey" />. /// </summary> /// <param name="algorithm"> /// The symmetric-key algorithm that the generated key is derived to interoperate with. The default value is /// <see cref="SymmetricAlgorithmSpecification.Aes256Cbc" />. /// </param> /// <returns> /// A new <see cref="SecureSymmetricKey" />. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="algorithm" /> is equal to <see cref="SymmetricAlgorithmSpecification.Unspecified" />. /// </exception> public static SecureSymmetricKey New(SymmetricAlgorithmSpecification algorithm) => New(algorithm, DefaultDerivationMode);
/// <summary> /// Encrypts the specified plaintext object. /// </summary> /// <param name="plaintextObject"> /// The plaintext object to encrypt. /// </param> /// <param name="key"> /// The key derivation bits used to transform the object. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the object. /// </param> /// <returns> /// The resulting ciphertext. /// </returns> /// <exception cref="SecurityException"> /// An exception was raised during encryption or serialization. /// </exception> public Byte[] Encrypt(T plaintextObject, SecureBuffer key, SymmetricAlgorithmSpecification algorithm) => Encrypt(plaintextObject, key, algorithm, null);
internal static Int32 ToKeyBitLength(this SymmetricAlgorithmSpecification target) { switch (target) { case SymmetricAlgorithmSpecification.Unspecified: return(default(Int32)); case SymmetricAlgorithmSpecification.Aes128Cbc: return(128); case SymmetricAlgorithmSpecification.Aes128Ecb: return(128); case SymmetricAlgorithmSpecification.Aes256Cbc: return(256); case SymmetricAlgorithmSpecification.Aes256Ecb: return(256); /* ================================================================================================================= * Twofish and Threefish are out-of-scope for MVP-01. * ================================================================================================================= * case SymmetricAlgorithmSpecification.Twofish128Cbc: * * return 128; * * case SymmetricAlgorithmSpecification.Twofish128Ecb: * * return 128; * * case SymmetricAlgorithmSpecification.Twofish192Cbc: * * return 192; * * case SymmetricAlgorithmSpecification.Twofish192Ecb: * * return 192; * * case SymmetricAlgorithmSpecification.Twofish256Cbc: * * return 256; * * case SymmetricAlgorithmSpecification.Twofish256Ecb: * * return 256; * * case SymmetricAlgorithmSpecification.Threefish256Cbc: * * return 256; * * case SymmetricAlgorithmSpecification.Threefish256Ecb: * * return 256; * * case SymmetricAlgorithmSpecification.Threefish512Cbc: * * return 512; * * case SymmetricAlgorithmSpecification.Threefish512Ecb: * * return 512; * * case SymmetricAlgorithmSpecification.Threefish1024Cbc: * * return 1024; * * case SymmetricAlgorithmSpecification.Threefish1024Ecb: * * return 1024;*/ default: throw new ArgumentException($"{target} is not a supported {nameof(SymmetricAlgorithmSpecification)}.", nameof(target)); } }
internal static SymmetricKeyCipher ToCipher(this SymmetricAlgorithmSpecification target, RandomNumberGenerator randomnessProvider) { switch (target) { case SymmetricAlgorithmSpecification.Unspecified: return(null); case SymmetricAlgorithmSpecification.Aes128Cbc: return(new Aes128CbcCipher(randomnessProvider)); case SymmetricAlgorithmSpecification.Aes128Ecb: return(new Aes128EcbCipher(randomnessProvider)); case SymmetricAlgorithmSpecification.Aes256Cbc: return(new Aes256CbcCipher(randomnessProvider)); case SymmetricAlgorithmSpecification.Aes256Ecb: return(new Aes256EcbCipher(randomnessProvider)); /* ================================================================================================================= * Twofish and Threefish are out-of-scope for MVP-01. * ================================================================================================================= * case SymmetricAlgorithmSpecification.Twofish128Cbc: * * return new Twofish128CbcCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Twofish128Ecb: * * return new Twofish128EcbCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Twofish192Cbc: * * return new Twofish192CbcCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Twofish192Ecb: * * return new Twofish192EcbCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Twofish256Cbc: * * return new Twofish256CbcCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Twofish256Ecb: * * return new Twofish256EcbCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Threefish256Cbc: * * return new Threefish256CbcCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Threefish256Ecb: * * return new Threefish256EcbCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Threefish512Cbc: * * return new Threefish512CbcCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Threefish512Ecb: * * return new Threefish512EcbCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Threefish1024Cbc: * * return new Threefish1024CbcCipher(randomnessProvider); * * case SymmetricAlgorithmSpecification.Threefish1024Ecb: * * return new Threefish1024EcbCipher(randomnessProvider);*/ default: throw new ArgumentException($"{target} is not a supported {nameof(SymmetricAlgorithmSpecification)}.", nameof(target)); } }
/// <summary> /// Generates a new <see cref="CascadingSymmetricKey" />. /// </summary> /// <param name="derivationMode"> /// The mode used to derive the generated keys. /// </param> /// <param name="firstLayerAlgorithm"> /// The algorithm for the first (inner-most) layer of encryption. /// </param> /// <param name="secondLayerAlgorithm"> /// The algorithm for the second layer of encryption. /// </param> /// <param name="thirdLayerAlgorithm"> /// The algorithm for the third layer of encryption. /// </param> /// <param name="fourthLayerAlgorithm"> /// The algorithm for the fourth (outer-most) layer of encryption. /// </param> /// <returns> /// A new <see cref="CascadingSymmetricKey" />. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="derivationMode" /> is equal to <see cref="SecureSymmetricKeyDerivationMode.Unspecified" /> -or- one or /// more algorithm layers are equal to <see cref="SymmetricAlgorithmSpecification.Unspecified" />. /// </exception> public static CascadingSymmetricKey New(SecureSymmetricKeyDerivationMode derivationMode, SymmetricAlgorithmSpecification firstLayerAlgorithm, SymmetricAlgorithmSpecification secondLayerAlgorithm, SymmetricAlgorithmSpecification thirdLayerAlgorithm, SymmetricAlgorithmSpecification fourthLayerAlgorithm) => new CascadingSymmetricKey(derivationMode, firstLayerAlgorithm, secondLayerAlgorithm, thirdLayerAlgorithm, fourthLayerAlgorithm);
/// <summary> /// Decrypts the specified Base64 string ciphertext. /// </summary> /// <typeparam name="T"> /// The type of the object that is decrypted. /// </typeparam> /// <param name="target"> /// The current <see cref="ISymmetricProcessor{T}" />. /// </param> /// <param name="ciphertext"> /// The Base64 string ciphertext to decrypt. /// </param> /// <param name="key"> /// The key derivation bits used to transform the ciphertext. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the ciphertext. /// </param> /// <returns> /// The resulting plaintext object. /// </returns> /// <exception cref="FormatException"> /// <paramref name="ciphertext" /> is not a valid Base64 string. /// </exception> /// <exception cref="SecurityException"> /// An exception was raised during decryption or deserialization. /// </exception> public static T DecryptFromBase64String <T>(this ISymmetricProcessor <T> target, String ciphertext, SecureBuffer key, SymmetricAlgorithmSpecification algorithm) => target.Decrypt(Convert.FromBase64String(ciphertext), key, algorithm);
/// <summary> /// Encrypts the specified plaintext object to a Base64 string. /// </summary> /// <typeparam name="T"> /// The type of the object that is encrypted. /// </typeparam> /// <param name="target"> /// The current <see cref="ISymmetricProcessor{T}" />. /// </param> /// <param name="plaintextObject"> /// The plaintext object to encrypt. /// </param> /// <param name="key"> /// The key derivation bits used to transform the object. /// </param> /// <param name="algorithm"> /// The algorithm specification used to transform the object. /// </param> /// <returns> /// The resulting ciphertext as a Base64 string. /// </returns> /// <exception cref="SecurityException"> /// An exception was raised during encryption or serialization. /// </exception> public static String EncryptToBase64String <T>(this ISymmetricProcessor <T> target, T plaintextObject, SecureBuffer key, SymmetricAlgorithmSpecification algorithm) => Convert.ToBase64String(target.Encrypt(plaintextObject, key, algorithm));
private static void Encrypt_ShouldBeReversible_UsingCascadingSymmetricKey_WithTwoLayers(SecureSymmetricKeyDerivationMode derivationMode, SymmetricAlgorithmSpecification firstLayerAlgorithm, SymmetricAlgorithmSpecification secondLayerAlgorithm) { using (var key = CascadingSymmetricKey.New(derivationMode, firstLayerAlgorithm, secondLayerAlgorithm)) { Encrypt_ShouldBeReversible_UsingCascadingSymmetricKey(key); } }