/// <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 (encryptionAlgorithm != CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)
            {
                throw new ArgumentException($"Encryption algorithm not supported: {encryptionAlgorithm}", nameof(encryptionAlgorithm));
            }

            byte[] rawKey = new byte[32];
            SecurityUtility.GenerateRandomBytes(rawKey);
            return(rawKey);
        }
        /// <summary>
        /// Decryption steps
        /// 1. Validate version byte
        /// 2. (optional) Validate Authentication tag
        /// 3. Decrypt the message
        /// </summary>
        protected byte[] DecryptData(byte[] cipherText, bool hasAuthenticationTag)
        {
            Debug.Assert(cipherText != null);

            byte[] iv = new byte[BlockSizeInBytes];

            int minimumCipherTextLength = hasAuthenticationTag ? MinimumCipherTextLengthInBytesWithAuthenticationTag : MinimumCipherTextLengthInBytesNoAuthenticationTag;

            if (cipherText.Length < minimumCipherTextLength)
            {
                throw EncryptionExceptionFactory.InvalidCipherTextSize(cipherText.Length, minimumCipherTextLength);
            }

            // Validate the version byte
            int startIndex = 0;

            if (cipherText[startIndex] != this.algorithmVersion)
            {
                // Cipher text was computed with a different algorithm version than this.
                throw EncryptionExceptionFactory.InvalidAlgorithmVersion(cipherText[startIndex], this.algorithmVersion);
            }

            startIndex += 1;
            int authenticationTagOffset = 0;

            // Read authentication tag
            if (hasAuthenticationTag)
            {
                authenticationTagOffset = startIndex;
                startIndex += KeySizeInBytes; // authentication tag size is KeySizeInBytes
            }

            // Read cell IV
            Buffer.BlockCopy(cipherText, startIndex, iv, 0, iv.Length);
            startIndex += iv.Length;

            // Read encrypted text
            int cipherTextOffset = startIndex;
            int cipherTextCount  = cipherText.Length - startIndex;

            if (hasAuthenticationTag)
            {
                // Compute authentication tag
                byte[] authenticationTag = this.PrepareAuthenticationTag(iv, cipherText, cipherTextOffset, cipherTextCount);
                if (!SecurityUtility.CompareBytes(authenticationTag, cipherText, authenticationTagOffset, authenticationTag.Length))
                {
                    // Potentially tampered data, throw an exception
                    throw EncryptionExceptionFactory.InvalidAuthenticationTag();
                }
            }

            // Decrypt the text and return
            return(this.DecryptData(iv, cipherText, cipherTextOffset, cipherTextCount));
        }
Example #3
0
        /// <summary>
        /// Derives all the required keys from the given root key
        /// </summary>
        internal AeadAes256CbcHmac256EncryptionKey(byte[] rootKey, string algorithmName)
            : base(rootKey)
        {
            this.algorithmName = algorithmName;

            int keySizeInBytes = KeySize / 8;

            // Key validation
            if (rootKey.Length != keySizeInBytes)
            {
                throw EncryptionExceptionFactory.InvalidKeySize(
                          this.algorithmName,
                          rootKey.Length,
                          keySizeInBytes);
            }

            // Derive keys from the root key
            //
            // Derive encryption key
            string encryptionKeySalt = string.Format(
                EncryptionKeySaltFormat,
                this.algorithmName,
                KeySize);

            byte[] buff1 = new byte[keySizeInBytes];
            SecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(encryptionKeySalt), this.RootKey, buff1);
            this.encryptionKey = new SymmetricKey(buff1);

            // Derive mac key
            string macKeySalt = string.Format(MacKeySaltFormat, this.algorithmName, KeySize);

            byte[] buff2 = new byte[keySizeInBytes];
            SecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(macKeySalt), this.RootKey, buff2);
            this.macKey = new SymmetricKey(buff2);

            // Derive iv key
            string ivKeySalt = string.Format(IvKeySaltFormat, this.algorithmName, KeySize);

            byte[] buff3 = new byte[keySizeInBytes];
            SecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(ivKeySalt), this.RootKey, buff3);
            this.ivKey = new SymmetricKey(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 (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);
        }
Example #5
0
 /// <summary>
 /// Computes SHA256 value of the plain text key bytes
 /// </summary>
 /// <returns>A string containing SHA256 hash of the root key</returns>
 internal virtual string GetKeyHash()
 {
     return(SecurityUtility.GetSHA256Hash(this.RootKey));
 }