All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager Maven / Gradle / Ivy

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.encryptionsdk;

import static com.amazonaws.encryptionsdk.internal.Utils.assertNonNull;

import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
import com.amazonaws.encryptionsdk.internal.Constants;
import com.amazonaws.encryptionsdk.internal.Utils;
import com.amazonaws.encryptionsdk.internal.TrailingSignatureAlgorithm;
import com.amazonaws.encryptionsdk.model.DecryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterialsRequest;
import com.amazonaws.encryptionsdk.model.KeyBlob;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;

/**
 * The default implementation of {@link CryptoMaterialsManager}, used implicitly when passing a
 * {@link MasterKeyProvider} to methods in {@link AwsCrypto}.
 *
 * This default implementation delegates to a specific {@link MasterKeyProvider} specified at construction time. It also
 * handles generating trailing signature keys when needed, placing them in the encryption context (and extracting them
 * at decrypt time).
 */
public class DefaultCryptoMaterialsManager implements CryptoMaterialsManager {
    private final MasterKeyProvider mkp;

    private final CryptoAlgorithm DEFAULT_CRYPTO_ALGORITHM = CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384;

    /**
     * @param mkp The master key provider to delegate to
     */
    public DefaultCryptoMaterialsManager(MasterKeyProvider mkp) {
        Utils.assertNonNull(mkp, "mkp");
        this.mkp = mkp;
    }

    @Override public EncryptionMaterials getMaterialsForEncrypt(EncryptionMaterialsRequest request) {
        Map context = request.getContext();

        CryptoAlgorithm algo = request.getRequestedAlgorithm();
        CommitmentPolicy commitmentPolicy = request.getCommitmentPolicy();
        // Set default according to commitment policy
        if (algo == null && commitmentPolicy == CommitmentPolicy.ForbidEncryptAllowDecrypt) {
            algo = CryptoAlgorithm.ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384;
        } else if (algo == null) {
            algo = CryptoAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384;
        }

        KeyPair trailingKeys = null;
        if (algo.getTrailingSignatureLength() > 0) {
            try {
                trailingKeys = generateTrailingSigKeyPair(algo);
                if (context.containsKey(Constants.EC_PUBLIC_KEY_FIELD)) {
                    throw new IllegalArgumentException("EncryptionContext contains reserved field "
                                                               + Constants.EC_PUBLIC_KEY_FIELD);
                }
                // make mutable
                context = new HashMap<>(context);
                context.put(Constants.EC_PUBLIC_KEY_FIELD, serializeTrailingKeyForEc(algo, trailingKeys));
            } catch (final GeneralSecurityException ex) {
                throw new AwsCryptoException(ex);
            }
        }

        final MasterKeyRequest.Builder mkRequestBuilder = MasterKeyRequest.newBuilder();
        mkRequestBuilder.setEncryptionContext(context);

        mkRequestBuilder.setStreaming(request.getPlaintextSize() == -1);
        if (request.getPlaintext() != null) {
            mkRequestBuilder.setPlaintext(request.getPlaintext());
        } else {
            mkRequestBuilder.setSize(request.getPlaintextSize());
        }

        @SuppressWarnings("unchecked")
        final List mks
                = (List)assertNonNull(mkp, "provider")
                        .getMasterKeysForEncryption(mkRequestBuilder.build());

        if (mks.isEmpty()) {
            throw new IllegalArgumentException("No master keys provided");
        }

        DataKey dataKey = mks.get(0).generateDataKey(algo, context);

        List keyBlobs = new ArrayList<>(mks.size());
        keyBlobs.add(new KeyBlob(dataKey));

        for (int i = 1; i < mks.size(); i++) {
            //noinspection unchecked
            keyBlobs.add(new KeyBlob(mks.get(i).encryptDataKey(algo, context, dataKey)));
        }

        //noinspection unchecked
        return EncryptionMaterials.newBuilder()
                                  .setAlgorithm(algo)
                                  .setCleartextDataKey(dataKey.getKey())
                                  .setEncryptedDataKeys(keyBlobs)
                                  .setEncryptionContext(context)
                                  .setTrailingSignatureKey(trailingKeys == null ? null : trailingKeys.getPrivate())
                                  .setMasterKeys(mks)
                                  .build();
    }

    @Override public DecryptionMaterials decryptMaterials(DecryptionMaterialsRequest request) {
        DataKey dataKey = mkp.decryptDataKey(
                request.getAlgorithm(),
                request.getEncryptedDataKeys(),
                request.getEncryptionContext()
        );

        if (dataKey == null) {
            throw new CannotUnwrapDataKeyException("Could not decrypt any data keys");
        }

        PublicKey pubKey = null;
        if (request.getAlgorithm().getTrailingSignatureLength() > 0) {
            try {
                String serializedPubKey = request.getEncryptionContext().get(Constants.EC_PUBLIC_KEY_FIELD);

                if (serializedPubKey == null) {
                    throw new AwsCryptoException("Missing trailing signature public key");
                }

                pubKey = deserializeTrailingKeyFromEc(request.getAlgorithm(), serializedPubKey);
            } catch (final IllegalStateException ex) {
                throw new AwsCryptoException(ex);
            }
        }

        return DecryptionMaterials.newBuilder()
                                  .setDataKey(dataKey)
                                  .setTrailingSignatureKey(pubKey)
                                  .build();
    }

    private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey)  {
        return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).deserializePublicKey(pubKey);
    }

    private static String serializeTrailingKeyForEc(CryptoAlgorithm algo, KeyPair trailingKeys) {
        return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).serializePublicKey(trailingKeys.getPublic());
    }

    private static KeyPair generateTrailingSigKeyPair(CryptoAlgorithm algo) throws GeneralSecurityException {
        return TrailingSignatureAlgorithm.forCryptoAlgorithm(algo).generateKey();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy