SimpleFernetKeyRotator.java

1
/**
2
   Copyright 2018 Carlos Macasaet
3
4
   Licensed under the Apache License, Version 2.0 (the "License");
5
   you may not use this file except in compliance with the License.
6
   You may obtain a copy of the License at
7
8
       http://www.apache.org/licenses/LICENSE-2.0
9
10
   Unless required by applicable law or agreed to in writing, software
11
   distributed under the License is distributed on an "AS IS" BASIS,
12
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
   See the License for the specific language governing permissions and
14
   limitations under the License.
15
 */
16
package com.macasaet.fernet.aws.secretsmanager.rotation;
17
18
import static com.macasaet.fernet.aws.secretsmanager.rotation.Stage.PENDING;
19
20
import java.nio.ByteBuffer;
21
import java.security.SecureRandom;
22
23
import com.amazonaws.services.kms.AWSKMS;
24
import com.amazonaws.services.kms.AWSKMSClientBuilder;
25
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
26
import com.macasaet.fernet.Key;
27
28
/**
29
 * <p>AWS Lambda that rotates Fernet keys. To access a key, retrieve AWSCURRENT, AWSPENDING, or AWSPREVIOUS. To validate
30
 * and decrypt a token, it will be necessary to retrieve AWSCURRENT and AWSPREVIOUS as there is no way to know which one
31
 * was used to generate the token.</p>
32
 * 
33
 * <p>Grant AWS Secrets Manager permission to execute the Lambda using this:</p>
34
 * <pre>aws lambda add-permission --function-name arn:aws:lambda:{region}:{accountId}:function:{functionName} --principal secretsmanager.amazonaws.com --action lambda:InvokeFunction --statement-id SecretsManagerAccess</pre>
35
 *
36
 * <p>Copyright &copy; 2018 Carlos Macasaet.</p>
37
 * @author Carlos Macasaet
38
 */
39
@SuppressWarnings("PMD.LawOfDemeter")
40
public class SimpleFernetKeyRotator extends AbstractFernetKeyRotator {
41
42
    private static final int fernetKeySize = 32;
43
44
    protected SimpleFernetKeyRotator(final SecretsManager secretsManager, final AWSKMS kms, final SecureRandom random) {
45
        super(secretsManager, kms, random);
46
    }
47
48
    protected SimpleFernetKeyRotator(final SecureRandom random) {
49
        this(new SecretsManager(AWSSecretsManagerClientBuilder.standard()
50
                .withRequestHandlers(new MemoryOverwritingRequestHandler(random)).build()),
51
                AWSKMSClientBuilder.defaultClient(), random);
52
    }
53
54
    public SimpleFernetKeyRotator() {
55
        this(new SecureRandom());
56
    }
57
58
    protected void createSecret(final String secretId, final String clientRequestToken) {
59
        final Key key = Key.generateKey(getRandom());
60 1 1. createSecret : removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::putSecretValue → KILLED
        getSecretsManager().putSecretValue(secretId, clientRequestToken, key, PENDING);
61
        getLogger().info("createSecret: Successfully put secret for ARN {} and version {}.", secretId, clientRequestToken);
62
    }
63
64
    protected void testSecret(final String secretId, final String clientRequestToken) {
65
        final ByteBuffer buffer = getSecretsManager().getSecretVersion(secretId, clientRequestToken);
66
        try {
67 1 1. testSecret : negated conditional → KILLED
            if (buffer.remaining() != fernetKeySize) {
68
                throw new IllegalStateException("Fernet key must be exactly " + fernetKeySize + " bytes");
69
            }
70
            final byte[] signingKey = new byte[16];
71
            buffer.get(signingKey);
72
            final byte[] encryptionKey = new byte[16];
73
            buffer.get(encryptionKey);
74 1 1. testSecret : negated conditional → KILLED
            if (buffer.hasRemaining()) {
75
                throw new IllegalStateException("Encountered extra bytes.");
76
            }
77
            new Key(signingKey, encryptionKey);
78 1 1. testSecret : removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SimpleFernetKeyRotator::wipe → SURVIVED
            wipe(signingKey);
79 1 1. testSecret : removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SimpleFernetKeyRotator::wipe → SURVIVED
            wipe(encryptionKey);
80
        } finally {
81 1 1. testSecret : removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SimpleFernetKeyRotator::wipe → KILLED
            wipe(buffer);
82
        }
83
        getLogger().info("testSecret: Successfully validated Fernet Key for ARN {} and version {}.", secretId, clientRequestToken);
84
    }
85
86
}

Mutations

60

1.1
Location : createSecret
Killed by : com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest.verifyHandleRequestCreatesKey(com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest)
removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SecretsManager::putSecretValue → KILLED

67

1.1
Location : testSecret
Killed by : com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest.verifyTestClearsIntermediateSecret(com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest)
negated conditional → KILLED

74

1.1
Location : testSecret
Killed by : com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest.verifyTestClearsIntermediateSecret(com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest)
negated conditional → KILLED

78

1.1
Location : testSecret
Killed by : none
removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SimpleFernetKeyRotator::wipe → SURVIVED

79

1.1
Location : testSecret
Killed by : none
removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SimpleFernetKeyRotator::wipe → SURVIVED

81

1.1
Location : testSecret
Killed by : com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest.verifyTestClearsIntermediateSecret(com.macasaet.fernet.aws.secretsmanager.rotation.SimpleFernetKeyRotatorTest)
removed call to com/macasaet/fernet/aws/secretsmanager/rotation/SimpleFernetKeyRotator::wipe → KILLED

Active mutators

Tests examined


Report generated by PIT 1.4.10