| 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 © 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 |
|
| 67 |
1.1 |
|
| 74 |
1.1 |
|
| 78 |
1.1 |
|
| 79 |
1.1 |
|
| 81 |
1.1 |