/// <summary> /// Derives all the required keys from the given root key /// </summary> /// <param name="rootKey">Root key used to derive all the required derived keys</param> internal SqlAeadAes256CbcHmac256EncryptionKey(byte[] rootKey, string algorithmName) : base(rootKey) { _algorithmName = algorithmName; int keySizeInBytes = KeySize / 8; // Key validation if (rootKey.Length != keySizeInBytes) { throw SQL.InvalidKeySize(_algorithmName, rootKey.Length, keySizeInBytes); } // Derive keys from the root key // // Derive encryption key string encryptionKeySalt = string.Format(_encryptionKeySaltFormat, _algorithmName, KeySize); byte[] buff1 = new byte[keySizeInBytes]; SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(encryptionKeySalt), RootKey, buff1); _encryptionKey = new SqlClientSymmetricKey(buff1); // Derive mac key string macKeySalt = string.Format(_macKeySaltFormat, _algorithmName, KeySize); byte[] buff2 = new byte[keySizeInBytes]; SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(macKeySalt), RootKey, buff2); _macKey = new SqlClientSymmetricKey(buff2); // Derive iv key string ivKeySalt = string.Format(_ivKeySaltFormat, _algorithmName, KeySize); byte[] buff3 = new byte[keySizeInBytes]; SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(ivKeySalt), RootKey, buff3); _ivKey = new SqlClientSymmetricKey(buff3); }
/// <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 (_isDeterministic) { SqlSecurityUtility.GetHMACWithSHA256(plainText, _columnEncryptionKey.IVKey, iv); } else { SqlSecurityUtility.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] = _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 (!_cryptoProviderPool.TryDequeue(out aesAlg)) { aesAlg = new AesCryptoServiceProvider(); try { // Set various algorithm properties aesAlg.Key = _columnEncryptionKey.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(_columnEncryptionKey.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. _cryptoProviderPool.Enqueue(aesAlg); } return(outBuffer); }