mirror of
https://github.com/google/nomulus.git
synced 2025-05-16 17:37:13 +02:00
Add Cloud KMS based secret storage
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=147791972
This commit is contained in:
parent
ab6e7b177a
commit
be30ecdf66
24 changed files with 2255 additions and 0 deletions
|
@ -867,6 +867,24 @@ public final class RegistryConfig {
|
||||||
return config.registryPolicy.greetingServerId;
|
return config.registryPolicy.greetingServerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name to use for the Cloud KMS KeyRing containing encryption keys for Nomulus secrets.
|
||||||
|
*
|
||||||
|
* @see <a
|
||||||
|
* href="https://cloud.google.com/kms/docs/reference/rest/v1beta1/projects.locations.keyRings#KeyRing">projects.locations.keyRings</a>
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Config("cloudKmsKeyRing")
|
||||||
|
public static String provideCloudKmsKeyRing(RegistryConfigSettings config) {
|
||||||
|
return config.kms.keyringName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Config("cloudKmsProjectId")
|
||||||
|
public static String provideCloudKmsProjectId(RegistryConfigSettings config) {
|
||||||
|
return config.kms.projectId;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Config("customLogicFactoryClass")
|
@Config("customLogicFactoryClass")
|
||||||
public static String provideCustomLogicFactoryClass(RegistryConfigSettings config) {
|
public static String provideCustomLogicFactoryClass(RegistryConfigSettings config) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ public class RegistryConfigSettings {
|
||||||
public Misc misc;
|
public Misc misc;
|
||||||
public Rdap rdap;
|
public Rdap rdap;
|
||||||
public Braintree braintree;
|
public Braintree braintree;
|
||||||
|
public Kms kms;
|
||||||
|
|
||||||
/** Configuration options that apply to the entire App Engine project. */
|
/** Configuration options that apply to the entire App Engine project. */
|
||||||
public static class AppEngine {
|
public static class AppEngine {
|
||||||
|
@ -78,6 +79,12 @@ public class RegistryConfigSettings {
|
||||||
public int baseOfyRetryMillis;
|
public int baseOfyRetryMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Configuration for Cloud KMS. */
|
||||||
|
public static class Kms {
|
||||||
|
public String keyringName;
|
||||||
|
public String projectId;
|
||||||
|
}
|
||||||
|
|
||||||
/** Configuration for caching. */
|
/** Configuration for caching. */
|
||||||
public static class Caching {
|
public static class Caching {
|
||||||
public int singletonCacheRefreshSeconds;
|
public int singletonCacheRefreshSeconds;
|
||||||
|
|
|
@ -182,3 +182,11 @@ braintree:
|
||||||
# currency). For example, one entry might be:
|
# currency). For example, one entry might be:
|
||||||
# USD: accountIdUsingUSD
|
# USD: accountIdUsingUSD
|
||||||
merchantAccountIdsMap: {}
|
merchantAccountIdsMap: {}
|
||||||
|
|
||||||
|
kms:
|
||||||
|
# GCP project containing the KMS keyring. Should only be used for KMS in
|
||||||
|
# order to keep a simple locked down IAM configuration.
|
||||||
|
projectId: registry-kms-project-id
|
||||||
|
# The name to use for the Cloud KMS KeyRing which will store encryption keys
|
||||||
|
# for Nomulus secrets.
|
||||||
|
keyringName: nomulus
|
||||||
|
|
29
java/google/registry/keyring/api/KeyringException.java
Normal file
29
java/google/registry/keyring/api/KeyringException.java
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.api;
|
||||||
|
|
||||||
|
/** Base class for all {@link Keyring} specific unchecked exceptions. */
|
||||||
|
public class KeyringException extends RuntimeException {
|
||||||
|
|
||||||
|
/** @see RuntimeException#RuntimeException(String) */
|
||||||
|
public KeyringException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see RuntimeException#RuntimeException(String, Throwable) */
|
||||||
|
public KeyringException(String msg, Throwable cause) {
|
||||||
|
super(msg, cause);
|
||||||
|
}
|
||||||
|
}
|
25
java/google/registry/keyring/kms/BUILD
Normal file
25
java/google/registry/keyring/kms/BUILD
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = "kms",
|
||||||
|
srcs = glob(["*.java"]),
|
||||||
|
deps = [
|
||||||
|
"//java/google/registry/config",
|
||||||
|
"//java/google/registry/keyring/api",
|
||||||
|
"//java/google/registry/model",
|
||||||
|
"//java/google/registry/util",
|
||||||
|
"//third_party/java/objectify:objectify-v4_1",
|
||||||
|
"@com_google_api_client",
|
||||||
|
"@com_google_apis_google_api_services_cloudkms",
|
||||||
|
"@com_google_code_findbugs_jsr305",
|
||||||
|
"@com_google_dagger",
|
||||||
|
"@com_google_guava",
|
||||||
|
"@com_google_http_client",
|
||||||
|
"@org_bouncycastle_bcpg_jdk15on",
|
||||||
|
"@org_bouncycastle_bcpkix_jdk15on",
|
||||||
|
],
|
||||||
|
)
|
243
java/google/registry/keyring/kms/KmsKeyring.java
Normal file
243
java/google/registry/keyring/kms/KmsKeyring.java
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static google.registry.keyring.api.PgpHelper.KeyRequirement.ENCRYPT;
|
||||||
|
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.CloudKMS;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.DecryptRequest;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
import google.registry.keyring.api.Keyring;
|
||||||
|
import google.registry.keyring.api.KeyringException;
|
||||||
|
import google.registry.keyring.api.PgpHelper;
|
||||||
|
import google.registry.model.server.KmsSecret;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
|
import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Keyring} implementation which stores encrypted secrets in Datastore and decrypts them
|
||||||
|
* using encryption keys stored in Cloud KMS.
|
||||||
|
*
|
||||||
|
* @see <a href="https://cloud.google.com/kms/docs/">Google Cloud Key Management Service
|
||||||
|
* Documentation</a>
|
||||||
|
*/
|
||||||
|
public class KmsKeyring implements Keyring {
|
||||||
|
|
||||||
|
private static final String KMS_KEYRING_NAME_FORMAT = "projects/%s/locations/global/keyRings/%s";
|
||||||
|
private static final String KMS_CRYPTO_KEY_NAME_FORMAT =
|
||||||
|
"projects/%s/locations/global/keyRings/%s/cryptoKeys/%s";
|
||||||
|
private static final String KMS_CRYPTO_KEY_VERSION_NAME_FORMAT =
|
||||||
|
"projects/%s/locations/global/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions";
|
||||||
|
|
||||||
|
static final String BRAINTREE_PRIVATE_KEY_NAME = "braintree-private-key";
|
||||||
|
static final String BRDA_RECEIVER_PUBLIC_NAME = "brda-receiver-public";
|
||||||
|
static final String BRDA_SIGNING_PRIVATE_NAME = "brda-signing-private";
|
||||||
|
static final String BRDA_SIGNING_PUBLIC_NAME = "brda-signing-public";
|
||||||
|
static final String ICANN_REPORTING_PASSWORD_NAME = "icann-reporting-password";
|
||||||
|
static final String JSON_CREDENTIAL_NAME = "json-credential";
|
||||||
|
static final String MARKSDB_DNL_LOGIN_NAME = "marksdb-dnl-login";
|
||||||
|
static final String MARKSDB_LORDN_PASSWORD_NAME = "marksdb-lordn-password";
|
||||||
|
static final String MARKSDB_SMDRL_LOGIN_NAME = "marksdb-smdrl-login";
|
||||||
|
static final String RDE_RECEIVER_PUBLIC_NAME = "rde-receiver-public";
|
||||||
|
static final String RDE_SIGNING_PRIVATE_NAME = "rde-signing-private";
|
||||||
|
static final String RDE_SIGNING_PUBLIC_NAME = "rde-signing-public";
|
||||||
|
static final String RDE_SSH_CLIENT_PRIVATE_NAME = "rde-ssh-client-private";
|
||||||
|
static final String RDE_SSH_CLIENT_PUBLIC_NAME = "rde-ssh-client-public";
|
||||||
|
static final String RDE_STAGING_PRIVATE_NAME = "rde-staging-private";
|
||||||
|
static final String RDE_STAGING_PUBLIC_NAME = "rde-staging-public";
|
||||||
|
|
||||||
|
private final CloudKMS kms;
|
||||||
|
private final String kmsKeyRingName;
|
||||||
|
private final String projectId;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KmsKeyring(
|
||||||
|
@Config("cloudKmsProjectId") String projectId,
|
||||||
|
@Config("cloudKmsKeyRing") String kmsKeyringName,
|
||||||
|
CloudKMS kms) {
|
||||||
|
this.projectId = projectId;
|
||||||
|
this.kmsKeyRingName = kmsKeyringName;
|
||||||
|
this.kms = kms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPKeyPair getRdeSigningKey() {
|
||||||
|
return getKeyPair(RDE_SIGNING_PUBLIC_NAME, RDE_SIGNING_PRIVATE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPPublicKey getRdeStagingEncryptionKey() {
|
||||||
|
return getPublicKeyForEncrypting(RDE_STAGING_PUBLIC_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPPrivateKey getRdeStagingDecryptionKey() {
|
||||||
|
return getPrivateKey(RDE_STAGING_PRIVATE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPPublicKey getRdeReceiverKey() {
|
||||||
|
return getPublicKeyForEncrypting(RDE_RECEIVER_PUBLIC_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPKeyPair getBrdaSigningKey() {
|
||||||
|
return getKeyPair(BRDA_SIGNING_PUBLIC_NAME, BRDA_SIGNING_PRIVATE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPPublicKey getBrdaReceiverKey() {
|
||||||
|
return getPublicKeyForEncrypting(BRDA_RECEIVER_PUBLIC_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRdeSshClientPublicKey() {
|
||||||
|
return new String(getDecryptedData((RDE_SSH_CLIENT_PUBLIC_NAME)), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRdeSshClientPrivateKey() {
|
||||||
|
return new String(getDecryptedData(RDE_SSH_CLIENT_PRIVATE_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIcannReportingPassword() {
|
||||||
|
return new String(getDecryptedData(ICANN_REPORTING_PASSWORD_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMarksdbDnlLogin() {
|
||||||
|
return new String(getDecryptedData(MARKSDB_DNL_LOGIN_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMarksdbLordnPassword() {
|
||||||
|
return new String(getDecryptedData(MARKSDB_LORDN_PASSWORD_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMarksdbSmdrlLogin() {
|
||||||
|
return new String(getDecryptedData(MARKSDB_SMDRL_LOGIN_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getJsonCredential() {
|
||||||
|
return new String(getDecryptedData(JSON_CREDENTIAL_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBraintreePrivateKey() {
|
||||||
|
return new String(getDecryptedData(BRAINTREE_PRIVATE_KEY_NAME), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** No persistent resources are maintained for this Keyring implementation. */
|
||||||
|
@Override
|
||||||
|
public void close() {}
|
||||||
|
|
||||||
|
private PGPKeyPair getKeyPair(String publicKeyName, String privateKeyName) {
|
||||||
|
try {
|
||||||
|
PGPPublicKey publicKey =
|
||||||
|
new BcPGPPublicKeyRing(getPgpInputStream(publicKeyName)).getPublicKey();
|
||||||
|
return new PGPKeyPair(publicKey, getPrivateKey(privateKeyName));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new KeyringException(
|
||||||
|
String.format(
|
||||||
|
"Could not parse public key %s and private key %s", publicKeyName, privateKeyName),
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PGPPublicKey getPublicKeyForEncrypting(String publicKeyName) {
|
||||||
|
try {
|
||||||
|
return PgpHelper.lookupPublicSubkey(
|
||||||
|
new BcPGPPublicKeyRing(getPgpInputStream(publicKeyName)), ENCRYPT)
|
||||||
|
.get();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new KeyringException(String.format("Could not parse public key %s", publicKeyName), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PGPPrivateKey getPrivateKey(String privateKeyName) {
|
||||||
|
try {
|
||||||
|
PGPSecretKeyRing privateKeyRing = new BcPGPSecretKeyRing(getPgpInputStream(privateKeyName));
|
||||||
|
// There shouldn't be a passphrase on the key
|
||||||
|
return privateKeyRing
|
||||||
|
.getSecretKey()
|
||||||
|
.extractPrivateKey(
|
||||||
|
new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider())
|
||||||
|
.build(new char[0]));
|
||||||
|
} catch (IOException | PGPException e) {
|
||||||
|
throw new KeyringException(
|
||||||
|
String.format("Could not parse private key %s", privateKeyName), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getPgpInputStream(String privateKeyName) throws IOException {
|
||||||
|
return PGPUtil.getDecoderStream(new ByteArrayInputStream(getDecryptedData(privateKeyName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getDecryptedData(String keyName) {
|
||||||
|
KmsSecret secret =
|
||||||
|
ofy().load().key(Key.create(getCrossTldKey(), KmsSecret.class, keyName)).now();
|
||||||
|
checkState(secret != null, "Requested secret '%s' does not exist.", keyName);
|
||||||
|
String encryptedData = ofy().load().key(secret.getLatestRevision()).now().getEncryptedValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return kms.projects()
|
||||||
|
.locations()
|
||||||
|
.keyRings()
|
||||||
|
.cryptoKeys()
|
||||||
|
.decrypt(
|
||||||
|
getCryptoKeyName(projectId, kmsKeyRingName, secret.getName()),
|
||||||
|
new DecryptRequest().setCiphertext(encryptedData))
|
||||||
|
.execute()
|
||||||
|
.decodePlaintext();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new KeyringException(
|
||||||
|
String.format("CloudKMS decrypt operation failed for secret %s", keyName), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getKeyRingName(String projectId, String kmsKeyRingName) {
|
||||||
|
return String.format(KMS_KEYRING_NAME_FORMAT, projectId, kmsKeyRingName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getCryptoKeyName(String projectId, String kmsKeyRingName, String cryptoKeyName) {
|
||||||
|
return String.format(KMS_CRYPTO_KEY_NAME_FORMAT, projectId, kmsKeyRingName, cryptoKeyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getCryptoKeyVersionName(
|
||||||
|
String projectId, String kmsKeyRingName, String cryptoKeyName) {
|
||||||
|
return String.format(
|
||||||
|
KMS_CRYPTO_KEY_VERSION_NAME_FORMAT, projectId, kmsKeyRingName, cryptoKeyName);
|
||||||
|
}
|
||||||
|
}
|
42
java/google/registry/keyring/kms/KmsModule.java
Normal file
42
java/google/registry/keyring/kms/KmsModule.java
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import com.google.api.client.http.HttpRequestInitializer;
|
||||||
|
import com.google.api.client.http.HttpTransport;
|
||||||
|
import com.google.api.client.json.JsonFactory;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.CloudKMS;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.CloudKMSScopes;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Dagger module for Cloud KMS connection objects. */
|
||||||
|
@Module
|
||||||
|
public final class KmsModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
static CloudKMS provideKms(
|
||||||
|
HttpTransport transport,
|
||||||
|
JsonFactory jsonFactory,
|
||||||
|
Function<Set<String>, ? extends HttpRequestInitializer> credential,
|
||||||
|
@Config("cloudKmsProjectId") String projectId) {
|
||||||
|
return new CloudKMS.Builder(transport, jsonFactory, credential.apply(CloudKMSScopes.all()))
|
||||||
|
.setApplicationName(projectId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
281
java/google/registry/keyring/kms/KmsUpdater.java
Normal file
281
java/google/registry/keyring/kms/KmsUpdater.java
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.BRAINTREE_PRIVATE_KEY_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.BRDA_RECEIVER_PUBLIC_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.BRDA_SIGNING_PRIVATE_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.BRDA_SIGNING_PUBLIC_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.ICANN_REPORTING_PASSWORD_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.JSON_CREDENTIAL_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.MARKSDB_DNL_LOGIN_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.MARKSDB_LORDN_PASSWORD_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.MARKSDB_SMDRL_LOGIN_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_RECEIVER_PUBLIC_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_SIGNING_PRIVATE_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_SIGNING_PUBLIC_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_SSH_CLIENT_PRIVATE_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_SSH_CLIENT_PUBLIC_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_STAGING_PRIVATE_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.RDE_STAGING_PUBLIC_NAME;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.getCryptoKeyName;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.getCryptoKeyVersionName;
|
||||||
|
import static google.registry.keyring.kms.KmsKeyring.getKeyRingName;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.CloudKMS;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.CryptoKey;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.CryptoKeyVersion;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.EncryptRequest;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.EncryptResponse;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.KeyRing;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.googlecode.objectify.VoidWork;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
import google.registry.model.server.KmsSecret;
|
||||||
|
import google.registry.model.server.KmsSecretRevision;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link KmsUpdater} accumulates updates to a {@link KmsKeyring} and persists them to KMS and
|
||||||
|
* Datastore when closed.
|
||||||
|
*/
|
||||||
|
public final class KmsUpdater {
|
||||||
|
|
||||||
|
private static final int RESOURCE_NOT_FOUND = 404;
|
||||||
|
|
||||||
|
private final String projectId;
|
||||||
|
private final String kmsKeyRingName;
|
||||||
|
private final CloudKMS kms;
|
||||||
|
|
||||||
|
private final HashMap<String, byte[]> secretValues;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public KmsUpdater(
|
||||||
|
@Config("cloudKmsProjectId") String projectId,
|
||||||
|
@Config("cloudKmsKeyRing") String kmsKeyRingName,
|
||||||
|
CloudKMS kms) {
|
||||||
|
this.projectId = projectId;
|
||||||
|
this.kmsKeyRingName = kmsKeyRingName;
|
||||||
|
this.kms = kms;
|
||||||
|
|
||||||
|
// Use LinkedHashMap to preserve insertion order on update() to simplify testing and debugging
|
||||||
|
this.secretValues = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setRdeSigningKey(BcPGPSecretKeyRing secretKeyRing) throws IOException {
|
||||||
|
checkArgumentNotNull(secretKeyRing);
|
||||||
|
setSecret(RDE_SIGNING_PRIVATE_NAME, checkArgumentNotNull(secretKeyRing).getEncoded());
|
||||||
|
setSecret(RDE_SIGNING_PUBLIC_NAME, secretKeyRing.getPublicKey().getEncoded());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setRdeStagingKey(BcPGPSecretKeyRing secretKeyRing) throws IOException {
|
||||||
|
checkArgumentNotNull(secretKeyRing);
|
||||||
|
|
||||||
|
setSecret(RDE_STAGING_PRIVATE_NAME, secretKeyRing.getEncoded());
|
||||||
|
setSecret(RDE_STAGING_PUBLIC_NAME, secretKeyRing.getPublicKey().getEncoded());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setRdeReceiverPublicKey(PGPPublicKey rdeReceiverPublicKey) throws IOException {
|
||||||
|
setSecret(RDE_RECEIVER_PUBLIC_NAME, checkArgumentNotNull(rdeReceiverPublicKey).getEncoded());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setBrdaSigningKey(BcPGPSecretKeyRing secretKeyRing) throws IOException {
|
||||||
|
checkArgumentNotNull(secretKeyRing);
|
||||||
|
setSecret(BRDA_SIGNING_PRIVATE_NAME, secretKeyRing.getEncoded());
|
||||||
|
setSecret(BRDA_SIGNING_PUBLIC_NAME, secretKeyRing.getPublicKey().getEncoded());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setBrdaReceiverPublicKey(PGPPublicKey publicKey) throws IOException {
|
||||||
|
setSecret(BRDA_RECEIVER_PUBLIC_NAME, checkArgumentNotNull(publicKey).getEncoded());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setRdeSshClientPublicKey(String asciiPublicKey) {
|
||||||
|
setSecret(RDE_SSH_CLIENT_PUBLIC_NAME, checkArgumentNotNull(asciiPublicKey).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setRdeSshClientPrivateKey(String asciiPrivateKey) {
|
||||||
|
setSecret(RDE_SSH_CLIENT_PRIVATE_NAME, checkArgumentNotNull(asciiPrivateKey).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setIcannReportingPassword(String password) {
|
||||||
|
setSecret(ICANN_REPORTING_PASSWORD_NAME, checkArgumentNotNull(password).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setMarksdbDnlLogin(String login) {
|
||||||
|
setSecret(MARKSDB_DNL_LOGIN_NAME, checkArgumentNotNull(login).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setMarksdbLordnPassword(String password) {
|
||||||
|
setSecret(MARKSDB_LORDN_PASSWORD_NAME, checkArgumentNotNull(password).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setMarksdbSmdrlLogin(String login) {
|
||||||
|
setSecret(MARKSDB_SMDRL_LOGIN_NAME, checkArgumentNotNull(login).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setJsonCredential(String credential) {
|
||||||
|
setSecret(JSON_CREDENTIAL_NAME, checkArgumentNotNull(credential).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KmsUpdater setBraintreePrivateKey(String braintreePrivateKey) {
|
||||||
|
setSecret(
|
||||||
|
BRAINTREE_PRIVATE_KEY_NAME, checkArgumentNotNull(braintreePrivateKey).getBytes(UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates new encryption keys in KMS, encrypts the updated secrets with them, and persists the
|
||||||
|
* encrypted secrets to Datastore.
|
||||||
|
*
|
||||||
|
* <p>The operations in this method are organized so that existing {@link KmsSecretRevision}
|
||||||
|
* entities remain primary and decryptable if a failure occurs.
|
||||||
|
*/
|
||||||
|
public void update() throws IOException {
|
||||||
|
checkState(!secretValues.isEmpty(), "At least one Keyring value must be persisted");
|
||||||
|
|
||||||
|
persistEncryptedValues(encryptValues(secretValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts updated secrets using KMS. If the configured {@code KeyRing} or {@code CryptoKey}
|
||||||
|
* associated with a secret doesn't exist, they will first be created.
|
||||||
|
*
|
||||||
|
* @see google.registry.config.RegistryConfigSettings#kms
|
||||||
|
*/
|
||||||
|
private ImmutableMap<String, EncryptResponse> encryptValues(Map<String, byte[]> keyValues)
|
||||||
|
throws IOException {
|
||||||
|
String fullKeyRingName = getKeyRingName(projectId, kmsKeyRingName);
|
||||||
|
try {
|
||||||
|
kms.projects().locations().keyRings().get(fullKeyRingName).execute();
|
||||||
|
} catch (GoogleJsonResponseException jsonException) {
|
||||||
|
if (jsonException.getStatusCode() == RESOURCE_NOT_FOUND) {
|
||||||
|
// Create the KeyRing in the "global" namespace. Encryption keys will be accessible from all
|
||||||
|
// GCP regions.
|
||||||
|
kms.projects()
|
||||||
|
.locations()
|
||||||
|
.keyRings()
|
||||||
|
.create("global", new KeyRing().setName(fullKeyRingName))
|
||||||
|
.execute();
|
||||||
|
} else {
|
||||||
|
throw jsonException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableMap.Builder<String, EncryptResponse> encryptedValues = new ImmutableMap.Builder<>();
|
||||||
|
for (Map.Entry<String, byte[]> entry : keyValues.entrySet()) {
|
||||||
|
String keyName = entry.getKey();
|
||||||
|
String fullKeyName = getCryptoKeyName(projectId, kmsKeyRingName, keyName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
kms.projects().locations().keyRings().cryptoKeys().get(fullKeyName).execute();
|
||||||
|
} catch (GoogleJsonResponseException jsonException) {
|
||||||
|
if (jsonException.getStatusCode() == RESOURCE_NOT_FOUND) {
|
||||||
|
kms.projects()
|
||||||
|
.locations()
|
||||||
|
.keyRings()
|
||||||
|
.cryptoKeys()
|
||||||
|
.create(fullKeyName, new CryptoKey().setName(keyName).setPurpose("ENCRYPT_DECRYPT"))
|
||||||
|
.execute();
|
||||||
|
} else {
|
||||||
|
throw jsonException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoKeyVersion cryptoKeyVersion =
|
||||||
|
kms.projects()
|
||||||
|
.locations()
|
||||||
|
.keyRings()
|
||||||
|
.cryptoKeys()
|
||||||
|
.cryptoKeyVersions()
|
||||||
|
.create(
|
||||||
|
getCryptoKeyVersionName(projectId, kmsKeyRingName, keyName),
|
||||||
|
new CryptoKeyVersion())
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
encryptedValues.put(
|
||||||
|
keyName,
|
||||||
|
kms.projects()
|
||||||
|
.locations()
|
||||||
|
.keyRings()
|
||||||
|
.cryptoKeys()
|
||||||
|
.encrypt(
|
||||||
|
cryptoKeyVersion.getName(),
|
||||||
|
new EncryptRequest().encodePlaintext(entry.getValue()))
|
||||||
|
.execute());
|
||||||
|
}
|
||||||
|
return encryptedValues.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists encrypted secrets to Datastore as {@link KmsSecretRevision} entities and makes them
|
||||||
|
* primary. {@link KmsSecret} entities point to the latest {@link KmsSecretRevision}.
|
||||||
|
*
|
||||||
|
* <p>The changes are committed transactionally; if an error occurs, all existing {@link
|
||||||
|
* KmsSecretRevision} entities will remain primary.
|
||||||
|
*/
|
||||||
|
private static void persistEncryptedValues(
|
||||||
|
final ImmutableMap<String, EncryptResponse> encryptedValues) {
|
||||||
|
ofy()
|
||||||
|
.transact(
|
||||||
|
new VoidWork() {
|
||||||
|
@Override
|
||||||
|
public void vrun() {
|
||||||
|
for (Map.Entry<String, EncryptResponse> entry : encryptedValues.entrySet()) {
|
||||||
|
String secretName = entry.getKey();
|
||||||
|
EncryptResponse revisionData = entry.getValue();
|
||||||
|
|
||||||
|
KmsSecretRevision secretRevision =
|
||||||
|
new KmsSecretRevision.Builder()
|
||||||
|
.setEncryptedValue(revisionData.getCiphertext())
|
||||||
|
.setKmsCryptoKeyVersionName(revisionData.getName())
|
||||||
|
.setParent(secretName)
|
||||||
|
.build();
|
||||||
|
ofy()
|
||||||
|
.save()
|
||||||
|
.entities(secretRevision, KmsSecret.create(secretName, secretRevision));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSecret(String secretName, byte[] value) {
|
||||||
|
checkArgument(!secretValues.containsKey(secretName), "Attempted to set %s twice", secretName);
|
||||||
|
secretValues.put(secretName, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,6 +48,8 @@ import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.registry.label.PremiumList;
|
import google.registry.model.registry.label.PremiumList;
|
||||||
import google.registry.model.registry.label.ReservedList;
|
import google.registry.model.registry.label.ReservedList;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
import google.registry.model.server.KmsSecret;
|
||||||
|
import google.registry.model.server.KmsSecretRevision;
|
||||||
import google.registry.model.server.Lock;
|
import google.registry.model.server.Lock;
|
||||||
import google.registry.model.server.ServerSecret;
|
import google.registry.model.server.ServerSecret;
|
||||||
import google.registry.model.smd.SignedMarkRevocationList;
|
import google.registry.model.smd.SignedMarkRevocationList;
|
||||||
|
@ -90,6 +92,8 @@ public final class EntityClasses {
|
||||||
GaeUserIdConverter.class,
|
GaeUserIdConverter.class,
|
||||||
HistoryEntry.class,
|
HistoryEntry.class,
|
||||||
HostResource.class,
|
HostResource.class,
|
||||||
|
KmsSecret.class,
|
||||||
|
KmsSecretRevision.class,
|
||||||
Lock.class,
|
Lock.class,
|
||||||
LogsExportCursor.class,
|
LogsExportCursor.class,
|
||||||
LrpTokenEntity.class,
|
LrpTokenEntity.class,
|
||||||
|
|
57
java/google/registry/model/server/KmsSecret.java
Normal file
57
java/google/registry/model/server/KmsSecret.java
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.model.server;
|
||||||
|
|
||||||
|
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||||
|
import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION;
|
||||||
|
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
|
import com.googlecode.objectify.annotation.Cache;
|
||||||
|
import com.googlecode.objectify.annotation.Entity;
|
||||||
|
import com.googlecode.objectify.annotation.Id;
|
||||||
|
import com.googlecode.objectify.annotation.Parent;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
|
import google.registry.model.annotations.ReportedOn;
|
||||||
|
import google.registry.model.common.EntityGroupRoot;
|
||||||
|
|
||||||
|
/** Pointer to the latest {@link KmsSecretRevision}. */
|
||||||
|
@Entity
|
||||||
|
@ReportedOn
|
||||||
|
@Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION)
|
||||||
|
public class KmsSecret extends ImmutableObject {
|
||||||
|
|
||||||
|
/** The unique name of this {@link KmsSecret}. */
|
||||||
|
@Id String name;
|
||||||
|
|
||||||
|
@Parent Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||||
|
|
||||||
|
/** The pointer to the latest {@link KmsSecretRevision}. */
|
||||||
|
Key<KmsSecretRevision> latestRevision;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key<KmsSecretRevision> getLatestRevision() {
|
||||||
|
return latestRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KmsSecret create(String name, KmsSecretRevision latestRevision) {
|
||||||
|
KmsSecret instance = new KmsSecret();
|
||||||
|
instance.name = name;
|
||||||
|
instance.latestRevision = Key.create(latestRevision);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
117
java/google/registry/model/server/KmsSecretRevision.java
Normal file
117
java/google/registry/model/server/KmsSecretRevision.java
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.model.server;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||||
|
import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION;
|
||||||
|
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
|
import com.googlecode.objectify.annotation.Cache;
|
||||||
|
import com.googlecode.objectify.annotation.Entity;
|
||||||
|
import com.googlecode.objectify.annotation.Id;
|
||||||
|
import com.googlecode.objectify.annotation.Parent;
|
||||||
|
import google.registry.model.Buildable;
|
||||||
|
import google.registry.model.CreateAutoTimestamp;
|
||||||
|
import google.registry.model.ImmutableObject;
|
||||||
|
import google.registry.model.annotations.ReportedOn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An encrypted value.
|
||||||
|
*
|
||||||
|
* <p>Used to store passwords and other sensitive information in Datastore. Multiple versions of a
|
||||||
|
* {@link KmsSecretRevision} may be persisted but only the latest version is primary. A key to the
|
||||||
|
* primary version is stored by {@link KmsSecret#latestRevision}.
|
||||||
|
*
|
||||||
|
* <p>The value can be encrypted and decrypted using Cloud KMS.
|
||||||
|
*
|
||||||
|
* @see <a href="https://cloud.google.com/kms/docs/">Google Cloud Key Management Service
|
||||||
|
* Documentation</a>
|
||||||
|
* @see google.registry.keyring.kms.KmsKeyring
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@ReportedOn
|
||||||
|
@Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION)
|
||||||
|
public class KmsSecretRevision extends ImmutableObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum allowable secret size. Although Datastore allows entities up to 1 MB in size,
|
||||||
|
* BigQuery imports of Datastore backups limit individual columns (entity attributes) to 64 KB.
|
||||||
|
*/
|
||||||
|
private static final int MAX_SECRET_SIZE_BYTES = 64 * 1024 * 1024;
|
||||||
|
|
||||||
|
/** The revision of this secret. */
|
||||||
|
@Id long revisionKey;
|
||||||
|
|
||||||
|
/** The parent {@link KmsSecret} which contains metadata about this {@link KmsSecretRevision}. */
|
||||||
|
@Parent Key<KmsSecret> parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the {@code cryptoKeyVersion} associated with this {@link KmsSecretRevision}.
|
||||||
|
*
|
||||||
|
* @see <a
|
||||||
|
* href="https://cloud.google.com/kms/docs/reference/rest/v1beta1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions">projects.locations.keyRings.cryptoKeys.cryptoKeyVersions</a>
|
||||||
|
*/
|
||||||
|
String kmsCryptoKeyVersionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base64-encoded encrypted value of this {@link KmsSecretRevision} as returned by the Cloud
|
||||||
|
* KMS API.
|
||||||
|
*
|
||||||
|
* @see <a
|
||||||
|
* href="https://cloud.google.com/kms/docs/reference/rest/v1beta1/projects.locations.keyRings.cryptoKeys/encrypt">projects.locations.keyRings.cryptoKeys.encrypt</a>
|
||||||
|
*/
|
||||||
|
String encryptedValue;
|
||||||
|
|
||||||
|
/** An automatically managed creation timestamp. */
|
||||||
|
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||||
|
|
||||||
|
public String getKmsCryptoKeyVersionName() {
|
||||||
|
return kmsCryptoKeyVersionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncryptedValue() {
|
||||||
|
return encryptedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A builder for constructing {@link KmsSecretRevision} entities, since they are immutable. */
|
||||||
|
public static class Builder extends Buildable.Builder<KmsSecretRevision> {
|
||||||
|
|
||||||
|
public Builder setKmsCryptoKeyVersionName(String kmsCryptoKeyVersionName) {
|
||||||
|
getInstance().kmsCryptoKeyVersionName = kmsCryptoKeyVersionName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setEncryptedValue(String encryptedValue) {
|
||||||
|
checkArgument(
|
||||||
|
encryptedValue.length() <= MAX_SECRET_SIZE_BYTES,
|
||||||
|
"Secret is greater than %s bytes",
|
||||||
|
MAX_SECRET_SIZE_BYTES);
|
||||||
|
|
||||||
|
getInstance().encryptedValue = encryptedValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the parent {@link KmsSecret}.
|
||||||
|
*
|
||||||
|
* <p>The secret may not exist yet, so it is referred to by name rather than by reference.
|
||||||
|
*/
|
||||||
|
public Builder setParent(String secretName) {
|
||||||
|
getInstance().parent = Key.create(getCrossTldKey(), KmsSecret.class, secretName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ def domain_registry_repositories(
|
||||||
omit_com_google_api_client_servlet=False,
|
omit_com_google_api_client_servlet=False,
|
||||||
omit_com_google_apis_google_api_services_admin_directory=False,
|
omit_com_google_apis_google_api_services_admin_directory=False,
|
||||||
omit_com_google_apis_google_api_services_bigquery=False,
|
omit_com_google_apis_google_api_services_bigquery=False,
|
||||||
|
omit_com_google_apis_google_api_services_cloudkms=False,
|
||||||
omit_com_google_apis_google_api_services_dns=False,
|
omit_com_google_apis_google_api_services_dns=False,
|
||||||
omit_com_google_apis_google_api_services_drive=False,
|
omit_com_google_apis_google_api_services_drive=False,
|
||||||
omit_com_google_apis_google_api_services_groupssettings=False,
|
omit_com_google_apis_google_api_services_groupssettings=False,
|
||||||
|
@ -129,6 +130,8 @@ def domain_registry_repositories(
|
||||||
com_google_apis_google_api_services_admin_directory()
|
com_google_apis_google_api_services_admin_directory()
|
||||||
if not omit_com_google_apis_google_api_services_bigquery:
|
if not omit_com_google_apis_google_api_services_bigquery:
|
||||||
com_google_apis_google_api_services_bigquery()
|
com_google_apis_google_api_services_bigquery()
|
||||||
|
if not omit_com_google_apis_google_api_services_cloudkms:
|
||||||
|
com_google_apis_google_api_services_cloudkms()
|
||||||
if not omit_com_google_apis_google_api_services_dns:
|
if not omit_com_google_apis_google_api_services_dns:
|
||||||
com_google_apis_google_api_services_dns()
|
com_google_apis_google_api_services_dns()
|
||||||
if not omit_com_google_apis_google_api_services_drive:
|
if not omit_com_google_apis_google_api_services_drive:
|
||||||
|
@ -424,6 +427,18 @@ def com_google_apis_google_api_services_bigquery():
|
||||||
deps = ["@com_google_api_client"],
|
deps = ["@com_google_api_client"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def com_google_apis_google_api_services_cloudkms():
|
||||||
|
java_import_external(
|
||||||
|
name = "com_google_apis_google_api_services_cloudkms",
|
||||||
|
licenses = ["notice"], # The Apache Software License, Version 2.0
|
||||||
|
jar_sha256 = "82e5995e9dd248d24edfeace90261c1be0e905ecbae0b2c5ee19bb06a3e7dfdf",
|
||||||
|
jar_urls = [
|
||||||
|
"http://domain-registry-maven.storage.googleapis.com/repo1.maven.org/maven2/com/google/apis/google-api-services-cloudkms/v1beta1-rev409-1.22.0/google-api-services-cloudkms-v1beta1-rev409-1.22.0.jar",
|
||||||
|
"http://repo1.maven.org/maven2/com/google/apis/google-api-services-cloudkms/v1beta1-rev409-1.22.0/google-api-services-cloudkms-v1beta1-rev409-1.22.0.jar",
|
||||||
|
],
|
||||||
|
deps = ["@com_google_api_client"],
|
||||||
|
)
|
||||||
|
|
||||||
def com_google_apis_google_api_services_dns():
|
def com_google_apis_google_api_services_dns():
|
||||||
java_import_external(
|
java_import_external(
|
||||||
name = "com_google_apis_google_api_services_dns",
|
name = "com_google_apis_google_api_services_dns",
|
||||||
|
|
|
@ -10,6 +10,8 @@ ForeignKeyDomainIndex
|
||||||
ForeignKeyHostIndex
|
ForeignKeyHostIndex
|
||||||
HistoryEntry
|
HistoryEntry
|
||||||
HostResource
|
HostResource
|
||||||
|
KmsSecret
|
||||||
|
KmsSecretRevision
|
||||||
LogsExportCursor
|
LogsExportCursor
|
||||||
LrpTokenEntity
|
LrpTokenEntity
|
||||||
Modification
|
Modification
|
||||||
|
|
|
@ -8,6 +8,8 @@ ForeignKeyDomainIndex
|
||||||
ForeignKeyHostIndex
|
ForeignKeyHostIndex
|
||||||
HistoryEntry
|
HistoryEntry
|
||||||
HostResource
|
HostResource
|
||||||
|
KmsSecret
|
||||||
|
KmsSecretRevision
|
||||||
LrpTokenEntity
|
LrpTokenEntity
|
||||||
Modification
|
Modification
|
||||||
OneTime
|
OneTime
|
||||||
|
|
40
javatests/google/registry/keyring/kms/BUILD
Normal file
40
javatests/google/registry/keyring/kms/BUILD
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package(
|
||||||
|
default_testonly = 1,
|
||||||
|
default_visibility = ["//java/google/registry:registry_project"],
|
||||||
|
)
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = "kms",
|
||||||
|
srcs = glob(["*.java"]),
|
||||||
|
resources = [
|
||||||
|
"pgp-private-keyring-registry.asc",
|
||||||
|
"pgp-public-keyring.asc",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//java/google/registry/keyring/kms",
|
||||||
|
"//java/google/registry/model",
|
||||||
|
"//javatests/google/registry/testing",
|
||||||
|
"//third_party/java/objectify:objectify-v4_1",
|
||||||
|
"@com_google_api_client",
|
||||||
|
"@com_google_apis_google_api_services_cloudkms",
|
||||||
|
"@com_google_code_findbugs_jsr305",
|
||||||
|
"@com_google_guava",
|
||||||
|
"@com_google_http_client",
|
||||||
|
"@com_google_http_client_jackson2",
|
||||||
|
"@com_google_truth",
|
||||||
|
"@junit",
|
||||||
|
"@org_bouncycastle_bcpg_jdk15on",
|
||||||
|
"@org_bouncycastle_bcpkix_jdk15on",
|
||||||
|
"@org_mockito_all",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
GenTestRules(
|
||||||
|
name = "GeneratedTestRules",
|
||||||
|
test_files = glob(["*Test.java"]),
|
||||||
|
deps = [":kms"],
|
||||||
|
)
|
|
@ -0,0 +1,171 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||||
|
import com.google.api.client.http.GenericUrl;
|
||||||
|
import com.google.api.client.http.HttpContent;
|
||||||
|
import com.google.api.client.http.HttpRequest;
|
||||||
|
import com.google.api.client.http.HttpRequestFactory;
|
||||||
|
import com.google.api.client.http.HttpResponse;
|
||||||
|
import com.google.api.client.http.HttpTransport;
|
||||||
|
import com.google.api.client.http.LowLevelHttpRequest;
|
||||||
|
import com.google.api.client.http.LowLevelHttpResponse;
|
||||||
|
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/** A helper to create instances of {@link GoogleJsonResponseException}. */
|
||||||
|
public class GoogleJsonResponseExceptionHelper {
|
||||||
|
/**
|
||||||
|
* @param statusCode the status code that should be in the returned {@link
|
||||||
|
* GoogleJsonResponseException}
|
||||||
|
* @return a {@link GoogleJsonResponseException} with the status code {@code statusCode}
|
||||||
|
* @throws IOException shouldn't occur
|
||||||
|
*/
|
||||||
|
public static GoogleJsonResponseException create(int statusCode) throws IOException {
|
||||||
|
HttpResponse response = createHttpResponse(statusCode, null);
|
||||||
|
return GoogleJsonResponseException.from(new JacksonFactory(), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpResponse createHttpResponse(int statusCode, InputStream content)
|
||||||
|
throws IOException {
|
||||||
|
FakeHttpTransport transport = new FakeHttpTransport(statusCode, content);
|
||||||
|
HttpRequestFactory factory = transport.createRequestFactory();
|
||||||
|
HttpRequest request =
|
||||||
|
factory.buildRequest(
|
||||||
|
"foo", new GenericUrl("http://example.com/bar"), new EmptyHttpContent());
|
||||||
|
request.setThrowExceptionOnExecuteError(false);
|
||||||
|
return request.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FakeHttpTransport extends HttpTransport {
|
||||||
|
private final int statusCode;
|
||||||
|
private final InputStream content;
|
||||||
|
|
||||||
|
FakeHttpTransport(int statusCode, InputStream content) {
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
|
||||||
|
return new FakeLowLevelHttpRequest(statusCode, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FakeLowLevelHttpRequest extends LowLevelHttpRequest {
|
||||||
|
private final int statusCode;
|
||||||
|
private final InputStream content;
|
||||||
|
|
||||||
|
FakeLowLevelHttpRequest(int statusCode, InputStream content) {
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addHeader(String name, String value) throws IOException {
|
||||||
|
// Nothing!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LowLevelHttpResponse execute() throws IOException {
|
||||||
|
return new FakeLowLevelHttpResponse(statusCode, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FakeLowLevelHttpResponse extends LowLevelHttpResponse {
|
||||||
|
private final int statusCode;
|
||||||
|
private final InputStream content;
|
||||||
|
|
||||||
|
FakeLowLevelHttpResponse(int statusCode, InputStream content) {
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getContent() throws IOException {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentEncoding() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLength() throws IOException {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() throws IOException {
|
||||||
|
return "text/json";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStatusLine() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStatusCode() throws IOException {
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReasonPhrase() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeaderCount() throws IOException {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeaderName(int index) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeaderValue(int index) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EmptyHttpContent implements HttpContent {
|
||||||
|
@Override
|
||||||
|
public long getLength() throws IOException {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "text/json";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retrySupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException {
|
||||||
|
// Nothing!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
344
javatests/google/registry/keyring/kms/KmsKeyringTest.java
Normal file
344
javatests/google/registry/keyring/kms/KmsKeyringTest.java
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.testing.DatastoreHelper.persistResources;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.CloudKMS;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.DecryptRequest;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.DecryptResponse;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import google.registry.model.server.KmsSecret;
|
||||||
|
import google.registry.model.server.KmsSecretRevision;
|
||||||
|
import google.registry.model.server.KmsSecretRevision.Builder;
|
||||||
|
import google.registry.testing.AppEngineRule;
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class KmsKeyringTest {
|
||||||
|
|
||||||
|
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
||||||
|
|
||||||
|
@Mock private CloudKMS kms;
|
||||||
|
@Mock private CloudKMS.Projects kmsProjects;
|
||||||
|
@Mock private CloudKMS.Projects.Locations kmsLocations;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings kmsKeyRings;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys kmsCryptoKeys;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Decrypt kmsCryptoKeysDecrypt;
|
||||||
|
|
||||||
|
@Captor private ArgumentCaptor<String> cryptoKeyName;
|
||||||
|
@Captor private ArgumentCaptor<DecryptRequest> decryptRequest;
|
||||||
|
|
||||||
|
private KmsKeyring keyring;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
keyring = new KmsKeyring("foo", "bar", kms);
|
||||||
|
|
||||||
|
when(kms.projects()).thenReturn(kmsProjects);
|
||||||
|
when(kmsProjects.locations()).thenReturn(kmsLocations);
|
||||||
|
when(kmsLocations.keyRings()).thenReturn(kmsKeyRings);
|
||||||
|
when(kmsKeyRings.cryptoKeys()).thenReturn(kmsCryptoKeys);
|
||||||
|
when(kmsCryptoKeys.decrypt(anyString(), any(DecryptRequest.class)))
|
||||||
|
.thenReturn(kmsCryptoKeysDecrypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getRdeSigningKey() throws Exception {
|
||||||
|
persistSecret("rde-signing-private");
|
||||||
|
persistSecret("rde-signing-public");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse()
|
||||||
|
.encodePlaintext(KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded()))
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse().encodePlaintext(KmsTestHelper.getPrivateKeyring().getEncoded()));
|
||||||
|
|
||||||
|
PGPKeyPair rdeSigningKey = keyring.getRdeSigningKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys, times(2)).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-signing-public",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-signing-private"));
|
||||||
|
assertThat(decryptRequest.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE),
|
||||||
|
new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE)));
|
||||||
|
assertThat(rdeSigningKey.getKeyID())
|
||||||
|
.isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getKeyID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getRdeStagingEncryptionKey() throws Exception {
|
||||||
|
persistSecret("rde-staging-public");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse()
|
||||||
|
.encodePlaintext(KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded()));
|
||||||
|
|
||||||
|
PGPPublicKey rdeStagingEncryptionKey = keyring.getRdeStagingEncryptionKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-staging-public");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(rdeStagingEncryptionKey.getFingerprint())
|
||||||
|
.isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getFingerprint());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getRdeStagingDecryptionKey() throws Exception {
|
||||||
|
persistSecret("rde-staging-private");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse().encodePlaintext(KmsTestHelper.getPrivateKeyring().getEncoded()));
|
||||||
|
|
||||||
|
PGPPrivateKey rdeStagingDecryptionKey = keyring.getRdeStagingDecryptionKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-staging-private");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(rdeStagingDecryptionKey.getKeyID())
|
||||||
|
.isEqualTo(KmsTestHelper.getPrivateKeyring().getSecretKey().getKeyID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getRdeReceiverKey() throws Exception {
|
||||||
|
persistSecret("rde-receiver-public");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse().encodePlaintext(KmsTestHelper.getPublicKeyring().getEncoded()));
|
||||||
|
|
||||||
|
PGPPublicKey rdeReceiverKey = keyring.getRdeReceiverKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-receiver-public");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(rdeReceiverKey.getFingerprint())
|
||||||
|
.isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getFingerprint());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getBrdaSigningKey() throws Exception {
|
||||||
|
persistSecret("brda-signing-public");
|
||||||
|
persistSecret("brda-signing-private");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse()
|
||||||
|
.encodePlaintext(KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded()))
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse().encodePlaintext(KmsTestHelper.getPrivateKeyring().getEncoded()));
|
||||||
|
|
||||||
|
PGPKeyPair brdaSigningKey = keyring.getBrdaSigningKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys, times(2)).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/brda-signing-public",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/brda-signing-private"));
|
||||||
|
assertThat(decryptRequest.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE),
|
||||||
|
new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE)));
|
||||||
|
assertThat(brdaSigningKey.getKeyID())
|
||||||
|
.isEqualTo(KmsTestHelper.getPrivateKeyring().getPublicKey().getKeyID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getBrdaReceiverKey() throws Exception {
|
||||||
|
persistSecret("brda-receiver-public");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new DecryptResponse()
|
||||||
|
.encodePlaintext(KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded()));
|
||||||
|
|
||||||
|
PGPPublicKey brdaReceiverKey = keyring.getBrdaReceiverKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/brda-receiver-public");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(brdaReceiverKey.getFingerprint())
|
||||||
|
.isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getFingerprint());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getRdeSshClientPublicKey() throws Exception {
|
||||||
|
persistSecret("rde-ssh-client-public");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String rdeSshClientPublicKey = keyring.getRdeSshClientPublicKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-ssh-client-public");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(rdeSshClientPublicKey).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getRdeSshClientPrivateKey() throws Exception {
|
||||||
|
persistSecret("rde-ssh-client-private");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String rdeSshClientPrivateKey = keyring.getRdeSshClientPrivateKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-ssh-client-private");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(rdeSshClientPrivateKey).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getIcannReportingPassword() throws Exception {
|
||||||
|
persistSecret("icann-reporting-password");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext("icann123".getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String icannReportingPassword = keyring.getIcannReportingPassword();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo(
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/icann-reporting-password");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(icannReportingPassword).isEqualTo("icann123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getMarksdbDnlLogin() throws Exception {
|
||||||
|
persistSecret("marksdb-dnl-login");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String marksdbDnlLogin = keyring.getMarksdbDnlLogin();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/marksdb-dnl-login");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(marksdbDnlLogin).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getMarksdbLordnPassword() throws Exception {
|
||||||
|
persistSecret("marksdb-lordn-password");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String marksdbLordnPassword = keyring.getMarksdbLordnPassword();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/marksdb-lordn-password");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(marksdbLordnPassword).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getMarksdbSmdrlLogin() throws Exception {
|
||||||
|
persistSecret("marksdb-smdrl-login");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String marksdbSmdrlLogin = keyring.getMarksdbSmdrlLogin();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/marksdb-smdrl-login");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(marksdbSmdrlLogin).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getJsonCredential() throws Exception {
|
||||||
|
persistSecret("json-credential");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String jsonCredential = keyring.getJsonCredential();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/json-credential");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(jsonCredential).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getBraintreePrivateKey() throws Exception {
|
||||||
|
persistSecret("braintree-private-key");
|
||||||
|
when(kmsCryptoKeysDecrypt.execute())
|
||||||
|
.thenReturn(new DecryptResponse().encodePlaintext(KmsTestHelper.DUMMY_KEY.getBytes(UTF_8)));
|
||||||
|
|
||||||
|
String braintreePrivateKey = keyring.getBraintreePrivateKey();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/braintree-private-key");
|
||||||
|
assertThat(decryptRequest.getValue())
|
||||||
|
.isEqualTo(new DecryptRequest().setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
assertThat(braintreePrivateKey).isEqualTo(KmsTestHelper.DUMMY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void persistSecret(String secretName) {
|
||||||
|
KmsSecretRevision secretRevision =
|
||||||
|
new Builder()
|
||||||
|
.setEncryptedValue(KmsTestHelper.DUMMY_ENCRYPTED_VALUE)
|
||||||
|
.setKmsCryptoKeyVersionName(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION)
|
||||||
|
.setParent(secretName)
|
||||||
|
.build();
|
||||||
|
KmsSecret secret = KmsSecret.create(secretName, secretRevision);
|
||||||
|
persistResources(ImmutableList.of(secretRevision, secret));
|
||||||
|
}
|
||||||
|
}
|
49
javatests/google/registry/keyring/kms/KmsTestHelper.java
Normal file
49
javatests/google/registry/keyring/kms/KmsTestHelper.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import static com.google.common.io.Resources.getResource;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteSource;
|
||||||
|
import com.google.common.io.Resources;
|
||||||
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
|
import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing;
|
||||||
|
|
||||||
|
/** Stores dummy values for test use in {@link KmsUpdaterTest} and {@link KmsKeyringTest}. */
|
||||||
|
final class KmsTestHelper {
|
||||||
|
|
||||||
|
static final String DUMMY_KEY = "the quick brown fox";
|
||||||
|
static final String DUMMY_CRYPTO_KEY_VERSION = "cheeseburger";
|
||||||
|
static final String DUMMY_ENCRYPTED_VALUE = "meow";
|
||||||
|
|
||||||
|
/** The contents of a dummy PGP public key stored in a file. */
|
||||||
|
private static final ByteSource PGP_PUBLIC_KEYRING =
|
||||||
|
Resources.asByteSource(getResource(KmsTestHelper.class, "pgp-public-keyring.asc"));
|
||||||
|
|
||||||
|
/** The contents of a dummy PGP private key stored in a file. */
|
||||||
|
private static final ByteSource PGP_PRIVATE_KEYRING =
|
||||||
|
Resources.asByteSource(getResource(KmsTestHelper.class, "pgp-private-keyring-registry.asc"));
|
||||||
|
|
||||||
|
static BcPGPPublicKeyRing getPublicKeyring() throws Exception {
|
||||||
|
return new BcPGPPublicKeyRing(PGPUtil.getDecoderStream(PGP_PUBLIC_KEYRING.openStream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static BcPGPSecretKeyRing getPrivateKeyring() throws Exception {
|
||||||
|
return new BcPGPSecretKeyRing(PGPUtil.getDecoderStream(PGP_PRIVATE_KEYRING.openStream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private KmsTestHelper() {}
|
||||||
|
}
|
477
javatests/google/registry/keyring/kms/KmsUpdaterTest.java
Normal file
477
javatests/google/registry/keyring/kms/KmsUpdaterTest.java
Normal file
|
@ -0,0 +1,477 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.keyring.kms;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||||
|
import com.google.api.client.http.HttpResponse;
|
||||||
|
import com.google.api.client.http.HttpResponseException;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.CloudKMS;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.CryptoKey;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.CryptoKeyVersion;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.EncryptRequest;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.EncryptResponse;
|
||||||
|
import com.google.api.services.cloudkms.v1beta1.model.KeyRing;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.googlecode.objectify.Key;
|
||||||
|
import google.registry.model.server.KmsSecret;
|
||||||
|
import google.registry.model.server.KmsSecretRevision;
|
||||||
|
import google.registry.testing.AppEngineRule;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class KmsUpdaterTest {
|
||||||
|
|
||||||
|
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
||||||
|
|
||||||
|
@Mock private CloudKMS kms;
|
||||||
|
@Mock private CloudKMS.Projects kmsProjects;
|
||||||
|
@Mock private CloudKMS.Projects.Locations kmsLocations;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings kmsKeyRings;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.Get kmsKeyRingsGet;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.Create kmsKeyRingsCreate;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys kmsCryptoKeys;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Get kmsCryptoKeysGet;
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Create kmsCryptoKeysCreate;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions kmsCryptoKeyVersions;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions.Create
|
||||||
|
kmsCryptoKeyVersionsCreate;
|
||||||
|
|
||||||
|
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Encrypt kmsCryptoKeysEncrypt;
|
||||||
|
|
||||||
|
@Captor private ArgumentCaptor<KeyRing> keyRing;
|
||||||
|
@Captor private ArgumentCaptor<CryptoKey> cryptoKey;
|
||||||
|
@Captor private ArgumentCaptor<CryptoKeyVersion> cryptoKeyVersion;
|
||||||
|
@Captor private ArgumentCaptor<String> keyRingName;
|
||||||
|
@Captor private ArgumentCaptor<String> cryptoKeyName;
|
||||||
|
@Captor private ArgumentCaptor<String> cryptoKeyVersionName;
|
||||||
|
@Captor private ArgumentCaptor<EncryptRequest> encryptRequest;
|
||||||
|
|
||||||
|
private KmsUpdater updater;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
when(kms.projects()).thenReturn(kmsProjects);
|
||||||
|
when(kmsProjects.locations()).thenReturn(kmsLocations);
|
||||||
|
when(kmsLocations.keyRings()).thenReturn(kmsKeyRings);
|
||||||
|
when(kmsKeyRings.get(anyString())).thenReturn(kmsKeyRingsGet);
|
||||||
|
when(kmsKeyRings.create(anyString(), any(KeyRing.class))).thenReturn(kmsKeyRingsCreate);
|
||||||
|
when(kmsKeyRings.cryptoKeys()).thenReturn(kmsCryptoKeys);
|
||||||
|
when(kmsCryptoKeys.get(anyString())).thenReturn(kmsCryptoKeysGet);
|
||||||
|
when(kmsCryptoKeys.create(anyString(), any(CryptoKey.class))).thenReturn(kmsCryptoKeysCreate);
|
||||||
|
when(kmsCryptoKeys.cryptoKeyVersions()).thenReturn(kmsCryptoKeyVersions);
|
||||||
|
when(kmsCryptoKeyVersions.create(anyString(), any(CryptoKeyVersion.class)))
|
||||||
|
.thenReturn(kmsCryptoKeyVersionsCreate);
|
||||||
|
when(kmsCryptoKeyVersionsCreate.execute())
|
||||||
|
.thenReturn(new CryptoKeyVersion().setName(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION));
|
||||||
|
when(kmsCryptoKeys.encrypt(anyString(), any(EncryptRequest.class)))
|
||||||
|
.thenReturn(kmsCryptoKeysEncrypt);
|
||||||
|
when(kmsCryptoKeysEncrypt.execute())
|
||||||
|
.thenReturn(
|
||||||
|
new EncryptResponse()
|
||||||
|
.setName(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION)
|
||||||
|
.setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
|
||||||
|
|
||||||
|
updater = new KmsUpdater("foo", "bar", kms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_close_createsNewKeyRing_ifNotFound() throws Exception {
|
||||||
|
when(kmsKeyRingsGet.execute()).thenThrow(createNotFoundException());
|
||||||
|
|
||||||
|
updater.setBraintreePrivateKey(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verify(kmsKeyRings).create(keyRingName.capture(), keyRing.capture());
|
||||||
|
assertThat(keyRingName.getValue()).isEqualTo("global");
|
||||||
|
assertThat(keyRing.getValue())
|
||||||
|
.isEqualTo(new KeyRing().setName("projects/foo/locations/global/keyRings/bar"));
|
||||||
|
verify(kmsKeyRingsCreate).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_close_createsNewCryptoKey_ifNotFound() throws Exception {
|
||||||
|
when(kmsCryptoKeysGet.execute()).thenThrow(createNotFoundException());
|
||||||
|
|
||||||
|
updater.setBraintreePrivateKey(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).create(cryptoKeyName.capture(), cryptoKey.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue())
|
||||||
|
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/braintree-private-key");
|
||||||
|
assertThat(cryptoKey.getValue())
|
||||||
|
.isEqualTo(new CryptoKey().setName("braintree-private-key").setPurpose("ENCRYPT_DECRYPT"));
|
||||||
|
verify(kmsCryptoKeysCreate).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setMultipleSecrets() throws Exception {
|
||||||
|
updater
|
||||||
|
.setBraintreePrivateKey("value1")
|
||||||
|
.setIcannReportingPassword("value2")
|
||||||
|
.setJsonCredential("value3");
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys, times(3)).get(cryptoKeyName.capture());
|
||||||
|
assertThat(cryptoKeyName.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
"projects/foo/locations/" + "global/keyRings/bar/cryptoKeys/braintree-private-key",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/icann-reporting-password",
|
||||||
|
"projects/foo/locations/" + "global/keyRings/bar/cryptoKeys/json-credential"));
|
||||||
|
|
||||||
|
verify(kmsCryptoKeyVersions, times(3))
|
||||||
|
.create(cryptoKeyVersionName.capture(), cryptoKeyVersion.capture());
|
||||||
|
assertThat(cryptoKeyVersionName.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/braintree-private-key/cryptoKeyVersions",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/icann-reporting-password/cryptoKeyVersions",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/json-credential/cryptoKeyVersions"));
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys, times(3)).encrypt(cryptoKeyName.capture(), encryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue()).isEqualTo(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION);
|
||||||
|
assertThat(encryptRequest.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
new EncryptRequest().encodePlaintext("value1".getBytes(UTF_8)),
|
||||||
|
new EncryptRequest().encodePlaintext("value2".getBytes(UTF_8)),
|
||||||
|
new EncryptRequest().encodePlaintext("value3".getBytes(UTF_8))));
|
||||||
|
|
||||||
|
KmsSecret firstSecret = loadSecret("braintree-private-key");
|
||||||
|
assertThat(firstSecret).isNotNull();
|
||||||
|
assertThat(firstSecret.getLatestRevision()).isNotNull();
|
||||||
|
|
||||||
|
KmsSecret secondSecret = loadSecret("icann-reporting-password");
|
||||||
|
assertThat(secondSecret).isNotNull();
|
||||||
|
assertThat(secondSecret.getLatestRevision()).isNotNull();
|
||||||
|
|
||||||
|
KmsSecret thirdSecret = loadSecret("icann-reporting-password");
|
||||||
|
assertThat(thirdSecret).isNotNull();
|
||||||
|
assertThat(thirdSecret.getLatestRevision()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setBraintreePrivateKey() throws Exception {
|
||||||
|
updater.setBraintreePrivateKey(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"braintree-private-key",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/braintree-private-key",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/braintree-private-key/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setBrdaReceiverKey() throws Exception {
|
||||||
|
updater.setBrdaReceiverPublicKey(KmsTestHelper.getPublicKeyring().getPublicKey());
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"brda-receiver-public",
|
||||||
|
KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/brda-receiver-public",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/brda-receiver-public/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setBrdaSigningKey() throws Exception {
|
||||||
|
updater.setBrdaSigningKey(KmsTestHelper.getPrivateKeyring());
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"brda-signing-private",
|
||||||
|
KmsTestHelper.getPrivateKeyring().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/brda-signing-private",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/brda-signing-private/cryptoKeyVersions",
|
||||||
|
"brda-signing-public",
|
||||||
|
KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/brda-signing-public",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/brda-signing-public/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setIcannReportingPassword() throws Exception {
|
||||||
|
updater.setIcannReportingPassword(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"icann-reporting-password",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/icann-reporting-password",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/icann-reporting-password/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setJsonCredential() throws Exception {
|
||||||
|
updater.setJsonCredential(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"json-credential",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/json-credential",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/json-credential/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setMarksdbDnlLogin() throws Exception {
|
||||||
|
updater.setMarksdbDnlLogin(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"marksdb-dnl-login",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/marksdb-dnl-login",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/marksdb-dnl-login/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setMarksdbLordnPassword() throws Exception {
|
||||||
|
updater.setMarksdbLordnPassword(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"marksdb-lordn-password",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/marksdb-lordn-password",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/marksdb-lordn-password/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setMarksdbSmdrlLogin() throws Exception {
|
||||||
|
updater.setMarksdbSmdrlLogin(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"marksdb-smdrl-login",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/marksdb-smdrl-login",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/marksdb-smdrl-login/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setRdeReceiverKey() throws Exception {
|
||||||
|
updater.setRdeReceiverPublicKey(KmsTestHelper.getPublicKeyring().getPublicKey());
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"rde-receiver-public",
|
||||||
|
KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-receiver-public",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-receiver-public/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setRdeSigningKey() throws Exception {
|
||||||
|
updater.setRdeSigningKey(KmsTestHelper.getPrivateKeyring());
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"rde-signing-private",
|
||||||
|
KmsTestHelper.getPrivateKeyring().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-signing-private",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-signing-private/cryptoKeyVersions",
|
||||||
|
"rde-signing-public",
|
||||||
|
KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-signing-public",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-signing-public/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setRdeSshClientPrivateKey() throws Exception {
|
||||||
|
updater.setRdeSshClientPrivateKey(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"rde-ssh-client-private",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-ssh-client-private",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-ssh-client-private/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setRdeSshClientPublicKey() throws Exception {
|
||||||
|
updater.setRdeSshClientPublicKey(KmsTestHelper.DUMMY_KEY);
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"rde-ssh-client-public",
|
||||||
|
KmsTestHelper.DUMMY_KEY.getBytes(UTF_8),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-ssh-client-public",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-ssh-client-public/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setRdeStagingKey() throws Exception {
|
||||||
|
updater.setRdeStagingKey(KmsTestHelper.getPrivateKeyring());
|
||||||
|
updater.update();
|
||||||
|
|
||||||
|
verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
"rde-staging-private",
|
||||||
|
KmsTestHelper.getPrivateKeyring().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar",
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-staging-private",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-staging-private/cryptoKeyVersions",
|
||||||
|
"rde-staging-public",
|
||||||
|
KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded(),
|
||||||
|
"projects/foo/locations/global/keyRings/bar/cryptoKeys/rde-staging-public",
|
||||||
|
"projects/foo/locations/"
|
||||||
|
+ "global/keyRings/bar/cryptoKeys/rde-staging-public/cryptoKeyVersions");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
String secretName,
|
||||||
|
byte[] goldenValue,
|
||||||
|
String goldenCryptoKeyRingName,
|
||||||
|
String goldenCryptoKeyName,
|
||||||
|
String goldenCryptoKeyVersionName)
|
||||||
|
throws Exception {
|
||||||
|
verify(kmsKeyRings).get(keyRingName.capture());
|
||||||
|
assertThat(keyRingName.getValue()).isEqualTo(goldenCryptoKeyRingName);
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).get(cryptoKeyName.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue()).isEqualTo(goldenCryptoKeyName);
|
||||||
|
|
||||||
|
verify(kmsCryptoKeyVersions).create(cryptoKeyVersionName.capture(), cryptoKeyVersion.capture());
|
||||||
|
assertThat(cryptoKeyVersionName.getValue()).isEqualTo(goldenCryptoKeyVersionName);
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys).encrypt(cryptoKeyName.capture(), encryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue()).isEqualTo(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION);
|
||||||
|
assertThat(encryptRequest.getValue())
|
||||||
|
.isEqualTo(new EncryptRequest().encodePlaintext(goldenValue));
|
||||||
|
|
||||||
|
KmsSecret secret = loadSecret(secretName);
|
||||||
|
KmsSecretRevision secretRevision = ofy().load().key(secret.getLatestRevision()).now();
|
||||||
|
assertThat(secretRevision.getKmsCryptoKeyVersionName())
|
||||||
|
.isEqualTo(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION);
|
||||||
|
assertThat(secretRevision.getEncryptedValue()).isEqualTo(KmsTestHelper.DUMMY_ENCRYPTED_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Variant of {@code verifyKmsApiCallsAndDatastoreWrites} for key pairs. */
|
||||||
|
private void verifyKmsApiCallsAndDatastoreWrites(
|
||||||
|
String firstSecretName,
|
||||||
|
byte[] firstGoldenValue,
|
||||||
|
String goldenCryptoKeyRingName,
|
||||||
|
String firstGoldenCryptoKeyName,
|
||||||
|
String firstGoldenCryptoKeyVersionName,
|
||||||
|
String secondSecretName,
|
||||||
|
byte[] secondGoldenValue,
|
||||||
|
String secondGoldenCryptoKeyName,
|
||||||
|
String secondGoldenCryptoKeyVersionName)
|
||||||
|
throws Exception {
|
||||||
|
verify(kmsKeyRings, times(1)).get(keyRingName.capture());
|
||||||
|
assertThat(keyRingName.getValue()).isEqualTo(goldenCryptoKeyRingName);
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys, times(2)).get(cryptoKeyName.capture());
|
||||||
|
assertThat(cryptoKeyName.getAllValues())
|
||||||
|
.isEqualTo(ImmutableList.of(firstGoldenCryptoKeyName, secondGoldenCryptoKeyName));
|
||||||
|
|
||||||
|
verify(kmsCryptoKeyVersions, times(2))
|
||||||
|
.create(cryptoKeyVersionName.capture(), cryptoKeyVersion.capture());
|
||||||
|
assertThat(cryptoKeyVersionName.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(firstGoldenCryptoKeyVersionName, secondGoldenCryptoKeyVersionName));
|
||||||
|
|
||||||
|
verify(kmsCryptoKeys, times(2)).encrypt(cryptoKeyName.capture(), encryptRequest.capture());
|
||||||
|
assertThat(cryptoKeyName.getValue()).isEqualTo(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION);
|
||||||
|
assertThat(encryptRequest.getAllValues())
|
||||||
|
.isEqualTo(
|
||||||
|
ImmutableList.of(
|
||||||
|
new EncryptRequest().encodePlaintext(firstGoldenValue),
|
||||||
|
new EncryptRequest().encodePlaintext(secondGoldenValue)));
|
||||||
|
|
||||||
|
KmsSecret secret = loadSecret(firstSecretName);
|
||||||
|
KmsSecretRevision secretRevision = ofy().load().key(secret.getLatestRevision()).now();
|
||||||
|
assertThat(secretRevision.getKmsCryptoKeyVersionName())
|
||||||
|
.isEqualTo(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION);
|
||||||
|
assertThat(secretRevision.getEncryptedValue()).isEqualTo(KmsTestHelper.DUMMY_ENCRYPTED_VALUE);
|
||||||
|
|
||||||
|
KmsSecret secondSecret = loadSecret(secondSecretName);
|
||||||
|
KmsSecretRevision secondSecretRevision =
|
||||||
|
ofy().load().key(secondSecret.getLatestRevision()).now();
|
||||||
|
assertThat(secondSecretRevision.getKmsCryptoKeyVersionName())
|
||||||
|
.isEqualTo(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION);
|
||||||
|
assertThat(secondSecretRevision.getEncryptedValue())
|
||||||
|
.isEqualTo(KmsTestHelper.DUMMY_ENCRYPTED_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GoogleJsonResponseException createNotFoundException() throws Exception {
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream("".getBytes(UTF_8));
|
||||||
|
HttpResponse response = GoogleJsonResponseExceptionHelper.createHttpResponse(404, inputStream);
|
||||||
|
HttpResponseException.Builder httpResponseExceptionBuilder =
|
||||||
|
new HttpResponseException.Builder(response);
|
||||||
|
httpResponseExceptionBuilder.setStatusCode(404);
|
||||||
|
httpResponseExceptionBuilder.setStatusMessage("NOT_FOUND");
|
||||||
|
return new GoogleJsonResponseException(httpResponseExceptionBuilder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KmsSecret loadSecret(String secret) {
|
||||||
|
return ofy().load().key(Key.create(getCrossTldKey(), KmsSecret.class, secret)).now();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
gpg -a --export-secret-keys --export-options export-minimal \
|
||||||
|
rde-unittest@registry.test \
|
||||||
|
rde-unittest-dsa@registry.test \
|
||||||
|
>javatests/google/registry/rde/testdata/pgp-private-keyring-registry.asc
|
||||||
|
|
||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: GnuPG v1
|
||||||
|
|
||||||
|
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
|
||||||
|
7h8MmwWj2G8T/gt3lu0SEvm0/kpChJxF6hmyQLq/6tZITEVjqUCmFuta1plbANDt
|
||||||
|
Cu+8Jmw3NVRk9iTbrYZc9fytfjROi98si5eimvMEiwKv53MreIqyX7THHw1FXlFa
|
||||||
|
RDzez3zspmuOO+534APA3mVyuDtT4/3K35mBDwVqcykbQRBM8/nZhS6vrXNwkPp4
|
||||||
|
PhkN/Q9Ethfe6y1w9uHa9UshT0/tAguzas8d8ve+UmpmXqd1G60/mbWDMiLnJfFJ
|
||||||
|
H7fCz7EAZ37r99Ss1Z+l/wF3XUaIJ4UiV/3i1T3COCZaF5rjqdv1oEy+555/r5Mv
|
||||||
|
0gABAJJg1dR1x4xjstvehkDP6Gm0G27gGXdcwEj2VCvlGgO8EVy0L0pvbmF0aGFu
|
||||||
|
IFN3aWZ0IDxyZGUtdW5pdHRlc3QtZHNhQHJlZ2lzdHJ5LnRlc3Q+iHoEExEIACIF
|
||||||
|
AlUj/0gCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJ6mQpZ89XyKn+gB
|
||||||
|
AK91twI+kyFHwBcg+8E5fsai63ZfJsusASeNvtWhMhavAQC+kNNqO1KrDdczgcLu
|
||||||
|
X3bQCJG3gOzo6iq/3zC723HPHp0CPQRVI/9IEAgA5QrYsEpUuMBdfyod0D1VPetE
|
||||||
|
FBZ+T3sT3FXW/cpijeSl2OKYJIColhnLo3Etld8zNmJ6td27onEhjOFvT2rsBMs5
|
||||||
|
DLX4b7avXiRqTFUXi+qPLyI8iGyPaWjlmGNhED07egCVesM2t4gN+46IE0PztIDR
|
||||||
|
Uy1SdHp8bwnAEdlud5T4VZgGmFXI9s+4nrjdnm532XBLa8w03xSzDYq5B4T/5F/J
|
||||||
|
qDSz//p6aqrUonMYHr2PCp0xaHM+Q34m+jR1cqvQQJxBvcEBiNNZJuh23w1wNB/v
|
||||||
|
M2cI/LJTrCKIHVLRfy7NJVQPhqkgnJeVT4Bnzsy7LLKCzKuG7He2Ecf9L5TspwAE
|
||||||
|
DQf8DMnCuEbWss+H9KK0h/Xwi3Ftbp/tnOaynUyp7dOULGnlZ+5nioTiwvW0jRfN
|
||||||
|
AP86OIDP8zztahFXH9PuOCtQ2rR7VbUUaohafsvX3Cn6+5ITjA7kzINa+Uc1eKsi
|
||||||
|
Bn1ze6rT5E7svLUxT+9N/0d0OCcjCf3aLfJggOHYws8PwDdhbYKPeX082t64BAsM
|
||||||
|
6UViv0ZiNUL4zj0Q5PUO1tWDvVZ3x9k/gHbi1vS/2+U6sMYhP/gh7/L//du0ty/v
|
||||||
|
5mOLkBKIyKWltfLRDcvfu12pKMcZBhj3iIPoTduC22PNjIV+YXdW6lOx32pz9Yvj
|
||||||
|
tYNmeeWb/n2w6D8iZUsw1s94uwABUgKn70aB8/FKhyqC+RkhdycbGYdnl+JJX/Aj
|
||||||
|
F4QI+pjW0h5Yf1hM66q699YVzIhhBBgRCAAJBQJVI/9IAhsMAAoJEJ6mQpZ89XyK
|
||||||
|
itEA/1F7PMm1HyJxb6DXxRaBbsIL3sVm3OjK5XGuoyjSltsmAQCdIMfGn9ArSsEo
|
||||||
|
Euhilp9DguZPtN4dIl56gnM8abR0rQ==
|
||||||
|
=+pjN
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
95
javatests/google/registry/keyring/kms/pgp-public-keyring.asc
Normal file
95
javatests/google/registry/keyring/kms/pgp-public-keyring.asc
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
gpg -a --export --export-options export-minimal \
|
||||||
|
rde-unittest@registry.test \
|
||||||
|
rde-unittest-dsa@registry.test \
|
||||||
|
rde-unittest@escrow.test \
|
||||||
|
>javatests/google/registry/rde/testdata/pgp-public-keyring.asc
|
||||||
|
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: GnuPG v1
|
||||||
|
|
||||||
|
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
|
||||||
|
zInfxrmYRlOExWEu5eg9EAI=
|
||||||
|
=60ZZ
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -813,6 +813,18 @@ enum google.registry.model.reporting.HistoryEntry$Type {
|
||||||
RDE_IMPORT;
|
RDE_IMPORT;
|
||||||
SYNTHETIC;
|
SYNTHETIC;
|
||||||
}
|
}
|
||||||
|
class google.registry.model.server.KmsSecret {
|
||||||
|
@Id java.lang.String name;
|
||||||
|
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
|
||||||
|
com.googlecode.objectify.Key<google.registry.model.server.KmsSecretRevision> latestRevision;
|
||||||
|
}
|
||||||
|
class google.registry.model.server.KmsSecretRevision {
|
||||||
|
@Id long revisionKey;
|
||||||
|
@Parent com.googlecode.objectify.Key<google.registry.model.server.KmsSecret> parent;
|
||||||
|
google.registry.model.CreateAutoTimestamp creationTime;
|
||||||
|
java.lang.String encryptedValue;
|
||||||
|
java.lang.String kmsCryptoKeyVersionName;
|
||||||
|
}
|
||||||
class google.registry.model.server.Lock {
|
class google.registry.model.server.Lock {
|
||||||
@Id java.lang.String lockId;
|
@Id java.lang.String lockId;
|
||||||
java.util.LinkedHashSet<java.lang.String> queue;
|
java.util.LinkedHashSet<java.lang.String> queue;
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.model.server;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import google.registry.testing.AppEngineRule;
|
||||||
|
import google.registry.testing.ExceptionRule;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class KmsSecretRevisionTest {
|
||||||
|
|
||||||
|
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
||||||
|
|
||||||
|
@Rule public final ExceptionRule thrown = new ExceptionRule();
|
||||||
|
|
||||||
|
private KmsSecretRevision secretRevision;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
secretRevision =
|
||||||
|
persistResource(
|
||||||
|
new KmsSecretRevision.Builder()
|
||||||
|
.setKmsCryptoKeyVersionName("foo")
|
||||||
|
.setParent("bar")
|
||||||
|
.setEncryptedValue("blah")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setEncryptedValue_tooLong_throwsException() {
|
||||||
|
thrown.expect(IllegalArgumentException.class, "Secret is greater than 67108864 bytes");
|
||||||
|
secretRevision =
|
||||||
|
persistResource(
|
||||||
|
new KmsSecretRevision.Builder()
|
||||||
|
.setKmsCryptoKeyVersionName("foo")
|
||||||
|
.setParent("bar")
|
||||||
|
.setEncryptedValue(Strings.repeat("a", 64 * 1024 * 1024 + 1))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPersistence() {
|
||||||
|
assertThat(ofy().load().entity(secretRevision).now()).isEqualTo(secretRevision);
|
||||||
|
}
|
||||||
|
}
|
53
javatests/google/registry/model/server/KmsSecretTest.java
Normal file
53
javatests/google/registry/model/server/KmsSecretTest.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.model.server;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||||
|
|
||||||
|
import google.registry.testing.AppEngineRule;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class KmsSecretTest {
|
||||||
|
|
||||||
|
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
||||||
|
|
||||||
|
private KmsSecret secret;
|
||||||
|
private KmsSecretRevision secretRevision;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
secretRevision =
|
||||||
|
persistResource(
|
||||||
|
new KmsSecretRevision.Builder()
|
||||||
|
.setKmsCryptoKeyVersionName("foo")
|
||||||
|
.setParent("bar")
|
||||||
|
.setEncryptedValue("blah")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
secret = persistResource(KmsSecret.create("someSecret", secretRevision));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPersistence() {
|
||||||
|
assertThat(ofy().load().entity(secret).now()).isEqualTo(secret);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue