public void SymmetricEncryption(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Skip.If(mode.IsAuthenticated(), "This test is only for non-authenticated block modes."); bool badCombination = false; badCombination |= !mode.IsBlockCipher() && padding != SymmetricAlgorithmPadding.None; // Padding does not apply to streaming ciphers. badCombination |= name.IsBlockCipher() != mode.IsBlockCipher(); // Incompatible cipher and block mode. Func<ISymmetricKeyAlgorithmProvider> creator = () => WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(name, mode, padding); if (badCombination) { Assert.Throws<ArgumentException>(creator); this.logger.WriteLine("Expected exception thrown for invalid combination."); return; } using (var algorithm = creator()) { int keyLength = algorithm.LegalKeySizes.First().MinSize; var keyMaterial = WinRTCrypto.CryptographicBuffer.GenerateRandom(keyLength / 8); using (var key = algorithm.CreateSymmetricKey(keyMaterial)) { var ciphertext = WinRTCrypto.CryptographicEngine.Encrypt(key, new byte[algorithm.BlockLength], null); Assert.NotEqual(0, ciphertext.Length); } } }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricCryptographicKey"/> class. /// </summary> /// <param name="algorithm">The algorithm, initialized with the key.</param> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> internal SymmetricCryptographicKey(Platform.SymmetricAlgorithm algorithm, SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Requires.NotNull(algorithm, "algorithm"); this.algorithm = algorithm; this.Name = name; this.Mode = mode; this.Padding = padding; }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricKeyAlgorithmProvider"/> class. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> public SymmetricKeyAlgorithmProvider(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Requires.Argument(mode.IsBlockCipher() == name.IsBlockCipher(), nameof(mode), "Block chaining mode incompatible with cipher. Don't mix streaming and non-streaming ciphers and modes."); Requires.Argument(padding == SymmetricAlgorithmPadding.None || mode.IsBlockCipher(), nameof(padding), "Padding does not apply to streaming ciphers."); this.Name = name; this.Mode = mode; this.Padding = padding; }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricKeyAlgorithmProvider"/> class. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> public SymmetricKeyAlgorithmProvider(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { this.Name = name; this.Mode = mode; this.Padding = padding; this.Algorithm = BCryptOpenAlgorithmProvider(GetAlgorithmName(name)); try { BCryptSetProperty(this.Algorithm, PropertyNames.BCRYPT_CHAINING_MODE, GetChainingMode(mode)); } catch (PInvoke.Win32Exception ex) { throw new ArgumentException(ex.Message, ex); } }
/// <summary> /// Gets the platform enum value for the padding used by the specified algorithm. /// </summary> /// <param name="padding">The algorithm padding.</param> /// <returns>The platform-specific enum value for the padding.</returns> private static Platform.PaddingMode GetPadding(SymmetricAlgorithmPadding padding) { switch (padding) { case SymmetricAlgorithmPadding.None: return(Platform.PaddingMode.None); case SymmetricAlgorithmPadding.PKCS7: return(Platform.PaddingMode.PKCS7); case SymmetricAlgorithmPadding.Zeros: return(Platform.PaddingMode.Zeros); default: throw new ArgumentException(); } }
/// <summary> /// Gets the padding substring to include in the string /// passed to <see cref="Cipher.GetInstance(string)"/>. /// </summary> /// <param name="padding">The padding.</param> /// <returns>A value such as "PKCS7Padding", or "NoPadding" if no padding.</returns> private static string GetPaddingName(SymmetricAlgorithmPadding padding) { // The constants used here come from // http://www.bouncycastle.org/specifications.html switch (padding) { case SymmetricAlgorithmPadding.Zeros: // we apply Zeros padding ourselves, since BC does it wrong (?!) case SymmetricAlgorithmPadding.None: return("NoPadding"); case SymmetricAlgorithmPadding.PKCS7: return("PKCS7Padding"); default: throw new NotSupportedException(); } }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricCryptographicKey" /> class. /// </summary> /// <param name="provider">The provider that created this instance.</param> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> /// <param name="keyMaterial">The key.</param> internal SymmetricCryptographicKey(SymmetricKeyAlgorithmProvider provider, SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding, byte[] keyMaterial) { Requires.NotNull(provider, nameof(provider)); Requires.NotNull(keyMaterial, nameof(keyMaterial)); if (name == SymmetricAlgorithmName.Aes && mode == SymmetricAlgorithmMode.Ccm && padding == SymmetricAlgorithmPadding.None) { // On Android encryption misbehaves causing our unit tests to fail. throw new NotSupportedException(); } this.provider = provider; this.Name = name; this.Mode = mode; this.Padding = padding; this.key = new SecretKeySpec(keyMaterial, this.Name.GetString()); this.KeySize = keyMaterial.Length * 8; }
public void EncryptDecrypt_AES(int inputLength, SymmetricAlgorithmPadding padding, string expectedCiphertext) { byte[] iv = IV; byte[] plaintext = new byte[inputLength]; Array.Copy(this.bigData, plaintext, inputLength); var algorithm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmName.Aes, SymmetricAlgorithmMode.Cbc, padding); using (var key = algorithm.CreateSymmetricKey(Convert.FromBase64String(AesKeyMaterial))) { if (expectedCiphertext == null) { Assert.Throws <ArgumentException>( () => WinRTCrypto.CryptographicEngine.Encrypt(key, plaintext, iv)); } else { byte[] actualCipherText = WinRTCrypto.CryptographicEngine.Encrypt(key, plaintext, iv); Assert.Equal( expectedCiphertext, Convert.ToBase64String(actualCipherText)); byte[] expectedPlainText = plaintext; if (!PaddingPreservesPlaintextLength(padding)) { // Therefore the expected decrypted value will have a length that is a multiple // of the block length. int blockLength = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmName.Aes, SymmetricAlgorithmMode.Cbc, SymmetricAlgorithmPadding.Zeros) .BlockLength; int bytesBeyondLastBlockLength = expectedPlainText.Length % blockLength; if (bytesBeyondLastBlockLength > 0) { int growBy = blockLength - bytesBeyondLastBlockLength; Array.Resize(ref expectedPlainText, expectedPlainText.Length + growBy); } } byte[] actualPlainText = WinRTCrypto.CryptographicEngine.Decrypt(key, actualCipherText, iv); Assert.Equal( Convert.ToBase64String(expectedPlainText), Convert.ToBase64String(actualPlainText)); } } }
/// <summary> /// Initializes a new instance of the <see cref="CryptoTransformAdaptor"/> class. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> /// <param name="transform">The transform.</param> internal CryptoTransformAdaptor(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding, Cipher transform) { Requires.NotNull(transform, "transform"); this.name = name; this.mode = mode; this.padding = padding; this.transform = transform; }
/// <summary> /// Finds a composite <see cref="SymmetricAlgorithm"/> for the specified unit parts, if one exists. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> /// <param name="algorithm">Receives the composite algorithm enum value, if one exists.</param> /// <returns><c>true</c> if a match was found; otherwise <c>false</c>.</returns> public static bool TryAssemblyAlgorithm(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding, out SymmetricAlgorithm algorithm) { foreach (SymmetricAlgorithm assembled in Enum.GetValues(typeof(SymmetricAlgorithm))) { if (assembled.GetName() == name && assembled.GetMode() == mode && assembled.GetPadding() == padding) { algorithm = assembled; return(true); } } algorithm = (SymmetricAlgorithm)0; return(false); }
/// <summary> /// Gets the padding substring to include in the string /// passed to <see cref="Cipher.GetInstance(string)"/> /// </summary> /// <param name="padding">The padding.</param> /// <returns>A value such as "PKCS7Padding", or "NoPadding" if no padding.</returns> private static string GetPaddingName(SymmetricAlgorithmPadding padding) { // The constants used here come from // http://www.bouncycastle.org/specifications.html switch (padding) { case SymmetricAlgorithmPadding.Zeros: // we apply Zeros padding ourselves, since BC does it wrong (?!) case SymmetricAlgorithmPadding.None: return "NoPadding"; case SymmetricAlgorithmPadding.PKCS7: return "PKCS7Padding"; default: throw new NotSupportedException(); } }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricKeyAlgorithmProvider"/> class. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> public SymmetricKeyAlgorithmProvider(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Requires.Argument(mode.IsBlockCipher() == name.IsBlockCipher(), nameof(mode), "Block chaining mode incompatible with cipher. Don't mix streaming and non-streaming ciphers and modes."); Requires.Argument(padding == SymmetricAlgorithmPadding.None || mode.IsBlockCipher(), nameof(padding), "Padding does not apply to streaming ciphers."); this.Name = name; this.Mode = mode; this.Padding = padding; }
/// <inheritdoc /> public ISymmetricKeyAlgorithmProvider OpenAlgorithm(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { return(new SymmetricKeyAlgorithmProvider(name, mode, padding)); }
/// <summary> /// Gets the platform enum value for the padding used by the specified algorithm. /// </summary> /// <param name="padding">The algorithm padding.</param> /// <returns>The platform-specific enum value for the padding.</returns> private static Platform.PaddingMode GetPadding(SymmetricAlgorithmPadding padding) { switch (padding) { case SymmetricAlgorithmPadding.None: return Platform.PaddingMode.None; case SymmetricAlgorithmPadding.PKCS7: return Platform.PaddingMode.PKCS7; case SymmetricAlgorithmPadding.Zeros: return Platform.PaddingMode.Zeros; default: throw new ArgumentException(); } }
public void EncryptDecrypt_AES(int inputLength, SymmetricAlgorithmPadding padding, string expectedCiphertext) { byte[] iv = IV; byte[] plaintext = new byte[inputLength]; Array.Copy(this.bigData, plaintext, inputLength); using (var algorithm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmName.Aes, SymmetricAlgorithmMode.Cbc, padding)) { using (var key = algorithm.CreateSymmetricKey(Convert.FromBase64String(AesKeyMaterial))) { if (expectedCiphertext == null) { Assert.Throws<ArgumentException>( () => WinRTCrypto.CryptographicEngine.Encrypt(key, plaintext, iv)); } else { byte[] actualCipherText = WinRTCrypto.CryptographicEngine.Encrypt(key, plaintext, iv); Assert.Equal( expectedCiphertext, Convert.ToBase64String(actualCipherText)); byte[] expectedPlainText = plaintext; if (!PaddingPreservesPlaintextLength(padding)) { // Therefore the expected decrypted value will have a length that is a multiple // of the block length. int blockLength = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmName.Aes, SymmetricAlgorithmMode.Cbc, SymmetricAlgorithmPadding.Zeros) .BlockLength; int bytesBeyondLastBlockLength = expectedPlainText.Length % blockLength; if (bytesBeyondLastBlockLength > 0) { int growBy = blockLength - bytesBeyondLastBlockLength; Array.Resize(ref expectedPlainText, expectedPlainText.Length + growBy); } } byte[] actualPlainText = WinRTCrypto.CryptographicEngine.Decrypt(key, actualCipherText, iv); Assert.Equal( Convert.ToBase64String(expectedPlainText), Convert.ToBase64String(actualPlainText)); } } } }
public void SymmetricEncryption(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Skip.If(mode.IsAuthenticated(), "This test is only for non-authenticated block modes."); bool badCombination = false; badCombination |= !mode.IsBlockCipher() && padding != SymmetricAlgorithmPadding.None; // Padding does not apply to streaming ciphers. badCombination |= name.IsBlockCipher() != mode.IsBlockCipher(); // Incompatible cipher and block mode. Func <ISymmetricKeyAlgorithmProvider> creator = () => WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(name, mode, padding); if (badCombination) { Assert.Throws <ArgumentException>(creator); this.logger.WriteLine("Expected exception thrown for invalid combination."); return; } var algorithm = creator(); int keyLength = algorithm.LegalKeySizes.First().MinSize; var keyMaterial = WinRTCrypto.CryptographicBuffer.GenerateRandom(keyLength / 8); using (var key = algorithm.CreateSymmetricKey(keyMaterial)) { var ciphertext = WinRTCrypto.CryptographicEngine.Encrypt(key, new byte[algorithm.BlockLength], null); Assert.NotEmpty(ciphertext); } }
public void CreateEncryptor_SymmetricEncryptionEquivalence(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Skip.If(!name.IsBlockCipher() && padding != SymmetricAlgorithmPadding.None, "By design - streaming ciphers need no padding."); var algorithmProvider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(name, mode, padding); int keyLength = GetKeyLength(name, algorithmProvider); byte[] keyMaterial = WinRTCrypto.CryptographicBuffer.GenerateRandom(keyLength); var key1 = algorithmProvider.CreateSymmetricKey(keyMaterial); var key2 = algorithmProvider.CreateSymmetricKey(keyMaterial); // create a second key so that streaming ciphers will be produce the same result when executed the second time var iv = mode.UsesIV() ? WinRTCrypto.CryptographicBuffer.GenerateRandom(algorithmProvider.BlockLength) : null; float incrementBy = padding == SymmetricAlgorithmPadding.None ? 1 : 0.5f; for (float dataLengthFactor = 1; dataLengthFactor <= 3; dataLengthFactor += incrementBy) { var data = WinRTCrypto.CryptographicBuffer.GenerateRandom((int)(dataLengthFactor * algorithmProvider.BlockLength)); var expected = WinRTCrypto.CryptographicEngine.Encrypt(key1, data, iv); var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(key2, iv); var actualStream = new MemoryStream(); using (var cryptoStream = CryptoStream.WriteTo(actualStream, encryptor)) { // Write it in smaller than block length chunks so we're exercising more product code. int chunkSize = Math.Max(1, (int)(data.Length / Math.Max(1, dataLengthFactor + 1))); for (int dataOffset = 0; dataOffset < data.Length; dataOffset += chunkSize) { cryptoStream.Write(data, dataOffset, Math.Min(chunkSize, data.Length - dataOffset)); } cryptoStream.FlushFinalBlock(); byte[] actual = actualStream.ToArray(); Assert.Equal( Convert.ToBase64String(expected), Convert.ToBase64String(actual)); } } }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricCryptographicKey"/> class. /// </summary> /// <param name="algorithm">The algorithm, initialized with the key.</param> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> internal SymmetricCryptographicKey(Platform.SymmetricAlgorithm algorithm, SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Requires.NotNull(algorithm, "algorithm"); this.algorithm = algorithm; this.Name = name; this.Mode = mode; this.Padding = padding; }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricCryptographicKey" /> class. /// </summary> /// <param name="provider">The provider that created this instance.</param> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> /// <param name="keyMaterial">The key.</param> internal SymmetricCryptographicKey(SymmetricKeyAlgorithmProvider provider, SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding, byte[] keyMaterial) { Requires.NotNull(provider, nameof(provider)); Requires.NotNull(keyMaterial, nameof(keyMaterial)); if (name == SymmetricAlgorithmName.Aes && mode == SymmetricAlgorithmMode.Ccm && padding == SymmetricAlgorithmPadding.None) { // On Android encryption misbehaves causing our unit tests to fail. throw new NotSupportedException(); } this.provider = provider; this.Name = name; this.Mode = mode; this.Padding = padding; this.key = new SecretKeySpec(keyMaterial, this.Name.GetString()); this.KeySize = keyMaterial.Length * 8; }
/// <summary> /// Initializes a new instance of the <see cref="CryptoTransformAdaptor"/> class. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> /// <param name="transform">The transform.</param> internal CryptoTransformAdaptor(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding, Cipher transform) { Requires.NotNull(transform, "transform"); this.name = name; this.mode = mode; this.padding = padding; this.transform = transform; }
/// <summary> /// Initializes a new instance of the <see cref="SymmetricKeyAlgorithmProvider"/> class. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> public SymmetricKeyAlgorithmProvider(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { this.Name = name; this.Mode = mode; this.Padding = padding; // Try opening the algorithm now to throw any exceptions that it may. using (this.OpenAlgorithm()) { } }
private static bool PaddingPreservesPlaintextLength(SymmetricAlgorithmPadding padding) { return padding != SymmetricAlgorithmPadding.Zeros; }
/// <summary> /// Finds a composite <see cref="SymmetricAlgorithm"/> for the specified unit parts, if one exists. /// </summary> /// <param name="name">The name of the base algorithm to use.</param> /// <param name="mode">The algorithm's mode (i.e. streaming or some block mode).</param> /// <param name="padding">The padding to use.</param> /// <param name="algorithm">Receives the composite algorithm enum value, if one exists.</param> /// <returns><c>true</c> if a match was found; otherwise <c>false</c>.</returns> public static bool TryAssemblyAlgorithm(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding, out SymmetricAlgorithm algorithm) { foreach (SymmetricAlgorithm assembled in Enum.GetValues(typeof(SymmetricAlgorithm))) { if (assembled.GetName() == name && assembled.GetMode() == mode && assembled.GetPadding() == padding) { algorithm = assembled; return true; } } algorithm = (SymmetricAlgorithm)0; return false; }
public void CreateEncryptor_SymmetricEncryptionEquivalence(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { Skip.If(!name.IsBlockCipher() && padding != SymmetricAlgorithmPadding.None, "By design - streaming ciphers need no padding."); var algorithmProvider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(name, mode, padding); int keyLength = GetKeyLength(name, algorithmProvider); byte[] keyMaterial = WinRTCrypto.CryptographicBuffer.GenerateRandom(keyLength); var key1 = algorithmProvider.CreateSymmetricKey(keyMaterial); var key2 = algorithmProvider.CreateSymmetricKey(keyMaterial); // create a second key so that streaming ciphers will be produce the same result when executed the second time var iv = mode.UsesIV() ? WinRTCrypto.CryptographicBuffer.GenerateRandom(algorithmProvider.BlockLength) : null; float incrementBy = padding == SymmetricAlgorithmPadding.None ? 1 : 0.5f; for (float dataLengthFactor = 1; dataLengthFactor <= 3; dataLengthFactor += incrementBy) { var data = WinRTCrypto.CryptographicBuffer.GenerateRandom((int)(dataLengthFactor * algorithmProvider.BlockLength)); var expected = WinRTCrypto.CryptographicEngine.Encrypt(key1, data, iv); var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(key2, iv); var actualStream = new MemoryStream(); using (var cryptoStream = CryptoStream.WriteTo(actualStream, encryptor)) { // Write it in smaller than block length chunks so we're exercising more product code. int chunkSize = Math.Max(1, (int)(data.Length / Math.Max(1, dataLengthFactor + 1))); for (int dataOffset = 0; dataOffset < data.Length; dataOffset += chunkSize) { cryptoStream.Write(data, dataOffset, Math.Min(chunkSize, data.Length - dataOffset)); } cryptoStream.FlushFinalBlock(); byte[] actual = actualStream.ToArray(); Assert.Equal( Convert.ToBase64String(expected), Convert.ToBase64String(actual)); } } }
/// <inheritdoc /> public ISymmetricKeyAlgorithmProvider OpenAlgorithm(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { return new SymmetricKeyAlgorithmProvider(name, mode, padding); }
private static bool PaddingPreservesPlaintextLength(SymmetricAlgorithmPadding padding) { return(padding != SymmetricAlgorithmPadding.Zeros); }