google.registry.keyring.api.ComparatorKeyring.java Source code

Java tutorial

Introduction

Here is the source code for google.registry.keyring.api.ComparatorKeyring.java

Source

// 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.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.
 */
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);
    }

    // .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();
    }
}