/// <inheritdoc/> public byte[] Encrypt( byte[] message, SecureString password, KeyDerivationCostType costType, string encryptorName, string kdfName = Pbkdf2.CryptoKdfName, string compression = null) { if (message == null) { throw new ArgumentNullException(nameof(message)); } ValidatePassword(password); if (_randomSource == null) { throw new ArgumentNullException(nameof(_randomSource)); } if (string.IsNullOrWhiteSpace(encryptorName)) { encryptorName = BouncyCastleXChaCha20.CryptoAlgorithmName; } if (string.IsNullOrWhiteSpace(kdfName)) { encryptorName = Pbkdf2.CryptoKdfName; } ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName); IKeyDerivationFunction kdf = new KeyDerivationFactory().CreateKdf(kdfName); // Prepare header CryptoHeader header = new CryptoHeader(); header.PackageName = PackageName; header.AlgorithmName = encryptor.Name; header.Nonce = _randomSource.GetRandomBytes(encryptor.ExpectedNonceSize); header.KdfName = kdf.Name; header.Salt = _randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes); int cost = kdf.RecommendedCost(costType); header.Cost = cost.ToString(); header.Compression = compression; try { if (string.Equals(CompressionGzip, header.Compression, StringComparison.InvariantCultureIgnoreCase)) { message = CompressUtils.Compress(message); } byte[] key = kdf.DeriveKeyFromPassword(password, encryptor.ExpectedKeySize, header.Salt, cost); byte[] cipher = encryptor.Encrypt(message, key, header.Nonce); return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher)); } catch (Exception ex) { throw new CryptoException("An unexpected error occured, while encrypting the message.", ex); } }
/// <summary> /// Encrypts a message with a user password, and adds a header containing all information /// necessary for the decryption (algorithm, nonce, salt, ...). /// </summary> /// <param name="message">Plain text message to encrypt.</param> /// <param name="password">Password to use for encryption, minimum length is 7 characters.</param> /// <param name="costType">The cost type to use for encryption.</param> /// <param name="randomSource">A cryptographically safe random source.</param> /// <param name="encryptorName">The name of an encryption algorithm which shall be used to /// do the encryption.</param> /// <param name="kdfName">The name of a key derivation function, which can convert the /// password to a key.</param> /// <returns>A binary array containing the cipher.</returns> public byte[] Encrypt( byte[] message, string password, KeyDerivationCostType costType, ICryptoRandomSource randomSource, string encryptorName, string kdfName = Pbkdf2.CryptoKdfName) { if (message == null) { throw new ArgumentNullException("message"); } ValidatePassword(password); if (randomSource == null) { throw new ArgumentNullException("randomSource"); } if (string.IsNullOrWhiteSpace(encryptorName)) { encryptorName = BouncyCastleAesGcm.CryptoAlgorithmName; } if (string.IsNullOrWhiteSpace(kdfName)) { encryptorName = Pbkdf2.CryptoKdfName; } ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName); IKeyDerivationFunction kdf = new KeyDerivationFactory().CreateKdf(kdfName); // Prepare header CryptoHeader header = new CryptoHeader(); header.AppName = _appName; header.AlgorithmName = encryptor.Name; header.Nonce = randomSource.GetRandomBytes(encryptor.ExpectedNonceSize); header.KdfName = kdf.Name; header.Salt = randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes); int cost = kdf.RecommendedCost(costType); header.Cost = cost.ToString(); try { byte[] key = kdf.DeriveKeyFromPassword(password, encryptor.ExpectedKeySize, header.Salt, cost); byte[] cipher = encryptor.Encrypt(message, key, header.Nonce); return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher)); } catch (Exception ex) { throw new CryptoException("An unexpected error occured, while encrypting the message.", ex); } }
/// <inheritdoc /> public int RecommendedCost(KeyDerivationCostType costType) { switch (costType) { case KeyDerivationCostType.Low: return(1000); case KeyDerivationCostType.High: return(10000); // measured 225ms (targeting ⅕s) on a mid-range mobile device in 2021 default: throw new ArgumentOutOfRangeException("costType"); } }
/// <inheritdoc /> public int RecommendedCost(KeyDerivationCostType costType) { switch (costType) { case KeyDerivationCostType.Low: return(1000); case KeyDerivationCostType.High: return(10000); default: throw new ArgumentOutOfRangeException("costType"); } }