Skip to content

Commit 4dc397f

Browse files
authored
Merge pull request #1522 from chief-micco/new-interfaces
Adding new async interfaces for future use
2 parents 701e8f6 + 07f7407 commit 4dc397f

17 files changed

+434
-42
lines changed

csharp/AppEncryption/AppEncryption.Tests/AppEncryption/Envelope/EnvelopeEncryptionBytesImplTest.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Collections.Immutable;
4+
using System.Threading.Tasks;
45
using GoDaddy.Asherah.AppEncryption.Envelope;
56
using Microsoft.Extensions.Logging;
67
using Moq;
@@ -36,6 +37,20 @@ public void TestDecryptDataRowRecord()
3637
Assert.Equal(expectedBytes, actualBytes);
3738
}
3839

40+
[Fact]
41+
public async Task TestDecryptDataRowRecordAsync()
42+
{
43+
byte[] expectedBytes = { 0, 1 };
44+
45+
envelopeEncryptionJsonImplMock.Setup(x => x.DecryptDataRowRecord(It.IsAny<JObject>()))
46+
.Returns(expectedBytes);
47+
48+
ImmutableDictionary<string, string> immutableDictionary = new Dictionary<string, string> { { "key", "value" } }.ToImmutableDictionary();
49+
byte[] dataRowRecordBytes = new Asherah.AppEncryption.Util.Json(JObject.FromObject(immutableDictionary)).ToUtf8();
50+
byte[] actualBytes = await envelopeEncryptionBytesImpl.DecryptDataRowRecordAsync(dataRowRecordBytes);
51+
Assert.Equal(expectedBytes, actualBytes);
52+
}
53+
3954
[Fact]
4055
public void TestEncryptPayload()
4156
{
@@ -49,6 +64,19 @@ public void TestEncryptPayload()
4964
Assert.Equal(expectedBytes, actualResult);
5065
}
5166

67+
[Fact]
68+
public async Task TestEncryptPayloadAsync()
69+
{
70+
ImmutableDictionary<string, string> immutableDictionary = new Dictionary<string, string> { { "key", "value" } }.ToImmutableDictionary();
71+
JObject dataRowRecord = JObject.FromObject(immutableDictionary);
72+
byte[] expectedBytes = { 123, 34, 107, 101, 121, 34, 58, 34, 118, 97, 108, 117, 101, 34, 125 };
73+
74+
envelopeEncryptionJsonImplMock.Setup(x => x.EncryptPayload(It.IsAny<byte[]>())).Returns(dataRowRecord);
75+
76+
byte[] actualResult = await envelopeEncryptionBytesImpl.EncryptPayloadAsync(new byte[] { 0, 1 });
77+
Assert.Equal(expectedBytes, actualResult);
78+
}
79+
5280
[Fact]
5381
public void TestDisposeSuccess()
5482
{

csharp/AppEncryption/AppEncryption.Tests/AppEncryption/Envelope/EnvelopeEncryptionJsonImplTest.cs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Text;
4+
using System.Threading.Tasks;
45
using GoDaddy.Asherah.AppEncryption.Envelope;
56
using GoDaddy.Asherah.AppEncryption.Exceptions;
67
using GoDaddy.Asherah.AppEncryption.Kms;
@@ -100,6 +101,33 @@ private void TestDecryptDataRowRecordWithParentKeyMetaShouldSucceed()
100101
aeadEnvelopeCryptoMock.Verify(x => x.EnvelopeDecrypt(encryptedData, dataRowKey.EncryptedKey, dataRowKey.Created, intermediateCryptoKeyMock.Object));
101102
}
102103

104+
[Fact]
105+
private async Task TestDecryptDataRowRecordAsyncWithParentKeyMetaShouldSucceed()
106+
{
107+
KeyMeta intermediateKeyMeta = new KeyMeta(partition.IntermediateKeyId, ikDateTime);
108+
EnvelopeKeyRecord dataRowKey = new EnvelopeKeyRecord(drkDateTime, intermediateKeyMeta, new byte[] { 0, 1, 2, 3 });
109+
byte[] encryptedData = { 4, 5, 6, 7 };
110+
111+
JObject dataRowRecord = JObject.FromObject(new Dictionary<string, object>
112+
{
113+
{ "Key", dataRowKey.ToJson() },
114+
{ "Data", Convert.ToBase64String(encryptedData) },
115+
});
116+
117+
envelopeEncryptionJsonImplSpy.Setup(x => x.WithIntermediateKeyForRead(intermediateKeyMeta, It.IsAny<Func<CryptoKey, byte[]>>()))
118+
.Returns<KeyMeta, Func<CryptoKey, byte[]>>((keyMeta, functionWithIntermediateKey) => functionWithIntermediateKey(intermediateCryptoKeyMock.Object));
119+
120+
byte[] expectedDecryptedPayload = { 11, 12, 13, 14 };
121+
aeadEnvelopeCryptoMock
122+
.Setup(x => x.EnvelopeDecrypt(
123+
encryptedData, dataRowKey.EncryptedKey, dataRowKey.Created, intermediateCryptoKeyMock.Object))
124+
.Returns(expectedDecryptedPayload);
125+
126+
byte[] actualDecryptedPayload = await envelopeEncryptionJsonImplSpy.Object.DecryptDataRowRecordAsync(dataRowRecord);
127+
Assert.Equal(expectedDecryptedPayload, actualDecryptedPayload);
128+
aeadEnvelopeCryptoMock.Verify(x => x.EnvelopeDecrypt(encryptedData, dataRowKey.EncryptedKey, dataRowKey.Created, intermediateCryptoKeyMock.Object));
129+
}
130+
103131
[Fact]
104132
private void TestDecryptDataRowRecordWithInvalidParentKeyMetaShouldFail()
105133
{
@@ -138,22 +166,22 @@ private void TestEncryptPayload()
138166
intermediateCryptoKeyMock.Setup(x => x.GetCreated()).Returns(ikDateTime);
139167

140168
envelopeEncryptionJsonImplSpy
141-
.Setup(x => x.WithIntermediateKeyForWrite(It.IsAny<Func<CryptoKey, EnvelopeEncryptResult>>()))
142-
.Returns<Func<CryptoKey, EnvelopeEncryptResult>>(functionWithIntermediateKey =>
169+
.Setup(x => x.WithIntermediateKeyForWrite(It.IsAny<Func<CryptoKey, EnvelopeEncryptResult<KeyMeta>>>()))
170+
.Returns<Func<CryptoKey, EnvelopeEncryptResult<KeyMeta>>>(functionWithIntermediateKey =>
143171
functionWithIntermediateKey(intermediateCryptoKeyMock.Object));
144172

145173
byte[] decryptedPayload = Encoding.Unicode.GetBytes("somepayload");
146174
KeyMeta intermediateKeyMeta = new KeyMeta(partition.IntermediateKeyId, ikDateTime);
147175
byte[] encryptedPayload = { 0, 1, 2, 3 };
148176
byte[] encryptedKey = { 4, 5, 6, 7 };
149-
EnvelopeEncryptResult envelopeEncryptResult = new EnvelopeEncryptResult
177+
EnvelopeEncryptResult<KeyMeta> envelopeEncryptResult = new EnvelopeEncryptResult<KeyMeta>
150178
{
151179
CipherText = encryptedPayload,
152180
EncryptedKey = encryptedKey,
153181
UserState = intermediateKeyMeta,
154182
};
155183
aeadEnvelopeCryptoMock
156-
.Setup(x => x.EnvelopeEncrypt(decryptedPayload, intermediateCryptoKeyMock.Object, intermediateKeyMeta))
184+
.Setup(x => x.EnvelopeEncrypt<KeyMeta>(decryptedPayload, intermediateCryptoKeyMock.Object, intermediateKeyMeta))
157185
.Returns(envelopeEncryptResult);
158186

159187
EnvelopeKeyRecord expectedDataRowKey = new EnvelopeKeyRecord(drkDateTime, intermediateKeyMeta, encryptedKey);
@@ -175,6 +203,49 @@ private void TestEncryptPayload()
175203
actualDataRowRecord.GetValue("Key").ToObject<JObject>().GetValue("ParentKeyMeta").ToObject<JObject>()));
176204
}
177205

206+
[Fact]
207+
private async Task TestEncryptPayloadAsync()
208+
{
209+
intermediateCryptoKeyMock.Setup(x => x.GetCreated()).Returns(ikDateTime);
210+
211+
envelopeEncryptionJsonImplSpy
212+
.Setup(x => x.WithIntermediateKeyForWrite(It.IsAny<Func<CryptoKey, EnvelopeEncryptResult<KeyMeta>>>()))
213+
.Returns<Func<CryptoKey, EnvelopeEncryptResult<KeyMeta>>>(functionWithIntermediateKey =>
214+
functionWithIntermediateKey(intermediateCryptoKeyMock.Object));
215+
216+
byte[] decryptedPayload = Encoding.Unicode.GetBytes("somepayload");
217+
KeyMeta intermediateKeyMeta = new KeyMeta(partition.IntermediateKeyId, ikDateTime);
218+
byte[] encryptedPayload = { 0, 1, 2, 3 };
219+
byte[] encryptedKey = { 4, 5, 6, 7 };
220+
EnvelopeEncryptResult<KeyMeta> envelopeEncryptResult = new EnvelopeEncryptResult<KeyMeta>
221+
{
222+
CipherText = encryptedPayload,
223+
EncryptedKey = encryptedKey,
224+
UserState = intermediateKeyMeta,
225+
};
226+
aeadEnvelopeCryptoMock
227+
.Setup(x => x.EnvelopeEncrypt<KeyMeta>(decryptedPayload, intermediateCryptoKeyMock.Object, intermediateKeyMeta))
228+
.Returns(envelopeEncryptResult);
229+
230+
EnvelopeKeyRecord expectedDataRowKey = new EnvelopeKeyRecord(drkDateTime, intermediateKeyMeta, encryptedKey);
231+
JObject expectedDataRowRecord = JObject.FromObject(new Dictionary<string, object>
232+
{
233+
{ "Key", expectedDataRowKey.ToJson() },
234+
{ "Data", Convert.ToBase64String(encryptedPayload) },
235+
});
236+
237+
JObject actualDataRowRecord = await envelopeEncryptionJsonImplSpy.Object.EncryptPayloadAsync(decryptedPayload);
238+
239+
// Asserting individual fields as work-around to hard-coding DateTimeOffset.UtcNow usage
240+
Assert.Equal(expectedDataRowRecord.GetValue("Data").ToObject<string>(), actualDataRowRecord.GetValue("Data").ToObject<string>());
241+
Assert.Equal(
242+
expectedDataRowRecord.GetValue("Key").ToObject<JObject>().GetValue("Key").ToString(),
243+
actualDataRowRecord.GetValue("Key").ToObject<JObject>().GetValue("Key").ToString());
244+
Assert.True(JToken.DeepEquals(
245+
expectedDataRowRecord.GetValue("Key").ToObject<JObject>().GetValue("ParentKeyMeta").ToObject<JObject>(),
246+
actualDataRowRecord.GetValue("Key").ToObject<JObject>().GetValue("ParentKeyMeta").ToObject<JObject>()));
247+
}
248+
178249
[Fact]
179250
private void TestDisposeSuccess()
180251
{

csharp/AppEncryption/AppEncryption.Tests/AppEncryption/Kms/AwsKeyManagementServiceImplTest.cs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,45 @@ public void TestDecryptKeySuccessful()
125125
Assert.Equal(cryptoKeyMock.Object, actualCryptoKey);
126126
}
127127

128+
[Fact]
129+
public async Task TestDecryptKeyAsyncSuccessful()
130+
{
131+
byte[] encryptedKey = { 0, 1 };
132+
byte[] kmsKeyEncryptionKey = { 2, 3 };
133+
134+
JObject kmsKeyEnvelopeTest = JObject.FromObject(new Dictionary<string, object>
135+
{
136+
{ EncryptedKey, Convert.ToBase64String(encryptedKey) },
137+
{
138+
KmsKeksKey, new List<Dictionary<string, object>>
139+
{
140+
new Dictionary<string, object>
141+
{
142+
{ RegionKey, UsWest1 },
143+
{ ArnKey, ArnUsWest1 },
144+
{ EncryptedKek, Convert.ToBase64String(kmsKeyEncryptionKey) },
145+
},
146+
}
147+
},
148+
});
149+
150+
DateTimeOffset now = DateTimeOffset.UtcNow;
151+
bool revoked = false;
152+
awsKeyManagementServiceImplSpy
153+
.Setup(x => x.DecryptKmsEncryptedKey(
154+
amazonKeyManagementServiceClientMock.Object,
155+
encryptedKey,
156+
now,
157+
kmsKeyEncryptionKey,
158+
revoked))
159+
.Returns(cryptoKeyMock.Object);
160+
161+
CryptoKey actualCryptoKey = await awsKeyManagementServiceImplSpy.Object.DecryptKeyAsync(
162+
new Asherah.AppEncryption.Util.Json(kmsKeyEnvelopeTest).ToUtf8(), now, revoked);
163+
Assert.Equal(cryptoKeyMock.Object, actualCryptoKey);
164+
}
165+
166+
128167
[Fact]
129168
public void TestDecryptKeyWithMissingRegionInPayloadShouldSkipAndSucceed()
130169
{
@@ -516,6 +555,83 @@ public void TestEncryptKeySuccessful()
516555
}
517556
}
518557

558+
[Fact]
559+
public async Task TestEncryptKeyAsyncSuccessful()
560+
{
561+
byte[] encryptedKey = { 3, 4 };
562+
byte[] dataKeyPlainText = { 1, 2 };
563+
byte[] dataKeyCipherText = { 5, 6 };
564+
byte[] encryptKeyCipherText = { 7, 8 };
565+
566+
JObject encryptKeyAndBuildResultJson = JObject.FromObject(new Dictionary<string, object>
567+
{
568+
{ RegionKey, UsEast1 },
569+
{ ArnKey, ArnUsEast1 },
570+
{ EncryptedKek, Convert.ToBase64String(encryptKeyCipherText) },
571+
});
572+
573+
JObject kmsKeyEnvelope = JObject.FromObject(new Dictionary<string, object>
574+
{
575+
{ EncryptedKey, Convert.ToBase64String(encryptedKey) },
576+
{
577+
KmsKeksKey, new List<object>
578+
{
579+
new Dictionary<string, object>
580+
{
581+
{ RegionKey, UsWest1 },
582+
{ ArnKey, ArnUsWest1 },
583+
{ EncryptedKek, Convert.ToBase64String(dataKeyCipherText) },
584+
},
585+
encryptKeyAndBuildResultJson,
586+
}
587+
},
588+
});
589+
GenerateDataKeyResponse generateDataKeyResult = new GenerateDataKeyResponse
590+
{
591+
Plaintext = new MemoryStream(dataKeyPlainText, 0, dataKeyPlainText.Length, true, true),
592+
CiphertextBlob = new MemoryStream(dataKeyCipherText, 0, dataKeyCipherText.Length, true, true),
593+
};
594+
595+
Mock<CryptoKey> generatedDataKeyCryptoKey = new Mock<CryptoKey>();
596+
string keyId = ArnUsWest1;
597+
598+
string outKeyId = keyId;
599+
awsKeyManagementServiceImplSpy
600+
.Setup(x => x.GenerateDataKey(
601+
It.IsAny<OrderedDictionary>(),
602+
out outKeyId))
603+
.Returns(generateDataKeyResult);
604+
cryptoMock.Setup(x => x.GenerateKeyFromBytes(generateDataKeyResult.Plaintext.ToArray()))
605+
.Returns(generatedDataKeyCryptoKey.Object);
606+
cryptoMock.Setup(x => x.EncryptKey(cryptoKeyMock.Object, generatedDataKeyCryptoKey.Object))
607+
.Returns(encryptedKey);
608+
awsKeyManagementServiceImplSpy.Setup(x =>
609+
x.EncryptKeyAndBuildResult(
610+
It.IsAny<IAmazonKeyManagementService>(),
611+
UsEast1,
612+
ArnUsEast1,
613+
dataKeyPlainText))
614+
.Returns(Option<JObject>.Some(encryptKeyAndBuildResultJson));
615+
616+
byte[] encryptedResult = await awsKeyManagementServiceImplSpy.Object.EncryptKeyAsync(cryptoKeyMock.Object);
617+
JObject kmsKeyEnvelopeResult = new Asherah.AppEncryption.Util.Json(encryptedResult).ToJObject();
618+
619+
Assert.Equal(new byte[] { 0, 0 }, dataKeyPlainText);
620+
621+
// This is a workaround for https://github.com/JamesNK/Newtonsoft.Json/issues/1437
622+
// If DeepEquals fails due to mismatching array order, compare the elements individually
623+
if (!JToken.DeepEquals(kmsKeyEnvelope, kmsKeyEnvelopeResult))
624+
{
625+
JArray kmsKeyEnvelopeKmsKeks = JArray.FromObject(kmsKeyEnvelope[KmsKeksKey]
626+
.OrderBy(k => k[RegionKey]));
627+
JArray kmsKeyEnvelopeResultKmsKeks = JArray.FromObject(kmsKeyEnvelopeResult[KmsKeksKey]
628+
.OrderBy(k => k[RegionKey]));
629+
630+
Assert.True(JToken.DeepEquals(kmsKeyEnvelope[EncryptedKey], kmsKeyEnvelopeResult[EncryptedKey]));
631+
Assert.True(JToken.DeepEquals(kmsKeyEnvelopeKmsKeks, kmsKeyEnvelopeResultKmsKeks));
632+
}
633+
}
634+
519635
[Fact]
520636
public void TestEncryptKeyShouldThrowExceptionAndWipeBytes()
521637
{

csharp/AppEncryption/AppEncryption.Tests/Crypto/Envelope/AeadEnvelopeCryptoTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ private void TestEnvelopeEncrypt()
8585
aeadEnvelopeCryptoMock.Setup(x => x.EncryptKey(keyMock.Object, keyEncryptionKey.Object))
8686
.Returns(expectedEncryptedKey);
8787
aeadEnvelopeCryptoMock.Setup(x => x.GenerateKey()).Returns(keyMock.Object);
88-
aeadEnvelopeCryptoMock.Setup(x => x.EnvelopeEncrypt(expectedPlainText, keyEncryptionKey.Object, null))
88+
aeadEnvelopeCryptoMock.Setup(x => x.EnvelopeEncrypt<object>(expectedPlainText, keyEncryptionKey.Object, null))
8989
.CallBase();
9090

91-
EnvelopeEncryptResult result = aeadEnvelopeCryptoMock.Object.EnvelopeEncrypt(expectedPlainText, keyEncryptionKey.Object, null);
91+
EnvelopeEncryptResult<object> result = aeadEnvelopeCryptoMock.Object.EnvelopeEncrypt<object>(expectedPlainText, keyEncryptionKey.Object, null);
9292
Assert.Equal(expectedCipherText, result.CipherText);
9393
Assert.Equal(expectedEncryptedKey, result.EncryptedKey);
9494
Assert.Null(result.UserState);
@@ -99,11 +99,11 @@ private void TestEnvelopeEncrypt()
9999
private void TestEnvelopeEncryptWithTwoParams()
100100
{
101101
byte[] encryptedKey = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
102-
aeadEnvelopeCryptoMock.Setup(x => x.EnvelopeEncrypt(
102+
aeadEnvelopeCryptoMock.Setup(x => x.EnvelopeEncrypt<object>(
103103
encryptedKey, keyEncryptionKey.Object)).CallBase();
104104

105-
aeadEnvelopeCryptoMock.Object.EnvelopeEncrypt(encryptedKey, keyEncryptionKey.Object);
106-
aeadEnvelopeCryptoMock.Verify(x => x.EnvelopeEncrypt(encryptedKey, keyEncryptionKey.Object, null));
105+
aeadEnvelopeCryptoMock.Object.EnvelopeEncrypt<object>(encryptedKey, keyEncryptionKey.Object);
106+
aeadEnvelopeCryptoMock.Verify(x => x.EnvelopeEncrypt<object>(encryptedKey, keyEncryptionKey.Object, null));
107107
}
108108

109109
[Fact]

csharp/AppEncryption/AppEncryption.Tests/GlobalSuppressions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77

88
[assembly: SuppressMessage("Design", "CA2201:Do not raise reserved exception types", Justification = "Test methods may use SystemException for testing purposes", Scope = "module")]
99
[assembly: SuppressMessage("Design", "CA1816:Call GC.SuppressFinalize correctly", Justification = "Test classes do not need to call GC.SuppressFinalize.")]
10+
[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Test method names commonly use underscores for readability", Scope = "module")]

csharp/AppEncryption/AppEncryption/Envelope/EnvelopeEncryptionBytesImpl.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Runtime.CompilerServices;
3+
using System.Threading.Tasks;
34
using GoDaddy.Asherah.AppEncryption.Util;
45
using Microsoft.Extensions.Logging;
56
using Newtonsoft.Json.Linq;
@@ -69,5 +70,17 @@ public virtual byte[] EncryptPayload(byte[] payload)
6970
Json drrJson = new Json(envelopeEncryptionJson.EncryptPayload(payload));
7071
return drrJson.ToUtf8();
7172
}
73+
74+
/// <inheritdoc/>
75+
public virtual async Task<byte[]> DecryptDataRowRecordAsync(byte[] dataRowRecord)
76+
{
77+
return await Task.FromResult(DecryptDataRowRecord(dataRowRecord));
78+
}
79+
80+
/// <inheritdoc/>
81+
public virtual async Task<byte[]> EncryptPayloadAsync(byte[] payload)
82+
{
83+
return await Task.FromResult(EncryptPayload(payload));
84+
}
7285
}
7386
}

0 commit comments

Comments
 (0)