/// <summary> /// Generates raw data encryption key bytes suitable for use with the provided encryption algorithm. /// </summary> /// <param name="encryptionAlgorithm">Encryption algorithm the returned key is intended to be used with.</param> /// <returns>New instance of data encryption key.</returns> public static byte[] Generate(string encryptionAlgorithm) { if (!string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { throw new ArgumentException($"Encryption algorithm not supported: {encryptionAlgorithm}. Supported Algorithm is '{CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized}'", nameof(encryptionAlgorithm)); } byte[] rawKey = new byte[32]; SecurityUtility.GenerateRandomBytes(rawKey); return(rawKey); }
/// <summary> /// Encryption Algorithm /// cell_iv = HMAC_SHA-2-256(iv_key, cell_data) truncated to 128 bits /// cell_ciphertext = AES-CBC-256(enc_key, cell_iv, cell_data) with PKCS7 padding. /// (optional) cell_tag = HMAC_SHA-2-256(mac_key, versionbyte + cell_iv + cell_ciphertext + versionbyte_length) /// cell_blob = versionbyte + [cell_tag] + cell_iv + cell_ciphertext /// </summary> /// <param name="plainText">Plaintext data to be encrypted</param> /// <param name="hasAuthenticationTag">Does the algorithm require authentication tag.</param> /// <returns>Returns the ciphertext corresponding to the plaintext.</returns> protected byte[] EncryptData(byte[] plainText, bool hasAuthenticationTag) { // Empty values get encrypted and decrypted properly for both Deterministic and Randomized encryptions. Debug.Assert(plainText != null); byte[] iv = new byte[BlockSizeInBytes]; // Prepare IV // Should be 1 single block (16 bytes) if (this.isDeterministic) { SecurityUtility.GetHMACWithSHA256(plainText, this.dataEncryptionKey.IVKey, iv); } else { SecurityUtility.GenerateRandomBytes(iv); } int numBlocks = (plainText.Length / BlockSizeInBytes) + 1; // Final blob we return = version + HMAC + iv + cipherText const int hmacStartIndex = 1; int authenticationTagLen = hasAuthenticationTag ? KeySizeInBytes : 0; int ivStartIndex = hmacStartIndex + authenticationTagLen; int cipherStartIndex = ivStartIndex + BlockSizeInBytes; // this is where hmac starts. // Output buffer size = size of VersionByte + Authentication Tag + IV + cipher Text blocks. int outputBufSize = sizeof(byte) + authenticationTagLen + iv.Length + (numBlocks * BlockSizeInBytes); byte[] outBuffer = new byte[outputBufSize]; // Store the version and IV rightaway outBuffer[0] = this.algorithmVersion; Buffer.BlockCopy(iv, 0, outBuffer, ivStartIndex, iv.Length); AesCryptoServiceProvider aesAlg; // Try to get a provider from the pool. // If no provider is available, create a new one. if (!this.cryptoProviderPool.TryDequeue(out aesAlg)) { aesAlg = new AesCryptoServiceProvider(); try { // Set various algorithm properties aesAlg.Key = this.dataEncryptionKey.EncryptionKey; aesAlg.Mode = cipherMode; aesAlg.Padding = paddingMode; } catch (Exception) { if (aesAlg != null) { aesAlg.Dispose(); } throw; } } try { // Always set the IV since it changes from cell to cell. aesAlg.IV = iv; // Compute CipherText and authentication tag in a single pass using (ICryptoTransform encryptor = aesAlg.CreateEncryptor()) { Debug.Assert(encryptor.CanTransformMultipleBlocks, "AES Encryptor can transform multiple blocks"); int count = 0; int cipherIndex = cipherStartIndex; // this is where cipherText starts if (numBlocks > 1) { count = (numBlocks - 1) * BlockSizeInBytes; cipherIndex += encryptor.TransformBlock(plainText, 0, count, outBuffer, cipherIndex); } byte[] buffTmp = encryptor.TransformFinalBlock(plainText, count, plainText.Length - count); // done encrypting Buffer.BlockCopy(buffTmp, 0, outBuffer, cipherIndex, buffTmp.Length); cipherIndex += buffTmp.Length; } if (hasAuthenticationTag) { using (HMACSHA256 hmac = new HMACSHA256(this.dataEncryptionKey.MACKey)) { Debug.Assert(hmac.CanTransformMultipleBlocks, "HMAC can't transform multiple blocks"); hmac.TransformBlock(version, 0, version.Length, version, 0); hmac.TransformBlock(iv, 0, iv.Length, iv, 0); // Compute HMAC on final block hmac.TransformBlock(outBuffer, cipherStartIndex, numBlocks * BlockSizeInBytes, outBuffer, cipherStartIndex); hmac.TransformFinalBlock(versionSize, 0, versionSize.Length); byte[] hash = hmac.Hash; Debug.Assert(hash.Length >= authenticationTagLen, "Unexpected hash size"); Buffer.BlockCopy(hash, 0, outBuffer, hmacStartIndex, authenticationTagLen); } } } finally { // Return the provider to the pool. this.cryptoProviderPool.Enqueue(aesAlg); } return(outBuffer); }