Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import aws.cryptography.esdk.testserver.server.model.ESDKClientConfig;
import aws.cryptography.esdk.testserver.server.model.ESDKClientError;
import aws.cryptography.esdk.testserver.server.model.Keyring;
import aws.cryptography.esdk.testserver.server.model.KmsEcdhKeyAgreementScheme;
import aws.cryptography.esdk.testserver.server.model.MultiKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.RawEcdhKeyAgreementScheme;
import aws.cryptography.esdk.testserver.server.model.RequiredEncryptionContextCmmConfig;

/**
Expand All @@ -19,8 +21,9 @@
* validated, recursing through the recursive variants to any nesting depth
* (Requirement 2.5): {@link CryptographicMaterialsManager} (Default /
* RequiredEncryptionContext / Caching, where Caching and
* RequiredEncryptionContext wrap another CMM) and {@link Keyring} (whose Multi
* variant contains child keyrings and an optional generator keyring).
* RequiredEncryptionContext wrap another CMM), {@link Keyring} (whose Multi
* variant contains child keyrings and an optional generator keyring), and the
* Raw ECDH and AWS KMS ECDH key agreement schemes.
*
* <p>When a polymorphic shape has zero, or two or more, variant members set, the
* request is rejected with an {@link ESDKClientError} <em>before</em> the
Expand Down Expand Up @@ -121,9 +124,21 @@ public void validateKeyring(Keyring keyring) {
if (keyring.getMulti() != null) {
set++;
}
if (keyring.getRawEcdh() != null) {
set++;
}
if (keyring.getAwsKmsEcdh() != null) {
set++;
}
if (set != 1) {
throw error("Keyring", set, keyringVariants());
}
if (keyring.getRawEcdh() != null) {
validateRawEcdhScheme(keyring.getRawEcdh().getKeyAgreementScheme());
}
if (keyring.getAwsKmsEcdh() != null) {
validateKmsEcdhScheme(keyring.getAwsKmsEcdh().getKeyAgreementScheme());
}

MultiKeyringConfig multi = keyring.getMulti();
if (multi != null) {
Expand All @@ -136,9 +151,48 @@ public void validateKeyring(Keyring keyring) {
}
}

private static void validateRawEcdhScheme(RawEcdhKeyAgreementScheme scheme) {
String variants = "RawPrivateKeyToStaticPublicKey, EphemeralPrivateKeyToStaticPublicKey, "
+ "PublicKeyDiscovery";
if (scheme == null) {
throw error("RawEcdhKeyAgreementScheme", 0, variants);
}
int set = 0;
if (scheme.getRawPrivateKeyToStaticPublicKey() != null) {
set++;
}
if (scheme.getEphemeralPrivateKeyToStaticPublicKey() != null) {
set++;
}
if (scheme.getPublicKeyDiscovery() != null) {
set++;
}
if (set != 1) {
throw error("RawEcdhKeyAgreementScheme", set, variants);
}
}

private static void validateKmsEcdhScheme(KmsEcdhKeyAgreementScheme scheme) {
String variants = "KmsPrivateKeyToStaticPublicKey, KmsPublicKeyDiscovery";
if (scheme == null) {
throw error("KmsEcdhKeyAgreementScheme", 0, variants);
}
int set = 0;
if (scheme.getKmsPrivateKeyToStaticPublicKey() != null) {
set++;
}
if (scheme.getKmsPublicKeyDiscovery() != null) {
set++;
}
if (set != 1) {
throw error("KmsEcdhKeyAgreementScheme", set, variants);
}
}

private static String keyringVariants() {
return "AwsKms, AwsKmsMrk, AwsKmsMultiKeyring, AwsKmsMrkMultiKeyring, "
+ "AwsKmsDiscovery, AwsKmsMrkDiscovery, AwsKmsRsa, AwsKmsHierarchical, RawAes, RawRsa, Multi";
+ "AwsKmsDiscovery, AwsKmsMrkDiscovery, AwsKmsRsa, AwsKmsEcdh, AwsKmsHierarchical, "
+ "RawAes, RawRsa, RawEcdh, Multi";
}

private static ESDKClientError error(String shape, int set, String variants) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aws.cryptography.esdk.testserver.server.config;

import aws.cryptography.esdk.testserver.server.model.AwsKmsDiscoveryKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.AwsKmsEcdhKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.AwsKmsHierarchicalKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.AwsKmsKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.AwsKmsMrkKeyringConfig;
Expand All @@ -11,11 +12,15 @@
import aws.cryptography.esdk.testserver.server.model.CachingCmmConfig;
import aws.cryptography.esdk.testserver.server.model.CryptographicMaterialsManager;
import aws.cryptography.esdk.testserver.server.model.DefaultCmmConfig;
import aws.cryptography.esdk.testserver.server.model.EcdhCurveSpec;
import aws.cryptography.esdk.testserver.server.model.ESDKClientConfig;
import aws.cryptography.esdk.testserver.server.model.Keyring;
import aws.cryptography.esdk.testserver.server.model.KmsEcdhKeyAgreementScheme;
import aws.cryptography.esdk.testserver.server.model.KmsRsaEncryptionAlgorithm;
import aws.cryptography.esdk.testserver.server.model.MultiKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.RawAesKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.RawEcdhKeyAgreementScheme;
import aws.cryptography.esdk.testserver.server.model.RawEcdhKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.RawRsaKeyringConfig;
import aws.cryptography.esdk.testserver.server.model.RequiredEncryptionContextCmmConfig;
import aws.cryptography.esdk.testserver.server.registry.EsdkClient;
Expand All @@ -39,6 +44,7 @@
import software.amazon.cryptography.materialproviders.MaterialProviders;
import software.amazon.cryptography.materialproviders.model.AesWrappingAlg;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsDiscoveryKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsEcdhKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsHierarchicalKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsMrkKeyringInput;
Expand All @@ -49,10 +55,19 @@
import software.amazon.cryptography.materialproviders.model.CreateDefaultCryptographicMaterialsManagerInput;
import software.amazon.cryptography.materialproviders.model.CreateMultiKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateRawAesKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateRawEcdhKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateRawRsaKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateRequiredEncryptionContextCMMInput;
import software.amazon.cryptography.materialproviders.model.EphemeralPrivateKeyToStaticPublicKeyInput;
import software.amazon.cryptography.materialproviders.model.KmsEcdhStaticConfigurations;
import software.amazon.cryptography.materialproviders.model.KmsPrivateKeyToStaticPublicKeyInput;
import software.amazon.cryptography.materialproviders.model.KmsPublicKeyDiscoveryInput;
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;
import software.amazon.cryptography.materialproviders.model.PaddingScheme;
import software.amazon.cryptography.materialproviders.model.PublicKeyDiscoveryInput;
import software.amazon.cryptography.materialproviders.model.RawEcdhStaticConfigurations;
import software.amazon.cryptography.materialproviders.model.RawPrivateKeyToStaticPublicKeyInput;
import software.amazon.cryptography.primitives.model.ECDHCurveSpec;

/**
* Translates a validated {@link ESDKClientConfig} into a {@link RealEsdkClient}
Expand Down Expand Up @@ -80,7 +95,10 @@
* network call —
* the KMS client is built eagerly but is not invoked — so {@code CreateClient}
* stays offline; the real AWS KMS calls happen only on {@code Encrypt}/{@code
* Decrypt} (Requirements 14.1, 14.3, 14.4, 14.14). The single exception is the
* Decrypt} (Requirements 14.1, 14.3, 14.4, 14.14). The Raw ECDH and AWS KMS ECDH
* keyrings are wired as well. The exceptions are the AWS KMS ECDH keyring when its
* static configuration omits the sender's public key (fetched via
* {@code kms:GetPublicKey}), and the
* {@code AwsKmsRsa} keyring when the modeled config omits the RSA public key: in
* that case the factory fetches it once via {@code kms:GetPublicKey} at
* construction (a network call the design explicitly permits at
Expand Down Expand Up @@ -154,6 +172,8 @@ private IKeyring buildKeyring(Keyring keyring) {
AwsKmsDiscoveryKeyringConfig awsKmsDiscovery = keyring.getAwsKmsDiscovery();
AwsKmsMrkDiscoveryKeyringConfig awsKmsMrkDiscovery = keyring.getAwsKmsMrkDiscovery();
AwsKmsHierarchicalKeyringConfig awsKmsHierarchical = keyring.getAwsKmsHierarchical();
RawEcdhKeyringConfig rawEcdh = keyring.getRawEcdh();
AwsKmsEcdhKeyringConfig awsKmsEcdh = keyring.getAwsKmsEcdh();

if (rawAes != null) {
return materialProviders.CreateRawAesKeyring(
Expand Down Expand Up @@ -253,9 +273,85 @@ private IKeyring buildKeyring(Keyring keyring) {
if (awsKmsHierarchical != null) {
return buildAwsKmsHierarchicalKeyring(awsKmsHierarchical);
}
if (rawEcdh != null) {
return buildRawEcdhKeyring(rawEcdh);
}
if (awsKmsEcdh != null) {
return buildAwsKmsEcdhKeyring(awsKmsEcdh);
}
throw new IllegalArgumentException("Keyring had no variant member set");
}

/** Build the Raw ECDH keyring from its key agreement scheme and curve. */
private IKeyring buildRawEcdhKeyring(RawEcdhKeyringConfig config) {
RawEcdhKeyAgreementScheme scheme = config.getKeyAgreementScheme();
RawEcdhStaticConfigurations.Builder agreement = RawEcdhStaticConfigurations.builder();
if (scheme.getRawPrivateKeyToStaticPublicKey() != null) {
agreement.RawPrivateKeyToStaticPublicKey(RawPrivateKeyToStaticPublicKeyInput.builder()
.senderStaticPrivateKey(scheme.getRawPrivateKeyToStaticPublicKey().getSenderStaticPrivateKey())
.recipientPublicKey(scheme.getRawPrivateKeyToStaticPublicKey().getRecipientPublicKey())
.build());
} else if (scheme.getEphemeralPrivateKeyToStaticPublicKey() != null) {
agreement.EphemeralPrivateKeyToStaticPublicKey(EphemeralPrivateKeyToStaticPublicKeyInput.builder()
.recipientPublicKey(scheme.getEphemeralPrivateKeyToStaticPublicKey().getRecipientPublicKey())
.build());
} else {
agreement.PublicKeyDiscovery(PublicKeyDiscoveryInput.builder()
.recipientStaticPrivateKey(scheme.getPublicKeyDiscovery().getRecipientStaticPrivateKey())
.build());
}
return materialProviders.CreateRawEcdhKeyring(CreateRawEcdhKeyringInput.builder()
.KeyAgreementScheme(agreement.build())
.curveSpec(toCurveSpec(config.getCurveSpec()))
.build());
}

/**
* Build the AWS KMS ECDH keyring. The KMS client is in the region of the
* sender's (static) or recipient's (discovery) KMS key; construction makes no
* network call unless the static configuration omits the sender's public key,
* which the MPL then fetches with {@code kms:GetPublicKey}.
*/
private IKeyring buildAwsKmsEcdhKeyring(AwsKmsEcdhKeyringConfig config) {
KmsEcdhKeyAgreementScheme scheme = config.getKeyAgreementScheme();
KmsEcdhStaticConfigurations.Builder agreement = KmsEcdhStaticConfigurations.builder();
String kmsKeyId;
if (scheme.getKmsPrivateKeyToStaticPublicKey() != null) {
var staticConfig = scheme.getKmsPrivateKeyToStaticPublicKey();
kmsKeyId = staticConfig.getSenderKmsIdentifier();
KmsPrivateKeyToStaticPublicKeyInput.Builder input = KmsPrivateKeyToStaticPublicKeyInput.builder()
.senderKmsIdentifier(kmsKeyId)
.recipientPublicKey(staticConfig.getRecipientPublicKey());
if (staticConfig.getSenderPublicKey() != null) {
input.senderPublicKey(staticConfig.getSenderPublicKey());
}
agreement.KmsPrivateKeyToStaticPublicKey(input.build());
} else {
kmsKeyId = scheme.getKmsPublicKeyDiscovery().getRecipientKmsIdentifier();
agreement.KmsPublicKeyDiscovery(KmsPublicKeyDiscoveryInput.builder()
.recipientKmsIdentifier(kmsKeyId)
.build());
}
CreateAwsKmsEcdhKeyringInput.Builder builder = CreateAwsKmsEcdhKeyringInput.builder()
.KeyAgreementScheme(agreement.build())
.curveSpec(toCurveSpec(config.getCurveSpec()))
.kmsClient(kmsClientForKey(kmsKeyId));
if (config.hasGrantTokens() && !config.getGrantTokens().isEmpty()) {
builder.grantTokens(new ArrayList<>(config.getGrantTokens()));
}
return materialProviders.CreateAwsKmsEcdhKeyring(builder.build());
}

private static ECDHCurveSpec toCurveSpec(EcdhCurveSpec curve) {
return switch (curve.getValue()) {
case "ECC_NIST_P256" -> ECDHCurveSpec.ECC_NIST_P256;
case "ECC_NIST_P384" -> ECDHCurveSpec.ECC_NIST_P384;
case "ECC_NIST_P521" -> ECDHCurveSpec.ECC_NIST_P521;
case "SM2" -> ECDHCurveSpec.SM2;
default -> throw new IllegalArgumentException("Unknown ECDH curve: " + curve.getValue());
};
}

/**
* Build the AWS KMS RSA keyring (Requirement 14.3, 14.4). The keyring needs
* the RSA <em>public key</em> bytes (for encrypt), the KMS key id/ARN and a KMS
Expand Down
Loading