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}