001// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.util;
006
007import java.security.NoSuchAlgorithmException;
008
009import org.bouncycastle.asn1.ASN1ObjectIdentifier;
010import org.bouncycastle.bcpg.ECPublicBCPGKey;
011import org.bouncycastle.openpgp.PGPPublicKey;
012
013public final class BCUtil {
014
015    private BCUtil() {
016
017    }
018
019    /**
020     * Utility method to get the bit strength of OpenPGP keys.
021     * Bouncycastle is lacking support for some keys (eg. EdDSA, X25519), so this method
022     * manually derives the bit strength from the keys curves OID.
023     *
024     * @param key key
025     * @return bit strength
026     */
027    public static int getBitStrength(PGPPublicKey key) throws NoSuchAlgorithmException {
028        int bitStrength = key.getBitStrength();
029
030        if (bitStrength == -1) {
031            // BC's PGPPublicKey.getBitStrength() does fail for some keys (EdDSA, X25519)
032            // therefore we manually set the bit strength.
033            // see https://github.com/bcgit/bc-java/issues/972
034
035            ASN1ObjectIdentifier oid = ((ECPublicBCPGKey) key.getPublicKeyPacket().getKey()).getCurveOID();
036            if (oid.getId().equals("1.3.6.1.4.1.11591.15.1")) {
037                // ed25519 is 256 bits
038                bitStrength = 256;
039            } else if (oid.getId().equals("1.3.6.1.4.1.3029.1.5.1")) {
040                // curvey25519 is 256 bits
041                bitStrength = 256;
042            } else {
043                throw new NoSuchAlgorithmException("Unknown curve: " + oid.getId());
044            }
045
046        }
047        return bitStrength;
048    }
049
050
051    /**
052     * A constant time equals comparison - does not terminate early if
053     * test will fail. For best results always pass the expected value
054     * as the first parameter.
055     *
056     * @param expected first array
057     * @param supplied second array
058     * @return true if arrays equal, false otherwise.
059     */
060    public static boolean constantTimeAreEqual(
061            char[]  expected,
062            char[]  supplied) {
063        if (expected == null || supplied == null) {
064            return false;
065        }
066
067        if (expected == supplied) {
068            return true;
069        }
070
071        int len = Math.min(expected.length, supplied.length);
072
073        int nonEqual = expected.length ^ supplied.length;
074
075        // do the char-wise comparison
076        for (int i = 0; i != len; i++) {
077            nonEqual |= (expected[i] ^ supplied[i]);
078        }
079        // If supplied is longer than expected, iterate over rest of supplied with NOPs
080        for (int i = len; i < supplied.length; i++) {
081            nonEqual |= ((byte) supplied[i] ^ (byte) ~supplied[i]);
082        }
083
084        return nonEqual == 0;
085    }
086
087}