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 StreamingCipherKeyRetainsStateAcrossOperations_Encrypt() { // NetFX doesn't support RC4. If another streaming cipher is ever added to the suite, // this test should be modified to use that cipher to test the NetFx PCL wrapper for // streaming cipher behavior. SymmetricAlgorithmName symmetricAlgorithm = SymmetricAlgorithmName.Rc4; SymmetricAlgorithmMode mode = SymmetricAlgorithmMode.Streaming; SymmetricAlgorithmPadding padding = SymmetricAlgorithmPadding.None; ISymmetricKeyAlgorithmProvider?algorithmProvider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(symmetricAlgorithm, mode, padding); int keyLength = GetKeyLength(symmetricAlgorithm, algorithmProvider); byte[] keyMaterial = WinRTCrypto.CryptographicBuffer.GenerateRandom(keyLength); ICryptographicKey?key1 = algorithmProvider.CreateSymmetricKey(keyMaterial); ICryptographicKey?key2 = algorithmProvider.CreateSymmetricKey(keyMaterial); byte[] allData = new byte[] { 1, 2, 3 }; byte[] allCiphertext = WinRTCrypto.CryptographicEngine.Encrypt(key1, allData); var cipherStream = new MemoryStream(); for (int i = 0; i < allData.Length; i++) { byte[] cipherText = WinRTCrypto.CryptographicEngine.Encrypt(key2, new byte[] { allData[i] }); cipherStream.Write(cipherText, 0, cipherText.Length); } byte[] incrementalResult = cipherStream.ToArray(); Assert.Equal( Convert.ToBase64String(allCiphertext), Convert.ToBase64String(incrementalResult)); }
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="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="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; // Try opening the algorithm now to throw any exceptions that it may. using (this.OpenAlgorithm()) { } }
/// <summary> /// Gets the block size (in bytes) for the specified algorithm. /// </summary> /// <param name="mode">The algorithm mode.</param> /// <param name="algorithm">The platform-specific algorithm.</param> /// <returns>The block size (in bytes).</returns> internal static int GetBlockSize(SymmetricAlgorithmMode mode, Cipher algorithm) { Requires.NotNull(algorithm, nameof(algorithm)); if (algorithm.BlockSize == 0 && mode == SymmetricAlgorithmMode.Streaming) { // This is a streaming cipher without a block size. Return 1 to emulate behavior of other platforms. return(1); } return(algorithm.BlockSize); }
/// <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 platform enum value for the block mode used by the specified algorithm. /// </summary> /// <param name="mode">The algorithm mode.</param> /// <returns>The platform-specific enum value describing the block mode.</returns> private static Platform.CipherMode GetMode(SymmetricAlgorithmMode mode) { switch (mode) { case SymmetricAlgorithmMode.Cbc: return(Platform.CipherMode.CBC); case SymmetricAlgorithmMode.Ecb: return(Platform.CipherMode.ECB); default: throw new NotSupportedException(); } }
public void LegalKeySizes(SymmetricAlgorithmName name, int minSize, int maxSize, int stepSize) { SymmetricAlgorithmMode blockMode = name.IsBlockCipher() ? SymmetricAlgorithmMode.Cbc : SymmetricAlgorithmMode.Streaming; SymmetricAlgorithmPadding padding = name.IsBlockCipher() ? SymmetricAlgorithmPadding.PKCS7 : SymmetricAlgorithmPadding.None; ISymmetricKeyAlgorithmProvider provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(name, blockMode, padding); IReadOnlyList <KeySizes>? result = provider.LegalKeySizes; Assert.NotNull(result); Assert.NotEmpty(result); var random = new Random(); Action <int> attemptKeySize = size => { var keyMaterial = new byte[size / 8]; random.NextBytes(keyMaterial); // some algorithms check against weak keys (e.g. all zeros) provider.CreateSymmetricKey(keyMaterial).Dispose(); }; // Verify that each allegedly legal key size actually works. foreach (KeySizes item in result) { this.logger.WriteLine($"{item.MinSize}-{item.MaxSize} ({item.StepSize})"); foreach (var keySize in item) { attemptKeySize(keySize); } // Also check the cases just off the edges of the range to see that they actually fail. // This ensures the returned values aren't too conservative. #if false // WinRT actually doesn't throw when given keys of inappropriate size. Go figure. if (item.StepSize > 0) { if (item.MinSize - item.StepSize > 0) { Assert.Throws <ArgumentException>(() => attemptKeySize(item.MinSize - item.StepSize)); } if (item.MaxSize + item.StepSize > 0) { Assert.Throws <ArgumentException>(() => attemptKeySize(item.MaxSize + item.StepSize)); } } #endif } KeySizes range = result.Single(); Assert.Equal(minSize, range.MinSize); Assert.Equal(maxSize, range.MaxSize); Assert.Equal(stepSize, range.StepSize); }
/// <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 a value indicating whether the specified block mode requires an initialization vector. /// </summary> /// <param name="mode">The block mode to check.</param> /// <returns><c>true</c> if the block mode uses an initialization vector; <c>false</c> otherwise.</returns> public static bool UsesIV(this SymmetricAlgorithmMode mode) { switch (mode) { case SymmetricAlgorithmMode.Cbc: case SymmetricAlgorithmMode.Ccm: case SymmetricAlgorithmMode.Gcm: return(true); case SymmetricAlgorithmMode.Ecb: return(false); default: throw new ArgumentException(); } }
/// <summary> /// Gets the BCrypt chaining mode to pass to set as the <see cref="PropertyNames.BCRYPT_CHAINING_MODE"/> property. /// </summary> /// <param name="mode">The block chaining mode.</param> /// <returns>The string name for the block chaining mode.</returns> private static string GetChainingMode(SymmetricAlgorithmMode mode) { switch (mode) { case SymmetricAlgorithmMode.Streaming: return(ChainingModes.NotApplicable); case SymmetricAlgorithmMode.Cbc: return(ChainingModes.Cbc); case SymmetricAlgorithmMode.Ecb: return(ChainingModes.Ecb); case SymmetricAlgorithmMode.Ccm: return(ChainingModes.Ccm); case SymmetricAlgorithmMode.Gcm: return(ChainingModes.Gcm); 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; }
/// <summary> /// Gets a value indicating whether the specified block mode requires an initialization vector. /// </summary> /// <param name="mode">The block mode to check.</param> /// <returns><c>true</c> if the block mode uses an initialization vector; <c>false</c> otherwise.</returns> public static bool UsesIV(this SymmetricAlgorithmMode mode) { switch (mode) { case SymmetricAlgorithmMode.Cbc: case SymmetricAlgorithmMode.Ccm: case SymmetricAlgorithmMode.Gcm: return(true); case SymmetricAlgorithmMode.Ecb: return(false); case SymmetricAlgorithmMode.Streaming: return(false); default: throw new ArgumentOutOfRangeException(nameof(mode)); } }
/// <inheritdoc /> public ISymmetricKeyAlgorithmProvider OpenAlgorithm(SymmetricAlgorithmName name, SymmetricAlgorithmMode mode, SymmetricAlgorithmPadding padding) { return new SymmetricKeyAlgorithmProvider(name, mode, padding); }
/// <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 a value indicating whether the specified mode offers authentication. /// </summary> /// <param name="mode">The mode to check.</param> /// <returns><c>true</c> if the cipher is an authenticating block mode cipher; <c>false</c> otherwise.</returns> public static bool IsAuthenticated(this SymmetricAlgorithmMode mode) => mode == SymmetricAlgorithmMode.Gcm || mode == SymmetricAlgorithmMode.Ccm;
/// <summary> /// Gets a value indicating whether the specified mode is implemented by a block cipher. /// </summary> /// <param name="mode">The mode to check.</param> /// <returns><c>true</c> if the cipher is a block cipher; <c>false</c> otherwise.</returns> public static bool IsBlockCipher(this SymmetricAlgorithmMode mode) => mode != SymmetricAlgorithmMode.Streaming;
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)); }
/// <summary> /// Gets the block size (in bytes) for the specified algorithm. /// </summary> /// <param name="mode">The algorithm mode.</param> /// <param name="algorithm">The platform-specific algorithm.</param> /// <returns>The block size (in bytes).</returns> internal static int GetBlockSize(SymmetricAlgorithmMode mode, Cipher algorithm) { Requires.NotNull(algorithm, "algorithm"); if (algorithm.BlockSize == 0 && mode == SymmetricAlgorithmMode.Streaming) { // This is a streaming cipher without a block size. Return 1 to emulate behavior of other platforms. return 1; } return algorithm.BlockSize; }
/// <summary> /// Gets the platform enum value for the block mode used by the specified algorithm. /// </summary> /// <param name="mode">The algorithm mode.</param> /// <returns>The platform-specific enum value describing the block mode.</returns> private static Platform.CipherMode GetMode(SymmetricAlgorithmMode mode) { switch (mode) { case SymmetricAlgorithmMode.Cbc: return Platform.CipherMode.CBC; case SymmetricAlgorithmMode.Ecb: return Platform.CipherMode.ECB; default: throw new NotSupportedException(); } }
/// <summary> /// Gets the BCrypt chaining mode to pass to set as the <see cref="PropertyNames.BCRYPT_CHAINING_MODE"/> property. /// </summary> /// <param name="mode">The block chaining mode.</param> /// <returns>The block chaining mode.</returns> private static string GetChainingMode(SymmetricAlgorithmMode mode) { switch (mode) { case SymmetricAlgorithmMode.Streaming: return ChainingModes.NotApplicable; case SymmetricAlgorithmMode.Cbc: return ChainingModes.Cbc; case SymmetricAlgorithmMode.Ecb: return ChainingModes.Ecb; case SymmetricAlgorithmMode.Ccm: return ChainingModes.Ccm; case SymmetricAlgorithmMode.Gcm: return ChainingModes.Gcm; default: throw new NotSupportedException(); } }