mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 20:17:51 +02:00
Replace KeystoreKeyring with ComparatorKeyring between KeystoreKeyring and KmsKeystore. In the opensource version, will replace DummyKeyring with KmsKeyring directly. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=152893767
208 lines
6.8 KiB
Java
208 lines
6.8 KiB
Java
// 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;
|
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.base.MoreObjects;
|
|
|
|
import google.registry.util.ComparingInvocationHandler;
|
|
import google.registry.util.FormattingLogger;
|
|
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.lang.reflect.Method;
|
|
import java.util.Arrays;
|
|
import java.util.Objects;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import org.bouncycastle.bcpg.BCPGKey;
|
|
import org.bouncycastle.bcpg.PublicKeyPacket;
|
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
|
|
/**
|
|
* Checks that a second keyring returns the same result as the current one.
|
|
*
|
|
* <p>Will behave exactly like the "actualKeyring" - as in will throw / return the exact same values
|
|
* - no matter what the "secondKeyring" does. But will log a warning if "secondKeyring" acts
|
|
* differently than "actualKeyring".
|
|
*
|
|
* <p>If both keyrings threw exceptions, there is no check whether the exeptions are the same. The
|
|
* assumption is that an error happened in both, but they might report that error differently.
|
|
*/
|
|
public final class ComparatorKeyring extends ComparingInvocationHandler<Keyring> {
|
|
|
|
@VisibleForTesting
|
|
static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
|
|
|
private ComparatorKeyring(Keyring original, Keyring second) {
|
|
super(Keyring.class, original, second);
|
|
}
|
|
|
|
/**
|
|
* Returns an instance of Keyring that is an exact proxy of "original".
|
|
*
|
|
* <p>This proxy will log any differences in return value or thrown exceptions with "second".
|
|
*/
|
|
public static Keyring create(Keyring original, Keyring second) {
|
|
return new ComparatorKeyring(original, second).makeProxy();
|
|
}
|
|
|
|
@Override
|
|
protected void log(Method method, String message) {
|
|
logger.severefmt("ComparatorKeyring.%s: %s", method.getName(), message);
|
|
}
|
|
|
|
/** Implements equals for the PGP classes. */
|
|
@Override
|
|
protected boolean compareResults(Method method, @Nullable Object a, @Nullable Object b) {
|
|
Class<?> clazz = method.getReturnType();
|
|
if (PGPPublicKey.class.equals(clazz)) {
|
|
return compare((PGPPublicKey) a, (PGPPublicKey) b);
|
|
}
|
|
if (PGPPrivateKey.class.equals(clazz)) {
|
|
return compare((PGPPrivateKey) a, (PGPPrivateKey) b);
|
|
}
|
|
if (PGPKeyPair.class.equals(clazz)) {
|
|
return compare((PGPKeyPair) a, (PGPKeyPair) b);
|
|
}
|
|
return super.compareResults(method, a, b);
|
|
}
|
|
|
|
/** Implements toString for the PGP classes. */
|
|
@Override
|
|
protected String stringifyResult(Method method, @Nullable Object a) {
|
|
Class<?> clazz = method.getReturnType();
|
|
if (PGPPublicKey.class.equals(clazz)) {
|
|
return stringify((PGPPublicKey) a);
|
|
}
|
|
if (PGPPrivateKey.class.equals(clazz)) {
|
|
return stringify((PGPPrivateKey) a);
|
|
}
|
|
if (PGPKeyPair.class.equals(clazz)) {
|
|
return stringify((PGPKeyPair) a);
|
|
}
|
|
return super.stringifyResult(method, a);
|
|
}
|
|
|
|
@Override
|
|
protected String stringifyThrown(Method method, Throwable throwable) {
|
|
StringWriter stringWriter = new StringWriter();
|
|
PrintWriter printWriter = new PrintWriter(stringWriter);
|
|
throwable.printStackTrace(printWriter);
|
|
return String.format("%s\nStack trace:\n%s", throwable.toString(), stringWriter.toString());
|
|
}
|
|
|
|
// .equals implementation for PGP types.
|
|
|
|
@VisibleForTesting
|
|
static boolean compare(@Nullable PGPKeyPair a, @Nullable PGPKeyPair b) {
|
|
if (a == null || b == null) {
|
|
return a == null && b == null;
|
|
}
|
|
return compare(a.getPublicKey(), b.getPublicKey())
|
|
&& compare(a.getPrivateKey(), b.getPrivateKey());
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static boolean compare(@Nullable PGPPublicKey a, @Nullable PGPPublicKey b) {
|
|
if (a == null || b == null) {
|
|
return a == null && b == null;
|
|
}
|
|
try {
|
|
return Arrays.equals(a.getFingerprint(), b.getFingerprint())
|
|
&& Arrays.equals(a.getEncoded(), b.getEncoded());
|
|
} catch (IOException e) {
|
|
logger.severefmt("ComparatorKeyring error: PGPPublicKey.getEncoded failed: %s", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static boolean compare(@Nullable PGPPrivateKey a, @Nullable PGPPrivateKey b) {
|
|
if (a == null || b == null) {
|
|
return a == null && b == null;
|
|
}
|
|
return a.getKeyID() == b.getKeyID()
|
|
&& compare(a.getPrivateKeyDataPacket(), b.getPrivateKeyDataPacket())
|
|
&& compare(a.getPublicKeyPacket(), b.getPublicKeyPacket());
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static boolean compare(PublicKeyPacket a, PublicKeyPacket b) {
|
|
if (a == null || b == null) {
|
|
return a == null && b == null;
|
|
}
|
|
try {
|
|
return Arrays.equals(a.getEncoded(), b.getEncoded());
|
|
} catch (IOException e) {
|
|
logger.severefmt("ComparatorKeyring error: PublicKeyPacket.getEncoded failed: %s", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static boolean compare(BCPGKey a, BCPGKey b) {
|
|
if (a == null || b == null) {
|
|
return a == null && b == null;
|
|
}
|
|
return Objects.equals(a.getFormat(), b.getFormat())
|
|
&& Arrays.equals(a.getEncoded(), b.getEncoded());
|
|
}
|
|
|
|
// toString implementations
|
|
|
|
@VisibleForTesting
|
|
static String stringify(PGPKeyPair a) {
|
|
if (a == null) {
|
|
return "null";
|
|
}
|
|
return MoreObjects.toStringHelper(PGPKeyPair.class)
|
|
.addValue(stringify(a.getPublicKey()))
|
|
.addValue(stringify(a.getPrivateKey()))
|
|
.toString();
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static String stringify(PGPPublicKey a) {
|
|
if (a == null) {
|
|
return "null";
|
|
}
|
|
|
|
StringBuilder builder = new StringBuilder("");
|
|
for (byte b : a.getFingerprint()) {
|
|
builder.append(String.format("%02x:", b));
|
|
}
|
|
return MoreObjects.toStringHelper(PGPPublicKey.class)
|
|
.add("fingerprint", builder.toString())
|
|
.toString();
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static String stringify(PGPPrivateKey a) {
|
|
if (a == null) {
|
|
return "null";
|
|
}
|
|
|
|
// We need to be careful what information we output here. The private key should be private, and
|
|
// I'm not sure what is safe to put in the logs.
|
|
return MoreObjects.toStringHelper(PGPPrivateKey.class)
|
|
.add("keyId", a.getKeyID())
|
|
.toString();
|
|
}
|
|
}
|