Skip to content

Commit 825691b

Browse files
committed
feat(KSA): Describe Mutation
1 parent ff03900 commit 825691b

File tree

6 files changed

+308
-5
lines changed

6 files changed

+308
-5
lines changed

AwsCryptographicMaterialProviders/dafny/AwsCryptographyKeyStoreAdmin/src/AwsCryptographyKeyStoreAdminOperations.dfy

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
include "../Model/AwsCryptographyKeyStoreAdminTypes.dfy"
44
include "Mutations.dfy"
55
include "KmsUtils.dfy"
6+
include "DescribeMutation.dfy"
67

78
module AwsCryptographyKeyStoreAdminOperations refines AbstractAwsCryptographyKeyStoreAdminOperations {
89
import opened AwsKmsUtils
@@ -12,6 +13,7 @@ module AwsCryptographyKeyStoreAdminOperations refines AbstractAwsCryptographyKey
1213
import KeyStoreTypes = KeyStoreOperations.Types
1314
import KMS = Com.Amazonaws.Kms
1415
import Mutations
16+
import DM = DescribeMutation
1517
import KmsUtils
1618

1719
datatype Config = Config(
@@ -340,6 +342,10 @@ module AwsCryptographyKeyStoreAdminOperations refines AbstractAwsCryptographyKey
340342
)
341343
returns (output: Result<DescribeMutationOutput, Error>)
342344
{
343-
return Failure(Types.KeyStoreAdminException(message := "Implement me"));
345+
var input := DM.InternalDescribeMutationInput(
346+
Identifier := input.Identifier,
347+
storage := config.storage);
348+
output := DM.DescribeMutation(input);
349+
return output;
344350
}
345351
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
include "../Model/AwsCryptographyKeyStoreAdminTypes.dfy"
4+
include "MutationStateStructures.dfy"
5+
include "MutationsConstants.dfy"
6+
7+
module {:options "/functionSyntax:4" } DescribeMutation {
8+
import opened StandardLibrary
9+
import opened Wrappers
10+
11+
import KeyStoreTypes = AwsCryptographyKeyStoreAdminTypes.AwsCryptographyKeyStoreTypes
12+
import Types = AwsCryptographyKeyStoreAdminTypes
13+
import StateStrucs = MutationStateStructures
14+
import M_ErrorMessages = MutationsConstants.ErrorMessages
15+
16+
datatype InternalDescribeMutationInput = | InternalDescribeMutationInput (
17+
nameonly Identifier: string ,
18+
nameonly storage: Types.AwsCryptographyKeyStoreTypes.IKeyStorageInterface
19+
)
20+
21+
method DescribeMutation(
22+
input: InternalDescribeMutationInput
23+
)
24+
returns (output: Result<Types.DescribeMutationOutput, Types.Error>)
25+
requires input.storage.ValidState()
26+
ensures input.storage.ValidState()
27+
modifies input.storage.Modifies
28+
{
29+
// TODO-Mutations-GA :: Consolidate the Index and Commitment validation here with ApplyMutation's
30+
var storageReq := KeyStoreTypes.GetMutationInput(
31+
Identifier := input.Identifier
32+
);
33+
var fetchMutation? := input.storage.GetMutation(storageReq);
34+
if (fetchMutation?.Failure?) {
35+
return Failure(Types.Error.AwsCryptographyKeyStore(AwsCryptographyKeyStore := fetchMutation?.error));
36+
}
37+
var fetchMutation := fetchMutation?.value;
38+
if (fetchMutation.MutationCommitment.None? && fetchMutation.MutationIndex.Some?) {
39+
return Failure(
40+
Types.MutationInvalidException(
41+
message := "Found a Mutation Index but no Mutation Commitment."
42+
+ " The Key Store's Storage, for this Branch Key, has become corrupted."
43+
+ " Recommend auditing the Branch Key's items for tampering."
44+
+ " Recommend auditing access to the storage."
45+
+ " To successfully start a new mutation, delete the Mutation Index."
46+
+ " But know that the new mutation will fail if any corrupt items are encountered."
47+
+ "\nBranch Key ID: " + input.Identifier + ";"
48+
+ " Mutation Index UUID: " + fetchMutation.MutationIndex.value.UUID));
49+
}
50+
if (fetchMutation.MutationCommitment.None? && fetchMutation.MutationIndex.None?) {
51+
var no := Types.MutationInFlight.No(No := "No Mutation in-flight for " + input.Identifier + ".");
52+
return Success(Types.DescribeMutationOutput(MutationInFlight := no));
53+
}
54+
var Commitment := fetchMutation.MutationCommitment.value;
55+
var token := Types.MutationToken(
56+
Identifier := Commitment.Identifier,
57+
UUID := Commitment.UUID,
58+
CreateTime := Commitment.CreateTime);
59+
:- Need(
60+
fetchMutation.MutationIndex.Some?,
61+
Types.MutationInvalidException(
62+
message := "No Mutation Index exsists for this in-flight mutation of Branch Key ID " + input.Identifier + " ."
63+
// TODO-Mutations-GA :: More details on this error
64+
));
65+
var Index := fetchMutation.MutationIndex.value;
66+
:- Need(
67+
// If custom storage is really bad
68+
Commitment.Identifier == Index.Identifier,
69+
Types.MutationInvalidException(
70+
message := "The Mutation Index read from storage and the Mutation Commitment are for different Branch Key IDs."
71+
+ " The Storage implementation is wrong, or something terrible has happened to storage."
72+
+ " Branch Key ID: " + input.Identifier + ";"
73+
+ " Mutation Commitment Branch Key ID: " + Commitment.Identifier + ";"
74+
+ " Mutation Index Branch Key ID: " + Index.Identifier + ";"
75+
));
76+
:- Need(
77+
Commitment.UUID == Index.UUID,
78+
Types.MutationInvalidException(
79+
message := "The Mutation Index read from storage and the Mutation Commitment are for different Mutations."
80+
+ " Branch Key ID: " + input.Identifier + ";"
81+
+ " Mutation Commitment UUID: " + Commitment.UUID + ";"
82+
+ " Mutation Index UUID: " + Index.UUID + ";"
83+
));
84+
var CommitmentAndIndex := StateStrucs.CommitmentAndIndex(
85+
Commitment := Commitment,
86+
Index := Index);
87+
assert CommitmentAndIndex.ValidState();
88+
// TODO-Mutations-GA :: Use System Key to Verify Commitment and Index
89+
var MutationToApply :- StateStrucs.DeserializeMutation(CommitmentAndIndex);
90+
var original := Types.MutableBranchKeyProperties(
91+
KmsArn := MutationToApply.Original.kmsArn,
92+
CustomEncryptionContext := MutationToApply.Original.customEncryptionContext
93+
);
94+
var terminal := Types.MutableBranchKeyProperties(
95+
KmsArn := MutationToApply.Terminal.kmsArn,
96+
CustomEncryptionContext := MutationToApply.Terminal.customEncryptionContext
97+
);
98+
var details := Types.MutationDetails(
99+
Original := original,
100+
Terminal := terminal,
101+
Input := MutationToApply.Input,
102+
SystemKey := "TrustStorage",
103+
CreateTime := MutationToApply.CreateTime,
104+
UUID := MutationToApply.UUID
105+
);
106+
var description := Types.MutationDescription(
107+
MutationDetails := details,
108+
MutationToken := token);
109+
var inFlight := Types.MutationInFlight.Yes(
110+
Yes := description);
111+
return Success(Types.DescribeMutationOutput(MutationInFlight := inFlight));
112+
}
113+
}

AwsCryptographicMaterialProviders/dafny/AwsCryptographyKeyStoreAdmin/src/Mutations.dfy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,13 @@ module {:options "/functionSyntax:4" } Mutations {
166166
return Failure(
167167
Types.MutationInvalidException(
168168
message := "Found a Mutation Index but no Mutation Commitment."
169-
+ " The Key Store's Storage has become corrupted."
169+
+ " The Key Store's Storage, for this Branch Key, has become corrupted."
170170
+ " Recommend auditing the Branch Key's items for tampering."
171171
+ " Recommend auditing access to the storage."
172172
+ " To successfully start a new mutation, delete the Mutation Index."
173173
+ " But know that the new mutation will fail if any corrupt items are encountered."
174-
+ " Branch Key ID: " + input.Identifier
175-
+ " \tMutation Index UUID: " + indexUUID));
174+
+ "\nBranch Key ID: " + input.Identifier + ";"
175+
+ " Mutation Index UUID: " + indexUUID));
176176
}
177177

178178
if (readItems.MutationCommitment.Some?) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package software.amazon.cryptography.example.hierarchy;
4+
5+
import static software.amazon.cryptography.example.hierarchy.MutationResumeExample.executeInitialize;
6+
7+
import java.util.HashMap;
8+
import java.util.Objects;
9+
import javax.annotation.Nullable;
10+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
11+
import software.amazon.awssdk.services.kms.KmsClient;
12+
import software.amazon.cryptography.example.DdbHelper;
13+
import software.amazon.cryptography.keystoreadmin.KeyStoreAdmin;
14+
import software.amazon.cryptography.keystoreadmin.model.ApplyMutationInput;
15+
import software.amazon.cryptography.keystoreadmin.model.ApplyMutationOutput;
16+
import software.amazon.cryptography.keystoreadmin.model.ApplyMutationResult;
17+
import software.amazon.cryptography.keystoreadmin.model.DescribeMutationInput;
18+
import software.amazon.cryptography.keystoreadmin.model.DescribeMutationOutput;
19+
import software.amazon.cryptography.keystoreadmin.model.InitializeMutationInput;
20+
import software.amazon.cryptography.keystoreadmin.model.InitializeMutationOutput;
21+
import software.amazon.cryptography.keystoreadmin.model.KeyManagementStrategy;
22+
import software.amazon.cryptography.keystoreadmin.model.MutationConflictException;
23+
import software.amazon.cryptography.keystoreadmin.model.MutationDescription;
24+
import software.amazon.cryptography.keystoreadmin.model.MutationToken;
25+
import software.amazon.cryptography.keystoreadmin.model.Mutations;
26+
import software.amazon.cryptography.keystoreadmin.model.SystemKey;
27+
import software.amazon.cryptography.keystoreadmin.model.TrustStorage;
28+
29+
public class DescribeMutationExample {
30+
31+
@Nullable
32+
public static MutationToken Example(
33+
String keyStoreTableName,
34+
String logicalKeyStoreName,
35+
String branchKeyId,
36+
@Nullable DynamoDbClient dynamoDbClient
37+
) {
38+
KeyStoreAdmin admin = AdminProvider.admin(
39+
keyStoreTableName,
40+
logicalKeyStoreName,
41+
dynamoDbClient
42+
);
43+
DescribeMutationInput input = DescribeMutationInput
44+
.builder()
45+
.Identifier(branchKeyId)
46+
.build();
47+
DescribeMutationOutput output = admin.DescribeMutation(input);
48+
if (output.MutationInFlight().No() != null) {
49+
System.out.println(
50+
"There is no mutation in flight for Branch Key ID: " + branchKeyId
51+
);
52+
return null;
53+
}
54+
if (output.MutationInFlight().Yes() != null) {
55+
MutationDescription description = output.MutationInFlight().Yes();
56+
System.out.println(
57+
"There is a mutation in flight for Branch Key ID: " + branchKeyId
58+
);
59+
System.out.println(
60+
"Description: " + description.MutationDetails().UUID()
61+
);
62+
return description.MutationToken();
63+
}
64+
throw new RuntimeException("Key Store Admin returned nonsensical response");
65+
}
66+
67+
public static MutationToken InitMutation(
68+
String keyStoreTableName,
69+
String logicalKeyStoreName,
70+
String kmsKeyArnTerminal,
71+
String branchKeyId,
72+
SystemKey systemKey,
73+
@Nullable DynamoDbClient dynamoDbClient,
74+
@Nullable KmsClient kmsClient
75+
) {
76+
kmsClient = AdminProvider.kms(kmsClient);
77+
KeyManagementStrategy strategy = AdminProvider.strategy(kmsClient);
78+
KeyStoreAdmin admin = AdminProvider.admin(
79+
keyStoreTableName,
80+
logicalKeyStoreName,
81+
dynamoDbClient
82+
);
83+
HashMap<String, String> terminalEC = new HashMap<>();
84+
terminalEC.put("Robbie", "is a dog.");
85+
Mutations mutations = Mutations
86+
.builder()
87+
.TerminalEncryptionContext(terminalEC)
88+
.TerminalKmsArn(kmsKeyArnTerminal)
89+
.build();
90+
InitializeMutationInput initInput = InitializeMutationInput
91+
.builder()
92+
.Mutations(mutations)
93+
.Identifier(branchKeyId)
94+
.Strategy(strategy)
95+
.SystemKey(systemKey)
96+
.build();
97+
98+
MutationToken token = executeInitialize(
99+
branchKeyId,
100+
admin,
101+
initInput,
102+
"InitLogs"
103+
);
104+
return token;
105+
}
106+
107+
public static void CompleteExample(
108+
String keyStoreTableName,
109+
String logicalKeyStoreName,
110+
String kmsKeyArnOriginal,
111+
String kmsKeyArnTerminal,
112+
String branchKeyId,
113+
SystemKey systemKey,
114+
@Nullable DynamoDbClient dynamoDbClient,
115+
@Nullable KmsClient kmsClient
116+
) {
117+
CreateKeyExample.CreateKey(
118+
keyStoreTableName,
119+
logicalKeyStoreName,
120+
kmsKeyArnOriginal,
121+
branchKeyId,
122+
dynamoDbClient
123+
);
124+
125+
MutationToken fromInit = InitMutation(
126+
keyStoreTableName,
127+
logicalKeyStoreName,
128+
kmsKeyArnTerminal,
129+
branchKeyId,
130+
systemKey,
131+
dynamoDbClient,
132+
kmsClient
133+
);
134+
135+
MutationToken fromDescribe = Example(
136+
keyStoreTableName,
137+
logicalKeyStoreName,
138+
branchKeyId,
139+
dynamoDbClient
140+
);
141+
assert fromDescribe != null;
142+
assert Objects.equals(fromInit.UUID(), fromDescribe.UUID());
143+
}
144+
}

AwsCryptographicMaterialProviders/runtimes/java/src/examples/java/software/amazon/cryptography/example/hierarchy/MutationResumeExample.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ private static ApplyMutationResult workPage(
202202
return result;
203203
}
204204

205-
private static MutationToken executeInitialize(
205+
static MutationToken executeInitialize(
206206
String branchKeyId,
207207
KeyStoreAdmin admin,
208208
InitializeMutationInput initInput,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package software.amazon.cryptography.example.hierarchy.mutations;
2+
3+
import org.testng.annotations.Test;
4+
import software.amazon.cryptography.example.Fixtures;
5+
import software.amazon.cryptography.example.StorageCheater;
6+
import software.amazon.cryptography.example.hierarchy.DescribeMutationExample;
7+
import software.amazon.cryptography.keystore.KeyStorageInterface;
8+
import software.amazon.cryptography.keystoreadmin.model.SystemKey;
9+
import software.amazon.cryptography.keystoreadmin.model.TrustStorage;
10+
11+
public class DescribeMutationTest {
12+
13+
static final String testPrefix = "mutation-describe-java-test-";
14+
15+
@Test
16+
public void test() {
17+
SystemKey systemKey = SystemKey
18+
.builder()
19+
.trustStorage(TrustStorage.builder().build())
20+
.build();
21+
KeyStorageInterface storage = StorageCheater.create(
22+
Fixtures.ddbClientWest2,
23+
Fixtures.TEST_KEYSTORE_NAME,
24+
Fixtures.TEST_LOGICAL_KEYSTORE_NAME
25+
);
26+
final String branchKeyId =
27+
testPrefix + java.util.UUID.randomUUID().toString();
28+
DescribeMutationExample.CompleteExample(
29+
Fixtures.TEST_KEYSTORE_NAME,
30+
Fixtures.TEST_LOGICAL_KEYSTORE_NAME,
31+
Fixtures.KEYSTORE_KMS_ARN,
32+
Fixtures.POSTAL_HORN_KEY_ARN,
33+
branchKeyId,
34+
systemKey,
35+
Fixtures.ddbClientWest2,
36+
Fixtures.kmsClientWest2
37+
);
38+
Fixtures.cleanUpBranchKeyId(storage, branchKeyId);
39+
}
40+
}

0 commit comments

Comments
 (0)