internal async Task <(DataEncryptionKeyProperties, InMemoryRawDek)> FetchUnwrappedAsync( string id, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { try { DataEncryptionKeyProperties dekProperties = await this.DekProvider.DekCache.GetOrAddDekPropertiesAsync( id, this.ReadResourceAsync, diagnosticsContext, cancellationToken); InMemoryRawDek inMemoryRawDek = await this.DekProvider.DekCache.GetOrAddRawDekAsync( dekProperties, this.UnwrapAsync, diagnosticsContext, cancellationToken); return(dekProperties, inMemoryRawDek); } catch (CosmosException exception) { throw EncryptionExceptionFactory.EncryptionKeyNotFoundException( $"Failed to retrieve Data Encryption Key with id: '{id}'.", exception); } }
/// <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)); }
/// <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); }