public void ShouldReturnResultFromKeyManagementServiceOnGenerateDataKey() { var keyId = Guid.NewGuid().ToString(); var expected = new GenerateDataKeyResult() { KeyCiphertext = new byte[] { 1, 2, 3 }, KeyPlaintext = new byte[] { 4, 5, 6 } }; _dataKeyCache.Setup(x => x.Get(It.Is <DataKeyIdentifier>(identifier => identifier.KeyId == keyId && identifier.EncryptionContext == _encryptionContext))) .Returns <GenerateDataKeyResult>(null); var keySpec = Guid.NewGuid().ToString(); _keyManagementService.Setup(x => x.GenerateDataKey(keyId, _encryptionContext, keySpec)) .Returns(expected); var result = _keyManagementServiceCache.GenerateDataKey(keyId, _encryptionContext, keySpec); _dataKeyCache.Verify(x => x.Set(It.Is <DataKeyIdentifier>(identifier => identifier.KeyId == keyId && identifier.EncryptionContext == _encryptionContext), expected), Times.Once); result.KeyPlaintext.ShouldBe(expected.KeyPlaintext); result.KeyCiphertext.ShouldBe(expected.KeyCiphertext); }
/// <summary> /// Attempt to generate a KMS datakey using the first successful response using a sorted dictionary of available KMS clients. /// </summary> /// <param name="sortedRegionToArnAndClientDictionary"> A sorted dictionary mapping regions and their arns and kms clients</param> /// <param name="dateKeyKeyId">The KMS arn used to generate the data key</param> /// <returns>A GenerateDataKeyResult object that contains the plain text key and the ciphertext for that key</returns> /// <exception cref="KeyManagementException">Throw an exception if we're unable to generate a datakey in any AWS region</exception> internal virtual GenerateDataKeyResult GenerateDataKey(OrderedDictionary sortedRegionToArnAndClientDictionary, out string dateKeyKeyId) { foreach (DictionaryEntry regionToArnAndClient in sortedRegionToArnAndClientDictionary) { try { TimerOptions generateDataKeyTimerOptions = new TimerOptions { Name = MetricsUtil.AelMetricsPrefix + ".kms.aws.generatedatakey." + regionToArnAndClient.Key }; using (MetricsUtil.MetricsInstance.Measure.Timer.Time(generateDataKeyTimerOptions)) { IAmazonKeyManagementService client = ((AwsKmsArnClient)regionToArnAndClient.Value).AwsKmsClient; string keyIdForDataKeyGeneration = ((AwsKmsArnClient)regionToArnAndClient.Value).Arn; GenerateDataKeyResult generateDataKeyResult = client.GenerateDataKey( keyIdForDataKeyGeneration, null, DataKeySpec.AES_256); dateKeyKeyId = keyIdForDataKeyGeneration; return(generateDataKeyResult); } } catch (AmazonServiceException e) { Logger.LogWarning(e, "Failed to generate data key via region {region}, trying next region", regionToArnAndClient.Key); // TODO Consider adding notification/CW alert } } throw new KeyManagementException("could not successfully generate data key using any regions"); }
public void ShouldReturnResultFromCacheOnGenerateDataKey() { var keyId = Guid.NewGuid().ToString(); var cacheKey = Guid.NewGuid().ToString(); _cacheKeyGenerator.Setup(x => x.Generate(keyId, _encryptionContext)) .Returns(cacheKey); var cacheValue = new byte[] { 10, 11, 12 }; _cache.Setup(x => x.Get(cacheKey)) .Returns(cacheValue); var dataKeyResult = new GenerateDataKeyResult() { KeyCiphertext = new byte[] { 1, 2, 3 }, KeyPlaintext = new byte[] { 4, 5, 6 } }; _cacheValueConverter.Setup(x => x.Convert(cacheValue)) .Returns(dataKeyResult); var result = _keyManagementServiceCache.GenerateDataKey(keyId, _encryptionContext, Guid.NewGuid().ToString()); _keyManagementService.Verify( x => x.GenerateDataKey( It.IsAny <string>(), It.IsAny <Dictionary <string, string> >(), It.IsAny <string>()), Times.Never); result.KeyPlaintext.ShouldBe(dataKeyResult.KeyPlaintext); result.KeyCiphertext.ShouldBe(dataKeyResult.KeyCiphertext); }
public void ShouldReturnResultFromKeyManagementServiceOnGenerateDataKey() { var keyId = Guid.NewGuid().ToString(); var cacheKey = Guid.NewGuid().ToString(); _cacheKeyGenerator.Setup(x => x.Generate(keyId, _encryptionContext)) .Returns(cacheKey); var cacheValue = new byte[] { 1, 2, 3 }; var expected = new GenerateDataKeyResult() { KeyCiphertext = new byte[] { 1, 2, 3 }, KeyPlaintext = new byte[] { 4, 5, 6 } }; _cacheValueConverter.Setup(x => x.Convert(expected)) .Returns(cacheValue); _cache.Setup(x => x.Get(cacheKey)) .Returns <byte[]>(null); var keySpec = Guid.NewGuid().ToString(); _keyManagementService.Setup(x => x.GenerateDataKey(keyId, _encryptionContext, keySpec)) .Returns(expected); var result = _keyManagementServiceCache.GenerateDataKey(keyId, _encryptionContext, keySpec); _cache.Verify(x => x.Set(cacheKey, cacheValue, It.IsAny <DistributedCacheEntryOptions>()), Times.Once); result.KeyPlaintext.ShouldBe(expected.KeyPlaintext); result.KeyCiphertext.ShouldBe(expected.KeyCiphertext); }
private void TestEncryptKeyShouldThrowExceptionAndWipeBytes() { byte[] dataKeyPlainText = { 1, 2 }; byte[] encryptedKey = { 3, 4 }; GenerateDataKeyResult generateDataKeyResult = new GenerateDataKeyResult { KeyPlaintext = dataKeyPlainText, }; Mock <CryptoKey> generatedDataKeyCryptoKey = new Mock <CryptoKey>(); string someKey = "some_key"; awsKeyManagementServiceImplSpy .Setup(x => x.GenerateDataKey( awsKeyManagementServiceImplSpy.Object.RegionToArnAndClientDictionary, out someKey)) .Returns(generateDataKeyResult); cryptoMock.Setup(x => x.GenerateKeyFromBytes(generateDataKeyResult.KeyPlaintext)) .Returns(generatedDataKeyCryptoKey.Object); // Inject unexpected exception so the Task.WaitAll throws an exception awsKeyManagementServiceImplSpy .Setup(x => x.EncryptKeyAndBuildResult( It.IsAny <IAmazonKeyManagementService>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <byte[]>())) .Throws <SystemException>(); cryptoMock.Setup(x => x.EncryptKey(cryptoKeyMock.Object, generatedDataKeyCryptoKey.Object)) .Returns(encryptedKey); Assert.Throws <AppEncryptionException>(() => awsKeyManagementServiceImplSpy.Object.EncryptKey(cryptoKeyMock.Object)); Assert.Equal(new byte[] { 0, 0 }, dataKeyPlainText); }
public void ShouldSetValue() { var identifier = new DataKeyIdentifier(Guid.NewGuid().ToString(), new Dictionary <string, string>()); var cacheKey = Guid.NewGuid().ToString(); _cacheKeyGenerator.Setup(x => x.Generate(identifier)) .Returns(cacheKey); var cacheValue = new byte[] { 1, 2, 3 }; var item = new GenerateDataKeyResult(); _cacheValueConverter.Setup(x => x.Convert(item)) .Returns(cacheValue); var entryOptions = new DistributedCacheEntryOptions(); _distributedCacheEntryOptionsFactory.Setup(x => x.Create(CacheItemType.DataKey)) .Returns(entryOptions); var dataKeyCache = new DataKeyCache(_cacheKeyGenerator.Object, _distributedCache.Object, _cacheValueConverter.Object, _distributedCacheEntryOptionsFactory.Object); dataKeyCache.Set(identifier, item); _distributedCache.Verify(x => x.Set(cacheKey, cacheValue, entryOptions)); }
public void ShouldReturnResultFromCacheOnGenerateDataKey() { var keyId = Guid.NewGuid().ToString(); var dataKeyResult = new GenerateDataKeyResult() { KeyCiphertext = new byte[] { 1, 2, 3 }, KeyPlaintext = new byte[] { 4, 5, 6 } }; _dataKeyCache.Setup(x => x.Get(It.Is <DataKeyIdentifier>(identifier => identifier.KeyId == keyId && identifier.EncryptionContext == _encryptionContext))) .Returns(dataKeyResult); var result = _keyManagementServiceCache.GenerateDataKey(keyId, _encryptionContext, Guid.NewGuid().ToString()); _keyManagementService.Verify( x => x.GenerateDataKey( It.IsAny <string>(), It.IsAny <Dictionary <string, string> >(), It.IsAny <string>()), Times.Never); result.KeyPlaintext.ShouldBe(dataKeyResult.KeyPlaintext); result.KeyCiphertext.ShouldBe(dataKeyResult.KeyCiphertext); }
public void ShouldGetValue() { var identifier = new DataKeyIdentifier(Guid.NewGuid().ToString(), new Dictionary <string, string>()); var cacheKey = Guid.NewGuid().ToString(); _cacheKeyGenerator.Setup(x => x.Generate(identifier)) .Returns(cacheKey); var cacheValue = new byte[] { 1, 2, 3 }; _distributedCache.Setup(x => x.Get(cacheKey)) .Returns(cacheValue); var expected = new GenerateDataKeyResult(); _cacheValueConverter.Setup(x => x.Convert(cacheValue)) .Returns(expected); var dataKeyCache = new DataKeyCache(_cacheKeyGenerator.Object, _distributedCache.Object, _cacheValueConverter.Object, _distributedCacheEntryOptionsFactory.Object); var result = dataKeyCache.Get(identifier); result.ShouldBe(expected); }
public GenerateDataKeyResult Convert(byte[] cacheValue) { using (var ms = new MemoryStream(cacheValue)) { using (var sr = new BinaryReader(ms)) { var keyCiphertextLengthBytes = sr.ReadBytes(sizeof(int)); Array.Reverse(keyCiphertextLengthBytes); var keyCiphertextLength = BitConverter.ToInt32(keyCiphertextLengthBytes, 0); var obj = new GenerateDataKeyResult(); obj.KeyCiphertext = sr.ReadBytes(keyCiphertextLength); var keyPlaintextLengthBytes = sr.ReadBytes(sizeof(int)); Array.Reverse(keyPlaintextLengthBytes); var keyPlaintextLength = BitConverter.ToInt32(keyPlaintextLengthBytes, 0); obj.KeyPlaintext = sr.ReadBytes(keyPlaintextLength); return(new GenerateDataKeyResult { KeyCiphertext = obj.KeyCiphertext, KeyPlaintext = obj.KeyPlaintext }); } } }
public override byte[] EncryptKey(CryptoKey key) { using (MetricsUtil.MetricsInstance.Measure.Timer.Time(EncryptkeyTimerOptions)) { Json kmsKeyEnvelope = new Json(); // We generate a KMS datakey (plaintext and encrypted) and encrypt its plaintext key against remaining regions. // This allows us to be able to decrypt from any of the regions locally later. GenerateDataKeyResult dataKey = GenerateDataKey(RegionToArnAndClientDictionary, out string dateKeyKeyId); byte[] dataKeyPlainText = dataKey.KeyPlaintext; try { byte[] encryptedKey = crypto.EncryptKey(key, crypto.GenerateKeyFromBytes(dataKeyPlainText)); kmsKeyEnvelope.Put(EncryptedKey, encryptedKey); ConcurrentBag <JObject> kmsRegionKeyJsonBag = new ConcurrentBag <JObject>(); Parallel.ForEach(RegionToArnAndClientDictionary.Cast <object>(), regionToArnClientObject => { DictionaryEntry regionToArnAndClient = (DictionaryEntry)regionToArnClientObject; AwsKmsArnClient arnClient = (AwsKmsArnClient)regionToArnAndClient.Value; string region = (string)regionToArnAndClient.Key; if (!arnClient.Arn.Equals(dateKeyKeyId)) { // If the ARN is different than the datakey's, call encrypt since it's another region EncryptKeyAndBuildResult( arnClient.AwsKmsClient, region, arnClient.Arn, dataKeyPlainText).IfSome(encryptedKeyResult => kmsRegionKeyJsonBag.Add(encryptedKeyResult)); } else { // This is the datakey, so build kmsKey json for it kmsRegionKeyJsonBag.Add((JObject)Option <JObject> .Some(BuildKmsRegionKeyJson( region, dateKeyKeyId, dataKey.KeyCiphertext))); } }); // TODO Consider adding minimum or quorum check on number of entries kmsKeyEnvelope.Put(KmsKeksKey, kmsRegionKeyJsonBag.ToList()); } catch (Exception e) { Logger.LogError(e, "Unexpected execution exception while encrypting KMS data key"); throw new AppEncryptionException("unexpected execution error during encrypt", e); } finally { ManagedBufferUtils.WipeByteArray(dataKeyPlainText); } return(kmsKeyEnvelope.ToUtf8()); } }
public void Set(DataKeyIdentifier key, GenerateDataKeyResult item) { var cacheKey = _cacheKeyGenerator.Generate(key); var cacheValue = _cacheValueConverter.Convert(item); var options = _distributedCacheEntryOptionsFactory.Create(CacheItemType.DataKey); _distributedCache.Set(cacheKey, cacheValue, options); }
private void TestGenerateDataKeySuccessful() { OrderedDictionary sortedRegionToArnAndClient = awsKeyManagementServiceImplSpy.Object.RegionToArnAndClientDictionary; Mock <GenerateDataKeyResult> dataKeyResultMock = new Mock <GenerateDataKeyResult>(); amazonKeyManagementServiceClientMock .Setup(x => x.GenerateDataKey(ArnUsWest1, null, DataKeySpec.AES_256)) // preferred region's ARN, verify it's the first and hence returned .Returns(dataKeyResultMock.Object); GenerateDataKeyResult dataKeyResponseActual = awsKeyManagementServiceImplSpy.Object.GenerateDataKey(sortedRegionToArnAndClient, out _); Assert.Equal(dataKeyResultMock.Object, dataKeyResponseActual); }
public void ShouldConvertFromCacheValue() { var cacheValueConverter = new CacheValueConverter(); var cacheValue = new byte[] { 0, 0, 0, 2, 1, 2, 0, 0, 0, 3, 3, 4, 5 }; var result = cacheValueConverter.Convert(cacheValue); var expected = new GenerateDataKeyResult() { KeyCiphertext = new byte[] { 1, 2 }, KeyPlaintext = new byte[] { 3, 4, 5 } }; Assert.True(result.KeyCiphertext.SequenceEqual(expected.KeyCiphertext)); Assert.True(result.KeyPlaintext.SequenceEqual(expected.KeyPlaintext)); }
public byte[] Convert(GenerateDataKeyResult result) { using (var ms = new MemoryStream()) { using (var sw = new BinaryWriter(ms)) { var keyCiphertextLengthBytes = BitConverter.GetBytes(result.KeyCiphertext.Length); Array.Reverse(keyCiphertextLengthBytes); var keyPlaintextLengthBytes = BitConverter.GetBytes(result.KeyPlaintext.Length); Array.Reverse(keyPlaintextLengthBytes); sw.Write(keyCiphertextLengthBytes); sw.Write(result.KeyCiphertext); sw.Write(keyPlaintextLengthBytes); sw.Write(result.KeyPlaintext); sw.Flush(); } return(ms.ToArray()); } }
public void GenerateDataKeyCalled() { _result = Manager.GenerateDataKey(_keyId); }
private void TestEncryptKeySuccessful() { byte[] encryptedKey = { 3, 4 }; byte[] dataKeyPlainText = { 1, 2 }; byte[] dataKeyCipherText = { 5, 6 }; byte[] encryptKeyCipherText = { 7, 8 }; JObject encryptKeyAndBuildResultJson = JObject.FromObject(new Dictionary <string, object> { { RegionKey, UsEast1 }, { ArnKey, ArnUsEast1 }, { EncryptedKek, Convert.ToBase64String(encryptKeyCipherText) }, }); JObject kmsKeyEnvelope = JObject.FromObject(new Dictionary <string, object> { { EncryptedKey, Convert.ToBase64String(encryptedKey) }, { KmsKeksKey, new List <object> { new Dictionary <string, object> { { RegionKey, UsWest1 }, { ArnKey, ArnUsWest1 }, { EncryptedKek, Convert.ToBase64String(dataKeyCipherText) }, }, encryptKeyAndBuildResultJson, } }, }); GenerateDataKeyResult generateDataKeyResult = new GenerateDataKeyResult { KeyPlaintext = dataKeyPlainText, KeyCiphertext = dataKeyCipherText, }; Mock <CryptoKey> generatedDataKeyCryptoKey = new Mock <CryptoKey>(); string keyId = ArnUsWest1; awsKeyManagementServiceImplSpy .Setup(x => x.GenerateDataKey(awsKeyManagementServiceImplSpy.Object.RegionToArnAndClientDictionary, out keyId)) .Returns(generateDataKeyResult); cryptoMock.Setup(x => x.GenerateKeyFromBytes(generateDataKeyResult.KeyPlaintext)) .Returns(generatedDataKeyCryptoKey.Object); cryptoMock.Setup(x => x.EncryptKey(cryptoKeyMock.Object, generatedDataKeyCryptoKey.Object)) .Returns(encryptedKey); awsKeyManagementServiceImplSpy.Setup(x => x.EncryptKeyAndBuildResult( It.IsAny <IAmazonKeyManagementService>(), UsEast1, ArnUsEast1, dataKeyPlainText)) .Returns(Option <JObject> .Some(encryptKeyAndBuildResultJson)); byte[] encryptedResult = awsKeyManagementServiceImplSpy.Object.EncryptKey(cryptoKeyMock.Object); JObject kmsKeyEnvelopeResult = new Asherah.AppEncryption.Util.Json(encryptedResult).ToJObject(); Assert.Equal(new byte[] { 0, 0 }, dataKeyPlainText); // This is a workaround for https://github.com/JamesNK/Newtonsoft.Json/issues/1437 // If DeepEquals fails due to mismatching array order, compare the elements individually if (!JToken.DeepEquals(kmsKeyEnvelope, kmsKeyEnvelopeResult)) { JArray kmsKeyEnvelopeKmsKeks = JArray.FromObject(kmsKeyEnvelope[KmsKeksKey] .OrderBy(k => k[RegionKey])); JArray kmsKeyEnvelopeResultKmsKeks = JArray.FromObject(kmsKeyEnvelopeResult[KmsKeksKey] .OrderBy(k => k[RegionKey])); Assert.True(JToken.DeepEquals(kmsKeyEnvelope[EncryptedKey], kmsKeyEnvelopeResult[EncryptedKey])); Assert.True(JToken.DeepEquals(kmsKeyEnvelopeKmsKeks, kmsKeyEnvelopeResultKmsKeks)); } }
public void DataKeyIsGeneratedAndRoundTripped() { _result = Manager.GenerateDataKey(_keyId); _decryptedCipherText = Manager.DecryptData(_result.CipherTextKey); }
private void TestEncryptKeySuccessful() { byte[] encryptedKey = { 3, 4 }; byte[] dataKeyPlainText = { 1, 2 }; byte[] dataKeyCipherText = { 5, 6 }; byte[] encryptKeyCipherText = { 7, 8 }; JObject encryptKeyAndBuildResultJson = JObject.FromObject(new Dictionary <string, object> { { RegionKey, UsEast1 }, { ArnKey, ArnUsEast1 }, { EncryptedKek, Convert.ToBase64String(encryptKeyCipherText) } }); JObject kmsKeyEnvelope = JObject.FromObject(new Dictionary <string, object> { { EncryptedKey, Convert.ToBase64String(encryptedKey) }, { // For some reason we have to use ConcurrentBag here. Likely due to internal ConcurrentBag list // structure failing to compare against regular List? KmsKeksKey, new ConcurrentBag <object> { new Dictionary <string, object> { { RegionKey, UsWest1 }, { ArnKey, ArnUsWest1 }, { EncryptedKek, Convert.ToBase64String(dataKeyCipherText) } }, encryptKeyAndBuildResultJson } } }); GenerateDataKeyResult generateDataKeyResult = new GenerateDataKeyResult { KeyPlaintext = dataKeyPlainText, KeyCiphertext = dataKeyCipherText }; Mock <CryptoKey> generatedDataKeyCryptoKey = new Mock <CryptoKey>(); string keyId = ArnUsWest1; awsKeyManagementServiceImplSpy .Setup(x => x.GenerateDataKey(awsKeyManagementServiceImplSpy.Object.RegionToArnAndClientDictionary, out keyId)) .Returns(generateDataKeyResult); cryptoMock.Setup(x => x.GenerateKeyFromBytes(generateDataKeyResult.KeyPlaintext)) .Returns(generatedDataKeyCryptoKey.Object); cryptoMock.Setup(x => x.EncryptKey(cryptoKeyMock.Object, generatedDataKeyCryptoKey.Object)) .Returns(encryptedKey); awsKeyManagementServiceImplSpy.Setup(x => x.EncryptKeyAndBuildResult( It.IsAny <IAmazonKeyManagementService>(), UsEast1, ArnUsEast1, dataKeyPlainText)) .Returns(Option <JObject> .Some(encryptKeyAndBuildResultJson)); byte[] encryptedResult = awsKeyManagementServiceImplSpy.Object.EncryptKey(cryptoKeyMock.Object); Asherah.AppEncryption.Util.Json kmsKeyEnvelopeResult = new Asherah.AppEncryption.Util.Json(encryptedResult); Assert.True(JToken.DeepEquals(kmsKeyEnvelope, kmsKeyEnvelopeResult.ToJObject())); Assert.Equal(new byte[] { 0, 0 }, dataKeyPlainText); }