diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java index edc436365d..641e7505f9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java @@ -47,6 +47,7 @@ import org.bouncycastle.internal.asn1.oiw.ElGamalParameter; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; import org.bouncycastle.util.Arrays; /** @@ -62,7 +63,7 @@ public class PrivateKeyFactory * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) - throws IOException + throws IOException { if (privateKeyInfoData == null) { @@ -84,7 +85,7 @@ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(InputStream inStr) - throws IOException + throws IOException { return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); } @@ -97,7 +98,7 @@ public static AsymmetricKeyParameter createKey(InputStream inStr) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) - throws IOException + throws IOException { if (keyInfo == null) { @@ -107,23 +108,29 @@ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm(); ASN1ObjectIdentifier algOID = algId.getAlgorithm(); + // #tls-injection + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedAlgorithm(algOID)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createPrivateKeyParameter(keyInfo); + } + if (algOID.equals(PKCSObjectIdentifiers.rsaEncryption) - || algOID.equals(PKCSObjectIdentifiers.id_RSASSA_PSS) - || algOID.equals(X509ObjectIdentifiers.id_ea_rsa)) + || algOID.equals(PKCSObjectIdentifiers.id_RSASSA_PSS) + || algOID.equals(X509ObjectIdentifiers.id_ea_rsa)) { RSAPrivateKey keyStructure = RSAPrivateKey.getInstance(keyInfo.parsePrivateKey()); return new RSAPrivateCrtKeyParameters(keyStructure.getModulus(), - keyStructure.getPublicExponent(), keyStructure.getPrivateExponent(), - keyStructure.getPrime1(), keyStructure.getPrime2(), keyStructure.getExponent1(), - keyStructure.getExponent2(), keyStructure.getCoefficient()); + keyStructure.getPublicExponent(), keyStructure.getPrivateExponent(), + keyStructure.getPrime1(), keyStructure.getPrime2(), keyStructure.getExponent1(), + keyStructure.getExponent2(), keyStructure.getCoefficient()); } // TODO? // else if (algOID.equals(X9ObjectIdentifiers.dhpublicnumber)) else if (algOID.equals(PKCSObjectIdentifiers.dhKeyAgreement)) { DHParameter params = DHParameter.getInstance(algId.getParameters()); - ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey(); + ASN1Integer derX = (ASN1Integer) keyInfo.parsePrivateKey(); BigInteger lVal = params.getL(); int l = lVal == null ? 0 : lVal.intValue(); @@ -134,14 +141,14 @@ else if (algOID.equals(PKCSObjectIdentifiers.dhKeyAgreement)) else if (algOID.equals(OIWObjectIdentifiers.elGamalAlgorithm)) { ElGamalParameter params = ElGamalParameter.getInstance(algId.getParameters()); - ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey(); + ASN1Integer derX = (ASN1Integer) keyInfo.parsePrivateKey(); return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters( - params.getP(), params.getG())); + params.getP(), params.getG())); } else if (algOID.equals(X9ObjectIdentifiers.id_dsa)) { - ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey(); + ASN1Integer derX = (ASN1Integer) keyInfo.parsePrivateKey(); ASN1Encodable algParameters = algId.getParameters(); DSAParameters parameters = null; @@ -162,7 +169,7 @@ else if (algOID.equals(X9ObjectIdentifiers.id_ecPublicKey)) if (params.isNamedCurve()) { - ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) params.getParameters(); x9 = CustomNamedCurves.getByOID(oid); if (x9 == null) @@ -175,7 +182,7 @@ else if (algOID.equals(X9ObjectIdentifiers.id_ecPublicKey)) { x9 = X9ECParameters.getInstance(params.getParameters()); dParams = new ECDomainParameters( - x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed()); + x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed()); } ECPrivateKey ec = ECPrivateKey.getInstance(keyInfo.parsePrivateKey()); @@ -212,9 +219,9 @@ else if (algOID.equals(EdECObjectIdentifiers.id_Ed448)) return new Ed448PrivateKeyParameters(getRawKey(keyInfo)); } else if ( - algOID.equals(CryptoProObjectIdentifiers.gostR3410_2001) || - algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) || - algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256)) + algOID.equals(CryptoProObjectIdentifiers.gostR3410_2001) || + algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) || + algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256)) { ASN1Encodable algParameters = algId.getParameters(); GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(algParameters); @@ -226,11 +233,11 @@ else if ( X9ECParameters ecP = ECGOST3410NamedCurves.getByOIDX9(gostParams.getPublicKeyParamSet()); ecSpec = new ECGOST3410Parameters( - new ECNamedDomainParameters( - gostParams.getPublicKeyParamSet(), ecP), - gostParams.getPublicKeyParamSet(), - gostParams.getDigestParamSet(), - gostParams.getEncryptionParamSet()); + new ECNamedDomainParameters( + gostParams.getPublicKeyParamSet(), ecP), + gostParams.getPublicKeyParamSet(), + gostParams.getDigestParamSet(), + gostParams.getEncryptionParamSet()); int privateKeyLength = keyInfo.getPrivateKeyLength(); @@ -262,8 +269,8 @@ else if ( X9ECParameters ecP = ECNamedCurveTable.getByOID(oid); ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters(oid, ecP), - gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), - gostParams.getEncryptionParamSet()); + gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), + gostParams.getEncryptionParamSet()); } else if (params.isImplicitlyCA()) { @@ -273,8 +280,8 @@ else if (params.isImplicitlyCA()) { X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters(algOID, ecP), - gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), - gostParams.getEncryptionParamSet()); + gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet(), + gostParams.getEncryptionParamSet()); } ASN1Encodable privKey = keyInfo.parsePrivateKey(); @@ -294,12 +301,12 @@ else if (params.isImplicitlyCA()) } return new ECPrivateKeyParameters( - d, - new ECGOST3410Parameters( - ecSpec, - gostParams.getPublicKeyParamSet(), - gostParams.getDigestParamSet(), - gostParams.getEncryptionParamSet())); + d, + new ECGOST3410Parameters( + ecSpec, + gostParams.getPublicKeyParamSet(), + gostParams.getDigestParamSet(), + gostParams.getEncryptionParamSet())); } else diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java index 1b00473f5e..21fe06f9bd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java @@ -41,6 +41,7 @@ import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.FixedPointCombMultiplier; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; /** * Factory to create ASN.1 private key info objects from lightweight private keys. @@ -71,7 +72,7 @@ private PrivateKeyInfoFactory() * @throws java.io.IOException on an error encoding the key */ public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) - throws IOException + throws IOException { return createPrivateKeyInfo(privateKey, null); } @@ -84,29 +85,37 @@ public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter private * @return the appropriate PrivateKeyInfo * @throws java.io.IOException on an error encoding the key */ - public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) - throws IOException + public static PrivateKeyInfo createPrivateKeyInfo( + AsymmetricKeyParameter privateKey, + ASN1Set attributes) + throws IOException { + // #tls-injection + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedParameter(privateKey)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createPrivateKeyInfo(privateKey, attributes); + } + if (privateKey instanceof RSAKeyParameters) { - RSAPrivateCrtKeyParameters priv = (RSAPrivateCrtKeyParameters)privateKey; + RSAPrivateCrtKeyParameters priv = (RSAPrivateCrtKeyParameters) privateKey; return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), - new RSAPrivateKey(priv.getModulus(), priv.getPublicExponent(), priv.getExponent(), priv.getP(), priv.getQ(), priv.getDP(), priv.getDQ(), priv.getQInv()), - attributes); + new RSAPrivateKey(priv.getModulus(), priv.getPublicExponent(), priv.getExponent(), priv.getP(), priv.getQ(), priv.getDP(), priv.getDQ(), priv.getQInv()), + attributes); } else if (privateKey instanceof DSAPrivateKeyParameters) { - DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)privateKey; + DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters) privateKey; DSAParameters params = priv.getParameters(); return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, - new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()), - attributes); + new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()), + attributes); } else if (privateKey instanceof ECPrivateKeyParameters) { - ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey; + ECPrivateKeyParameters priv = (ECPrivateKeyParameters) privateKey; ECDomainParameters domainParams = priv.getParameters(); ASN1Encodable params; int orderBitLength; @@ -119,9 +128,9 @@ else if (privateKey instanceof ECPrivateKeyParameters) else if (domainParams instanceof ECGOST3410Parameters) { GOST3410PublicKeyAlgParameters gostParams = new GOST3410PublicKeyAlgParameters( - ((ECGOST3410Parameters)domainParams).getPublicKeyParamSet(), - ((ECGOST3410Parameters)domainParams).getDigestParamSet(), - ((ECGOST3410Parameters)domainParams).getEncryptionParamSet()); + ((ECGOST3410Parameters) domainParams).getPublicKeyParamSet(), + ((ECGOST3410Parameters) domainParams).getDigestParamSet(), + ((ECGOST3410Parameters) domainParams).getEncryptionParamSet()); int size; @@ -137,8 +146,8 @@ else if (domainParams instanceof ECGOST3410Parameters) boolean is512 = priv.getD().bitLength() > 256; identifier = (is512) ? - RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512 : - RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256; + RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512 : + RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256; size = (is512) ? 64 : 32; } byte[] encKey = new byte[size]; @@ -149,17 +158,17 @@ else if (domainParams instanceof ECGOST3410Parameters) } else if (domainParams instanceof ECNamedDomainParameters) { - params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName()); + params = new X962Parameters(((ECNamedDomainParameters) domainParams).getName()); orderBitLength = domainParams.getN().bitLength(); } else { X9ECParameters ecP = new X9ECParameters( - domainParams.getCurve(), - new X9ECPoint(domainParams.getG(), false), - domainParams.getN(), - domainParams.getH(), - domainParams.getSeed()); + domainParams.getCurve(), + new X9ECPoint(domainParams.getG(), false), + domainParams.getN(), + domainParams.getH(), + domainParams.getSeed()); params = new X962Parameters(ecP); orderBitLength = domainParams.getN().bitLength(); @@ -171,37 +180,37 @@ else if (domainParams instanceof ECNamedDomainParameters) DERBitString publicKey = new DERBitString(q.getEncoded(false)); return new PrivateKeyInfo( - new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), - new ECPrivateKey(orderBitLength, priv.getD(), publicKey, params), - attributes); + new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), + new ECPrivateKey(orderBitLength, priv.getD(), publicKey, params), + attributes); } else if (privateKey instanceof X448PrivateKeyParameters) { - X448PrivateKeyParameters key = (X448PrivateKeyParameters)privateKey; + X448PrivateKeyParameters key = (X448PrivateKeyParameters) privateKey; return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), - new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); + new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); } else if (privateKey instanceof X25519PrivateKeyParameters) { - X25519PrivateKeyParameters key = (X25519PrivateKeyParameters)privateKey; + X25519PrivateKeyParameters key = (X25519PrivateKeyParameters) privateKey; return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), - new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); + new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); } else if (privateKey instanceof Ed448PrivateKeyParameters) { - Ed448PrivateKeyParameters key = (Ed448PrivateKeyParameters)privateKey; + Ed448PrivateKeyParameters key = (Ed448PrivateKeyParameters) privateKey; return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), - new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); + new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); } else if (privateKey instanceof Ed25519PrivateKeyParameters) { - Ed25519PrivateKeyParameters key = (Ed25519PrivateKeyParameters)privateKey; + Ed25519PrivateKeyParameters key = (Ed25519PrivateKeyParameters) privateKey; return new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), - new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); + new DEROctetString(key.getEncoded()), attributes, key.generatePublicKey().getEncoded()); } else { @@ -210,7 +219,11 @@ else if (privateKey instanceof Ed25519PrivateKeyParameters) } - private static void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI) + private static void extractBytes( + byte[] encKey, + int size, + int offSet, + BigInteger bI) { byte[] val = bI.toByteArray(); if (val.length < size) diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java index a8c7055e48..a74155f205 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java @@ -63,6 +63,7 @@ import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; import org.bouncycastle.util.Arrays; /** @@ -103,7 +104,7 @@ public class PublicKeyFactory * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(byte[] keyInfoData) - throws IOException + throws IOException { if (keyInfoData == null) { @@ -124,7 +125,7 @@ public static AsymmetricKeyParameter createKey(byte[] keyInfoData) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(InputStream inStr) - throws IOException + throws IOException { return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); } @@ -137,7 +138,7 @@ public static AsymmetricKeyParameter createKey(InputStream inStr) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) - throws IOException + throws IOException { if (keyInfo == null) { @@ -154,8 +155,10 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) * @return the appropriate key parameter * @throws IOException on an error decoding the key */ - public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + public static AsymmetricKeyParameter createKey( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { if (keyInfo == null) { @@ -163,8 +166,15 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Obj } AlgorithmIdentifier algID = keyInfo.getAlgorithm(); + ASN1ObjectIdentifier algOID = algID.getAlgorithm(); - SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algID.getAlgorithm()); + // #tls-injection + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedAlgorithm((algOID))) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createPublicKeyParameter(keyInfo, defaultParams); + } + + SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter) converters.get(algID.getAlgorithm()); if (null == converter) { throw new IOException("algorithm identifier in public key not recognised: " + algID.getAlgorithm()); @@ -175,15 +185,19 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Obj private static abstract class SubjectPublicKeyInfoConverter { - abstract AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException; + abstract AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException; } private static class RSAConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { RSAPublicKey pubKey = RSAPublicKey.getInstance(keyInfo.parsePublicKey()); @@ -192,10 +206,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class DHPublicNumberConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { DHPublicKey dhPublicKey = DHPublicKey.getInstance(keyInfo.parsePublicKey()); @@ -230,13 +246,15 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class DHAgreementConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { DHParameter params = DHParameter.getInstance(keyInfo.getAlgorithm().getParameters()); - ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey(); + ASN1Integer derY = (ASN1Integer) keyInfo.parsePublicKey(); BigInteger lVal = params.getL(); int l = lVal == null ? 0 : lVal.intValue(); @@ -247,26 +265,30 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class ElGamalConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { ElGamalParameter params = ElGamalParameter.getInstance(keyInfo.getAlgorithm().getParameters()); - ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey(); + ASN1Integer derY = (ASN1Integer) keyInfo.parsePublicKey(); return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters( - params.getP(), params.getG())); + params.getP(), params.getG())); } } private static class DSAConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { - ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey(); + ASN1Integer derY = (ASN1Integer) keyInfo.parsePublicKey(); ASN1Encodable de = keyInfo.getAlgorithm().getParameters(); DSAParameters parameters = null; @@ -281,16 +303,18 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class ECConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { X962Parameters params = X962Parameters.getInstance(keyInfo.getAlgorithm().getParameters()); ECDomainParameters dParams; if (params.isNamedCurve()) { - ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) params.getParameters(); X9ECParameters x9 = CustomNamedCurves.getByOID(oid); if (x9 == null) @@ -301,7 +325,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } else if (params.isImplicitlyCA()) { - dParams = (ECDomainParameters)defaultParams; + dParams = (ECDomainParameters) defaultParams; } else { @@ -317,7 +341,7 @@ else if (params.isImplicitlyCA()) // extra octet string - the old extra embedded octet string // if (data[0] == 0x04 && data[1] == data.length - 2 - && (data[2] == 0x02 || data[2] == 0x03)) + && (data[2] == 0x02 || data[2] == 0x03)) { int qLength = new X9IntegerConverter().getByteLength(dParams.getCurve()); @@ -325,9 +349,8 @@ else if (params.isImplicitlyCA()) { try { - key = (ASN1OctetString)ASN1Primitive.fromByteArray(data); - } - catch (IOException ex) + key = (ASN1OctetString) ASN1Primitive.fromByteArray(data); + } catch (IOException ex) { throw new IllegalArgumentException("error recovering public key"); } @@ -341,9 +364,11 @@ else if (params.isImplicitlyCA()) } private static class GOST3410_2001Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { AlgorithmIdentifier algID = keyInfo.getAlgorithm(); // ASN1ObjectIdentifier algOid = algID.getAlgorithm(); @@ -351,17 +376,16 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje ASN1ObjectIdentifier publicKeyParamSet = gostParams.getPublicKeyParamSet(); ECGOST3410Parameters ecDomainParameters = new ECGOST3410Parameters( - new ECNamedDomainParameters(publicKeyParamSet, ECGOST3410NamedCurves.getByOIDX9(publicKeyParamSet)), - publicKeyParamSet, - gostParams.getDigestParamSet(), - gostParams.getEncryptionParamSet()); + new ECNamedDomainParameters(publicKeyParamSet, ECGOST3410NamedCurves.getByOIDX9(publicKeyParamSet)), + publicKeyParamSet, + gostParams.getDigestParamSet(), + gostParams.getEncryptionParamSet()); ASN1OctetString key; try { - key = (ASN1OctetString)keyInfo.parsePublicKey(); - } - catch (IOException ex) + key = (ASN1OctetString) keyInfo.parsePublicKey(); + } catch (IOException ex) { throw new IllegalArgumentException("error recovering GOST3410_2001 public key"); } @@ -390,9 +414,11 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class GOST3410_2012Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { AlgorithmIdentifier algID = keyInfo.getAlgorithm(); ASN1ObjectIdentifier algOid = algID.getAlgorithm(); @@ -400,17 +426,16 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje ASN1ObjectIdentifier publicKeyParamSet = gostParams.getPublicKeyParamSet(); ECGOST3410Parameters ecDomainParameters = new ECGOST3410Parameters( - new ECNamedDomainParameters(publicKeyParamSet, ECGOST3410NamedCurves.getByOIDX9(publicKeyParamSet)), - publicKeyParamSet, - gostParams.getDigestParamSet(), - gostParams.getEncryptionParamSet()); + new ECNamedDomainParameters(publicKeyParamSet, ECGOST3410NamedCurves.getByOIDX9(publicKeyParamSet)), + publicKeyParamSet, + gostParams.getDigestParamSet(), + gostParams.getEncryptionParamSet()); ASN1OctetString key; try { - key = (ASN1OctetString)keyInfo.parsePublicKey(); - } - catch (IOException ex) + key = (ASN1OctetString) keyInfo.parsePublicKey(); + } catch (IOException ex) { throw new IllegalArgumentException("error recovering GOST3410_2012 public key"); } @@ -443,10 +468,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class DSTUConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { AlgorithmIdentifier algID = keyInfo.getAlgorithm(); ASN1ObjectIdentifier algOid = algID.getAlgorithm(); @@ -455,9 +482,8 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje ASN1OctetString key; try { - key = (ASN1OctetString)keyInfo.parsePublicKey(); - } - catch (IOException ex) + key = (ASN1OctetString) keyInfo.parsePublicKey(); + } catch (IOException ex) { throw new IllegalArgumentException("error recovering DSTU public key"); } @@ -513,42 +539,52 @@ private void reverseBytes(byte[] bytes) } private static class X25519Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams)); } } private static class X448Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams)); } } private static class Ed25519Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams)); } } private static class Ed448Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams)); } } - private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams) + private static byte[] getRawKey( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) { /* * TODO[RFC 8422] diff --git a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java index 3ade492281..c970516b66 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java @@ -36,6 +36,8 @@ import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; + /** * Factory to create ASN.1 subject public key info objects from lightweight public keys. */ @@ -65,17 +67,23 @@ private SubjectPublicKeyInfoFactory() * @throws java.io.IOException on an error encoding the key */ public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) - throws IOException + throws IOException { + // #tls-injection: + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedParameter(publicKey)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createSubjectPublicKeyInfo(publicKey); + } + if (publicKey instanceof RSAKeyParameters) { - RSAKeyParameters pub = (RSAKeyParameters)publicKey; + RSAKeyParameters pub = (RSAKeyParameters) publicKey; return new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKey(pub.getModulus(), pub.getExponent())); } else if (publicKey instanceof DSAPublicKeyParameters) { - DSAPublicKeyParameters pub = (DSAPublicKeyParameters)publicKey; + DSAPublicKeyParameters pub = (DSAPublicKeyParameters) publicKey; DSAParameter params = null; DSAParameters dsaParams = pub.getParameters(); @@ -88,7 +96,7 @@ else if (publicKey instanceof DSAPublicKeyParameters) } else if (publicKey instanceof ECPublicKeyParameters) { - ECPublicKeyParameters pub = (ECPublicKeyParameters)publicKey; + ECPublicKeyParameters pub = (ECPublicKeyParameters) publicKey; ECDomainParameters domainParams = pub.getParameters(); ASN1Encodable params; @@ -98,7 +106,7 @@ else if (publicKey instanceof ECPublicKeyParameters) } else if (domainParams instanceof ECGOST3410Parameters) { - ECGOST3410Parameters gostParams = (ECGOST3410Parameters)domainParams; + ECGOST3410Parameters gostParams = (ECGOST3410Parameters) domainParams; BigInteger bX = pub.getQ().getAffineXCoord().toBigInteger(); BigInteger bY = pub.getQ().getAffineYCoord().toBigInteger(); @@ -140,25 +148,24 @@ else if (domainParams instanceof ECGOST3410Parameters) try { return new SubjectPublicKeyInfo(new AlgorithmIdentifier(algIdentifier, params), new DEROctetString(encKey)); - } - catch (IOException e) + } catch (IOException e) { return null; } } else if (domainParams instanceof ECNamedDomainParameters) { - params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName()); + params = new X962Parameters(((ECNamedDomainParameters) domainParams).getName()); } else { X9ECParameters ecP = new X9ECParameters( - domainParams.getCurve(), - // TODO Support point compression - new X9ECPoint(domainParams.getG(), false), - domainParams.getN(), - domainParams.getH(), - domainParams.getSeed()); + domainParams.getCurve(), + // TODO Support point compression + new X9ECPoint(domainParams.getG(), false), + domainParams.getN(), + domainParams.getH(), + domainParams.getSeed()); params = new X962Parameters(ecP); } @@ -170,25 +177,25 @@ else if (domainParams instanceof ECNamedDomainParameters) } else if (publicKey instanceof X448PublicKeyParameters) { - X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey; + X448PublicKeyParameters key = (X448PublicKeyParameters) publicKey; return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.getEncoded()); } else if (publicKey instanceof X25519PublicKeyParameters) { - X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey; + X25519PublicKeyParameters key = (X25519PublicKeyParameters) publicKey; return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.getEncoded()); } else if (publicKey instanceof Ed448PublicKeyParameters) { - Ed448PublicKeyParameters key = (Ed448PublicKeyParameters)publicKey; + Ed448PublicKeyParameters key = (Ed448PublicKeyParameters) publicKey; return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), key.getEncoded()); } else if (publicKey instanceof Ed25519PublicKeyParameters) { - Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters)publicKey; + Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters) publicKey; return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.getEncoded()); } @@ -198,7 +205,11 @@ else if (publicKey instanceof Ed25519PublicKeyParameters) } } - private static void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI) + private static void extractBytes( + byte[] encKey, + int size, + int offSet, + BigInteger bI) { byte[] val = bI.toByteArray(); if (val.length < size) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index 5442f6c2ce..7e9568a3ee 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -17,17 +17,7 @@ import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.pqc.asn1.CMCEPrivateKey; -import org.bouncycastle.pqc.asn1.FalconPrivateKey; -import org.bouncycastle.pqc.asn1.McElieceCCA2PrivateKey; -import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers; -import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams; -import org.bouncycastle.pqc.asn1.SPHINCSPLUSPrivateKey; -import org.bouncycastle.pqc.asn1.SPHINCSPLUSPublicKey; -import org.bouncycastle.pqc.asn1.XMSSKeyParams; -import org.bouncycastle.pqc.asn1.XMSSMTKeyParams; -import org.bouncycastle.pqc.asn1.XMSSMTPrivateKey; -import org.bouncycastle.pqc.asn1.XMSSPrivateKey; +import org.bouncycastle.pqc.asn1.*; import org.bouncycastle.pqc.crypto.bike.BIKEParameters; import org.bouncycastle.pqc.crypto.bike.BIKEPrivateKeyParameters; import org.bouncycastle.pqc.crypto.cmce.CMCEParameters; @@ -70,6 +60,7 @@ import org.bouncycastle.pqc.crypto.xmss.XMSSUtil; import org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceCCA2PrivateKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPrivateKeyParameters; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; @@ -86,7 +77,7 @@ public class PrivateKeyFactory * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) - throws IOException + throws IOException { if (privateKeyInfoData == null) { @@ -108,7 +99,7 @@ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(InputStream inStr) - throws IOException + throws IOException { return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); } @@ -121,7 +112,7 @@ public static AsymmetricKeyParameter createKey(InputStream inStr) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) - throws IOException + throws IOException { if (keyInfo == null) { @@ -131,6 +122,12 @@ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm(); ASN1ObjectIdentifier algOID = algId.getAlgorithm(); + // #tls-injection: + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedAlgorithm(algOID)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createPrivateKeyParameter(keyInfo); + } + if (algOID.on(PQCObjectIdentifiers.qTESLA)) { ASN1OctetString qTESLAPriv = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()); @@ -140,7 +137,7 @@ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) else if (algOID.equals(PQCObjectIdentifiers.sphincs256)) { return new SPHINCSPrivateKeyParameters(ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets(), - Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(algId.getParameters()))); + Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(algId.getParameters()))); } else if (algOID.equals(PQCObjectIdentifiers.newHope)) { @@ -182,7 +179,7 @@ else if (algOID.on(BCObjectIdentifiers.sphincsPlus) || algOID.on(BCObjectIdentif SPHINCSPLUSPrivateKey spKey = SPHINCSPLUSPrivateKey.getInstance(obj); SPHINCSPLUSPublicKey publicKey = spKey.getPublicKey(); return new SPHINCSPlusPrivateKeyParameters(spParams, spKey.getSkseed(), spKey.getSkprf(), - publicKey.getPkseed(), publicKey.getPkroot()); + publicKey.getPkseed(), publicKey.getPkroot()); } else { @@ -238,10 +235,10 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_ntrulprime)) NTRULPRimeParameters spParams = Utils.ntrulprimeParamsLookup(algOID); return new NTRULPRimePrivateKeyParameters(spParams, - ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets()); + ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets()); } else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) { @@ -250,14 +247,14 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) SNTRUPrimeParameters spParams = Utils.sntruprimeParamsLookup(algOID); return new SNTRUPrimePrivateKeyParameters(spParams, - ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(4)).getOctets()); + ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(4)).getOctets()); } else if (algOID.equals(BCObjectIdentifiers.dilithium2) - || algOID.equals(BCObjectIdentifiers.dilithium3) || algOID.equals(BCObjectIdentifiers.dilithium5)) + || algOID.equals(BCObjectIdentifiers.dilithium3) || algOID.equals(BCObjectIdentifiers.dilithium5)) { ASN1Encodable keyObj = keyInfo.parsePrivateKey(); DilithiumParameters spParams = Utils.dilithiumParamsLookup(algOID); @@ -277,24 +274,24 @@ else if (algOID.equals(BCObjectIdentifiers.dilithium2) DilithiumPublicKeyParameters pubParams = PublicKeyFactory.DilithiumConverter.getPublicKeyParams(spParams, keyInfo.getPublicKeyData()); return new DilithiumPrivateKeyParameters(spParams, - ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), - pubParams.getT1()); // encT1 + ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), + pubParams.getT1()); // encT1 } else { return new DilithiumPrivateKeyParameters(spParams, - ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), - null); + ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), + null); } } else if (keyObj instanceof DEROctetString) @@ -353,12 +350,12 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss)) try { XMSSPrivateKeyParameters.Builder keyBuilder = new XMSSPrivateKeyParameters - .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest))) - .withIndex(xmssPrivateKey.getIndex()) - .withSecretKeySeed(xmssPrivateKey.getSecretKeySeed()) - .withSecretKeyPRF(xmssPrivateKey.getSecretKeyPRF()) - .withPublicSeed(xmssPrivateKey.getPublicSeed()) - .withRoot(xmssPrivateKey.getRoot()); + .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest))) + .withIndex(xmssPrivateKey.getIndex()) + .withSecretKeySeed(xmssPrivateKey.getSecretKeySeed()) + .withSecretKeyPRF(xmssPrivateKey.getSecretKeyPRF()) + .withPublicSeed(xmssPrivateKey.getPublicSeed()) + .withRoot(xmssPrivateKey.getRoot()); if (xmssPrivateKey.getVersion() != 0) { @@ -367,13 +364,12 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss)) if (xmssPrivateKey.getBdsState() != null) { - BDS bds = (BDS)XMSSUtil.deserialize(xmssPrivateKey.getBdsState(), BDS.class); + BDS bds = (BDS) XMSSUtil.deserialize(xmssPrivateKey.getBdsState(), BDS.class); keyBuilder.withBDSState(bds.withWOTSDigest(treeDigest)); } return keyBuilder.build(); - } - catch (ClassNotFoundException e) + } catch (ClassNotFoundException e) { throw new IOException("ClassNotFoundException processing BDS state: " + e.getMessage()); } @@ -388,12 +384,12 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss_mt)) XMSSMTPrivateKey xmssMtPrivateKey = XMSSMTPrivateKey.getInstance(keyInfo.parsePrivateKey()); XMSSMTPrivateKeyParameters.Builder keyBuilder = new XMSSMTPrivateKeyParameters - .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest))) - .withIndex(xmssMtPrivateKey.getIndex()) - .withSecretKeySeed(xmssMtPrivateKey.getSecretKeySeed()) - .withSecretKeyPRF(xmssMtPrivateKey.getSecretKeyPRF()) - .withPublicSeed(xmssMtPrivateKey.getPublicSeed()) - .withRoot(xmssMtPrivateKey.getRoot()); + .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest))) + .withIndex(xmssMtPrivateKey.getIndex()) + .withSecretKeySeed(xmssMtPrivateKey.getSecretKeySeed()) + .withSecretKeyPRF(xmssMtPrivateKey.getSecretKeyPRF()) + .withPublicSeed(xmssMtPrivateKey.getPublicSeed()) + .withRoot(xmssMtPrivateKey.getRoot()); if (xmssMtPrivateKey.getVersion() != 0) { @@ -402,13 +398,12 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss_mt)) if (xmssMtPrivateKey.getBdsState() != null) { - BDSStateMap bdsState = (BDSStateMap)XMSSUtil.deserialize(xmssMtPrivateKey.getBdsState(), BDSStateMap.class); + BDSStateMap bdsState = (BDSStateMap) XMSSUtil.deserialize(xmssMtPrivateKey.getBdsState(), BDSStateMap.class); keyBuilder.withBDSState(bdsState.withWOTSDigest(treeDigest)); } return keyBuilder.build(); - } - catch (ClassNotFoundException e) + } catch (ClassNotFoundException e) { throw new IOException("ClassNotFoundException processing BDS state: " + e.getMessage()); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index 1c97591500..f637be5ece 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -10,17 +10,7 @@ import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.pqc.asn1.CMCEPrivateKey; -import org.bouncycastle.pqc.asn1.CMCEPublicKey; -import org.bouncycastle.pqc.asn1.FalconPrivateKey; -import org.bouncycastle.pqc.asn1.FalconPublicKey; -import org.bouncycastle.pqc.asn1.McElieceCCA2PrivateKey; -import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers; -import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams; -import org.bouncycastle.pqc.asn1.XMSSKeyParams; -import org.bouncycastle.pqc.asn1.XMSSMTKeyParams; -import org.bouncycastle.pqc.asn1.XMSSMTPrivateKey; -import org.bouncycastle.pqc.asn1.XMSSPrivateKey; +import org.bouncycastle.pqc.asn1.*; import org.bouncycastle.pqc.crypto.bike.BIKEPrivateKeyParameters; import org.bouncycastle.pqc.crypto.cmce.CMCEPrivateKeyParameters; import org.bouncycastle.pqc.crypto.crystals.dilithium.DilithiumPrivateKeyParameters; @@ -48,6 +38,7 @@ import org.bouncycastle.pqc.crypto.xmss.XMSSUtil; import org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceCCA2PrivateKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPrivateKeyParameters; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; import org.bouncycastle.util.Pack; /** @@ -79,11 +70,19 @@ public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter private * @return the appropriate PrivateKeyInfo * @throws java.io.IOException on an error encoding the key */ - public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) throws IOException + public static PrivateKeyInfo createPrivateKeyInfo( + AsymmetricKeyParameter privateKey, + ASN1Set attributes) throws IOException { + // #tls-injection: + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedParameter(privateKey)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createPrivateKeyInfo(privateKey, attributes); + } + if (privateKey instanceof QTESLAPrivateKeyParameters) { - QTESLAPrivateKeyParameters keyParams = (QTESLAPrivateKeyParameters)privateKey; + QTESLAPrivateKeyParameters keyParams = (QTESLAPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = Utils.qTeslaLookupAlgID(keyParams.getSecurityCategory()); @@ -91,15 +90,15 @@ public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter private } else if (privateKey instanceof SPHINCSPrivateKeyParameters) { - SPHINCSPrivateKeyParameters params = (SPHINCSPrivateKeyParameters)privateKey; + SPHINCSPrivateKeyParameters params = (SPHINCSPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256, - new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest()))); + new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest()))); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getKeyData())); } else if (privateKey instanceof NHPrivateKeyParameters) { - NHPrivateKeyParameters params = (NHPrivateKeyParameters)privateKey; + NHPrivateKeyParameters params = (NHPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope); @@ -115,7 +114,7 @@ else if (privateKey instanceof NHPrivateKeyParameters) } else if (privateKey instanceof LMSPrivateKeyParameters) { - LMSPrivateKeyParameters params = (LMSPrivateKeyParameters)privateKey; + LMSPrivateKeyParameters params = (LMSPrivateKeyParameters) privateKey; byte[] encoding = Composer.compose().u32str(1).bytes(params).build(); byte[] pubEncoding = Composer.compose().u32str(1).bytes(params.getPublicKey()).build(); @@ -125,7 +124,7 @@ else if (privateKey instanceof LMSPrivateKeyParameters) } else if (privateKey instanceof HSSPrivateKeyParameters) { - HSSPrivateKeyParameters params = (HSSPrivateKeyParameters)privateKey; + HSSPrivateKeyParameters params = (HSSPrivateKeyParameters) privateKey; byte[] encoding = Composer.compose().u32str(params.getL()).bytes(params).build(); byte[] pubEncoding = Composer.compose().u32str(params.getL()).bytes(params.getPublicKey().getLMSPublicKey()).build(); @@ -135,7 +134,7 @@ else if (privateKey instanceof HSSPrivateKeyParameters) } else if (privateKey instanceof SPHINCSPlusPrivateKeyParameters) { - SPHINCSPlusPrivateKeyParameters params = (SPHINCSPlusPrivateKeyParameters)privateKey; + SPHINCSPlusPrivateKeyParameters params = (SPHINCSPlusPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.sphincsPlusOidLookup(params.getParameters())); @@ -143,7 +142,7 @@ else if (privateKey instanceof SPHINCSPlusPrivateKeyParameters) } else if (privateKey instanceof PicnicPrivateKeyParameters) { - PicnicPrivateKeyParameters params = (PicnicPrivateKeyParameters)privateKey; + PicnicPrivateKeyParameters params = (PicnicPrivateKeyParameters) privateKey; byte[] encoding = params.getEncoded(); @@ -152,7 +151,7 @@ else if (privateKey instanceof PicnicPrivateKeyParameters) } else if (privateKey instanceof CMCEPrivateKeyParameters) { - CMCEPrivateKeyParameters params = (CMCEPrivateKeyParameters)privateKey; + CMCEPrivateKeyParameters params = (CMCEPrivateKeyParameters) privateKey; //todo either make CMCEPrivateKey split the parameters from the private key or // (current) Make CMCEPrivateKey take parts of the private key splitted in the params @@ -165,25 +164,25 @@ else if (privateKey instanceof CMCEPrivateKeyParameters) } else if (privateKey instanceof XMSSPrivateKeyParameters) { - XMSSPrivateKeyParameters keyParams = (XMSSPrivateKeyParameters)privateKey; + XMSSPrivateKeyParameters keyParams = (XMSSPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss, - new XMSSKeyParams(keyParams.getParameters().getHeight(), - Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); + new XMSSKeyParams(keyParams.getParameters().getHeight(), + Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); return new PrivateKeyInfo(algorithmIdentifier, xmssCreateKeyStructure(keyParams), attributes); } else if (privateKey instanceof XMSSMTPrivateKeyParameters) { - XMSSMTPrivateKeyParameters keyParams = (XMSSMTPrivateKeyParameters)privateKey; + XMSSMTPrivateKeyParameters keyParams = (XMSSMTPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss_mt, - new XMSSMTKeyParams(keyParams.getParameters().getHeight(), keyParams.getParameters().getLayers(), - Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); + new XMSSMTKeyParams(keyParams.getParameters().getHeight(), keyParams.getParameters().getLayers(), + Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); return new PrivateKeyInfo(algorithmIdentifier, xmssmtCreateKeyStructure(keyParams), attributes); } else if (privateKey instanceof McElieceCCA2PrivateKeyParameters) { - McElieceCCA2PrivateKeyParameters priv = (McElieceCCA2PrivateKeyParameters)privateKey; + McElieceCCA2PrivateKeyParameters priv = (McElieceCCA2PrivateKeyParameters) privateKey; McElieceCCA2PrivateKey mcEliecePriv = new McElieceCCA2PrivateKey(priv.getN(), priv.getK(), priv.getField(), priv.getGoppaPoly(), priv.getP(), Utils.getAlgorithmIdentifier(priv.getDigest())); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.mcElieceCca2); @@ -191,7 +190,7 @@ else if (privateKey instanceof McElieceCCA2PrivateKeyParameters) } else if (privateKey instanceof FrodoPrivateKeyParameters) { - FrodoPrivateKeyParameters params = (FrodoPrivateKeyParameters)privateKey; + FrodoPrivateKeyParameters params = (FrodoPrivateKeyParameters) privateKey; byte[] encoding = params.getEncoded(); @@ -201,7 +200,7 @@ else if (privateKey instanceof FrodoPrivateKeyParameters) } else if (privateKey instanceof SABERPrivateKeyParameters) { - SABERPrivateKeyParameters params = (SABERPrivateKeyParameters)privateKey; + SABERPrivateKeyParameters params = (SABERPrivateKeyParameters) privateKey; byte[] encoding = params.getEncoded(); @@ -211,7 +210,7 @@ else if (privateKey instanceof SABERPrivateKeyParameters) } else if (privateKey instanceof NTRUPrivateKeyParameters) { - NTRUPrivateKeyParameters params = (NTRUPrivateKeyParameters)privateKey; + NTRUPrivateKeyParameters params = (NTRUPrivateKeyParameters) privateKey; byte[] encoding = params.getEncoded(); @@ -221,7 +220,7 @@ else if (privateKey instanceof NTRUPrivateKeyParameters) } else if (privateKey instanceof FalconPrivateKeyParameters) { - FalconPrivateKeyParameters params = (FalconPrivateKeyParameters)privateKey; + FalconPrivateKeyParameters params = (FalconPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.falconOidLookup(params.getParameters())); @@ -232,15 +231,15 @@ else if (privateKey instanceof FalconPrivateKeyParameters) } else if (privateKey instanceof KyberPrivateKeyParameters) { - KyberPrivateKeyParameters params = (KyberPrivateKeyParameters)privateKey; - + KyberPrivateKeyParameters params = (KyberPrivateKeyParameters) privateKey; + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.kyberOidLookup(params.getParameters())); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes); } else if (privateKey instanceof NTRULPRimePrivateKeyParameters) { - NTRULPRimePrivateKeyParameters params = (NTRULPRimePrivateKeyParameters)privateKey; + NTRULPRimePrivateKeyParameters params = (NTRULPRimePrivateKeyParameters) privateKey; ASN1EncodableVector v = new ASN1EncodableVector(); @@ -255,7 +254,7 @@ else if (privateKey instanceof NTRULPRimePrivateKeyParameters) } else if (privateKey instanceof SNTRUPrimePrivateKeyParameters) { - SNTRUPrimePrivateKeyParameters params = (SNTRUPrimePrivateKeyParameters)privateKey; + SNTRUPrimePrivateKeyParameters params = (SNTRUPrimePrivateKeyParameters) privateKey; ASN1EncodableVector v = new ASN1EncodableVector(); @@ -271,7 +270,7 @@ else if (privateKey instanceof SNTRUPrimePrivateKeyParameters) } else if (privateKey instanceof DilithiumPrivateKeyParameters) { - DilithiumPrivateKeyParameters params = (DilithiumPrivateKeyParameters)privateKey; + DilithiumPrivateKeyParameters params = (DilithiumPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.dilithiumOidLookup(params.getParameters())); @@ -281,21 +280,21 @@ else if (privateKey instanceof DilithiumPrivateKeyParameters) } else if (privateKey instanceof BIKEPrivateKeyParameters) { - BIKEPrivateKeyParameters params = (BIKEPrivateKeyParameters)privateKey; + BIKEPrivateKeyParameters params = (BIKEPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.bikeOidLookup(params.getParameters())); byte[] encoding = params.getEncoded(); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); } else if (privateKey instanceof HQCPrivateKeyParameters) { - HQCPrivateKeyParameters params = (HQCPrivateKeyParameters)privateKey; + HQCPrivateKeyParameters params = (HQCPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.hqcOidLookup(params.getParameters())); byte[] encoding = params.getEncoded(); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); } else if (privateKey instanceof RainbowPrivateKeyParameters) { - RainbowPrivateKeyParameters params = (RainbowPrivateKeyParameters)privateKey; + RainbowPrivateKeyParameters params = (RainbowPrivateKeyParameters) privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.rainbowOidLookup(params.getParameters())); byte[] encoding = params.getEncoded(); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); @@ -307,7 +306,7 @@ else if (privateKey instanceof RainbowPrivateKeyParameters) } private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters keyParams) - throws IOException + throws IOException { byte[] keyData = keyParams.getEncoded(); @@ -320,7 +319,7 @@ private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters ke int rootSize = n; int position = 0; - int index = (int)XMSSUtil.bytesToXBigEndian(keyData, position, indexSize); + int index = (int) XMSSUtil.bytesToXBigEndian(keyData, position, indexSize); if (!XMSSUtil.isIndexValid(totalHeight, index)) { throw new IllegalArgumentException("index out of bounds"); @@ -334,14 +333,13 @@ private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters ke position += publicSeedSize; byte[] root = XMSSUtil.extractBytesAtOffset(keyData, position, rootSize); position += rootSize; - /* import BDS state */ + /* import BDS state */ byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(keyData, position, keyData.length - position); BDS bds = null; try { - bds = (BDS)XMSSUtil.deserialize(bdsStateBinary, BDS.class); - } - catch (ClassNotFoundException e) + bds = (BDS) XMSSUtil.deserialize(bdsStateBinary, BDS.class); + } catch (ClassNotFoundException e) { throw new IOException("cannot parse BDS: " + e.getMessage()); } @@ -357,7 +355,7 @@ private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters ke } private static XMSSMTPrivateKey xmssmtCreateKeyStructure(XMSSMTPrivateKeyParameters keyParams) - throws IOException + throws IOException { byte[] keyData = keyParams.getEncoded(); @@ -370,7 +368,7 @@ private static XMSSMTPrivateKey xmssmtCreateKeyStructure(XMSSMTPrivateKeyParamet int rootSize = n; int position = 0; - int index = (int)XMSSUtil.bytesToXBigEndian(keyData, position, indexSize); + int index = (int) XMSSUtil.bytesToXBigEndian(keyData, position, indexSize); if (!XMSSUtil.isIndexValid(totalHeight, index)) { throw new IllegalArgumentException("index out of bounds"); @@ -384,14 +382,13 @@ private static XMSSMTPrivateKey xmssmtCreateKeyStructure(XMSSMTPrivateKeyParamet position += publicSeedSize; byte[] root = XMSSUtil.extractBytesAtOffset(keyData, position, rootSize); position += rootSize; - /* import BDS state */ + /* import BDS state */ byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(keyData, position, keyData.length - position); BDSStateMap bds = null; try { - bds = (BDSStateMap)XMSSUtil.deserialize(bdsStateBinary, BDSStateMap.class); - } - catch (ClassNotFoundException e) + bds = (BDSStateMap) XMSSUtil.deserialize(bdsStateBinary, BDSStateMap.class); + } catch (ClassNotFoundException e) { throw new IOException("cannot parse BDSStateMap: " + e.getMessage()); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index b5b0decbae..5b3fc3eb30 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -63,6 +63,7 @@ import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters; import org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceCCA2PublicKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPublicKeyParameters; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; @@ -138,7 +139,7 @@ public class PublicKeyFactory converters.put(BCObjectIdentifiers.sphincsPlus_shake_256s, new SPHINCSPlusConverter()); converters.put(BCObjectIdentifiers.sphincsPlus_shake_256f, new SPHINCSPlusConverter()); converters.put(new ASN1ObjectIdentifier("1.3.9999.6.4.10"), new SPHINCSPlusConverter()); - + converters.put(BCObjectIdentifiers.mceliece348864_r3, new CMCEConverter()); converters.put(BCObjectIdentifiers.mceliece348864f_r3, new CMCEConverter()); converters.put(BCObjectIdentifiers.mceliece460896_r3, new CMCEConverter()); @@ -239,7 +240,7 @@ public class PublicKeyFactory * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(byte[] keyInfoData) - throws IOException + throws IOException { if (keyInfoData == null) { @@ -260,7 +261,7 @@ public static AsymmetricKeyParameter createKey(byte[] keyInfoData) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(InputStream inStr) - throws IOException + throws IOException { return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); } @@ -273,7 +274,7 @@ public static AsymmetricKeyParameter createKey(InputStream inStr) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) - throws IOException + throws IOException { if (keyInfo == null) { @@ -290,8 +291,10 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) * @return the appropriate key parameter * @throws IOException on an error decoding the key */ - public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + public static AsymmetricKeyParameter createKey( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { if (keyInfo == null) { @@ -299,7 +302,15 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Obj } AlgorithmIdentifier algId = keyInfo.getAlgorithm(); - SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algId.getAlgorithm()); + ASN1ObjectIdentifier algOID = algId.getAlgorithm(); + + // #tls-injection: + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedAlgorithm(algOID)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createPublicKeyParameter(keyInfo, defaultParams); + } + + SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter) converters.get(algId.getAlgorithm()); if (converter != null) { @@ -313,46 +324,56 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Obj private static abstract class SubjectPublicKeyInfoConverter { - abstract AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException; + abstract AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException; } private static class QTeslaConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { return new QTESLAPublicKeyParameters(Utils.qTeslaLookupSecurityCategory(keyInfo.getAlgorithm()), keyInfo.getPublicKeyData().getOctets()); } } private static class SPHINCSConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { return new SPHINCSPublicKeyParameters(keyInfo.getPublicKeyData().getBytes(), - Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(keyInfo.getAlgorithm().getParameters()))); + Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(keyInfo.getAlgorithm().getParameters()))); } } private static class NHConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { return new NHPublicKeyParameters(keyInfo.getPublicKeyData().getBytes()); } } private static class XMSSConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { XMSSKeyParams keyParams = XMSSKeyParams.getInstance(keyInfo.getAlgorithm().getParameters()); @@ -362,26 +383,28 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje XMSSPublicKey xmssPublicKey = XMSSPublicKey.getInstance(keyInfo.parsePublicKey()); return new XMSSPublicKeyParameters - .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest))) - .withPublicSeed(xmssPublicKey.getPublicSeed()) - .withRoot(xmssPublicKey.getRoot()).build(); + .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest))) + .withPublicSeed(xmssPublicKey.getPublicSeed()) + .withRoot(xmssPublicKey.getRoot()).build(); } else { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); return new XMSSPublicKeyParameters - .Builder(XMSSParameters.lookupByOID(Pack.bigEndianToInt(keyEnc, 0))) - .withPublicKey(keyEnc).build(); + .Builder(XMSSParameters.lookupByOID(Pack.bigEndianToInt(keyEnc, 0))) + .withPublicKey(keyEnc).build(); } } } private static class XMSSMTConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { XMSSMTKeyParams keyParams = XMSSMTKeyParams.getInstance(keyInfo.getAlgorithm().getParameters()); @@ -392,26 +415,28 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje XMSSPublicKey xmssMtPublicKey = XMSSPublicKey.getInstance(keyInfo.parsePublicKey()); return new XMSSMTPublicKeyParameters - .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest))) - .withPublicSeed(xmssMtPublicKey.getPublicSeed()) - .withRoot(xmssMtPublicKey.getRoot()).build(); + .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest))) + .withPublicSeed(xmssMtPublicKey.getPublicSeed()) + .withRoot(xmssMtPublicKey.getRoot()).build(); } else { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); return new XMSSMTPublicKeyParameters - .Builder(XMSSMTParameters.lookupByOID(Pack.bigEndianToInt(keyEnc, 0))) - .withPublicKey(keyEnc).build(); + .Builder(XMSSMTParameters.lookupByOID(Pack.bigEndianToInt(keyEnc, 0))) + .withPublicKey(keyEnc).build(); } } } private static class LMSConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); @@ -432,10 +457,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class SPHINCSPlusConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { try { @@ -444,8 +471,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje SPHINCSPlusParameters spParams = Utils.sphincsPlusParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); return new SPHINCSPlusPublicKeyParameters(spParams, Arrays.copyOfRange(keyEnc, 4, keyEnc.length)); - } - catch (Exception e) + } catch (Exception e) { byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); @@ -457,10 +483,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class CMCEConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { try { @@ -469,9 +497,8 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje CMCEParameters spParams = Utils.mcElieceParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); return new CMCEPublicKeyParameters(spParams, keyEnc); - } - catch (Exception e) - { + } catch (Exception e) + { byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); CMCEParameters spParams = Utils.mcElieceParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); @@ -484,7 +511,9 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje private static class SABERConverter extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) throws IOException { byte[] keyEnc = ASN1OctetString.getInstance( @@ -497,10 +526,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class McElieceCCA2Converter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { McElieceCCA2PublicKey mKey = McElieceCCA2PublicKey.getInstance(keyInfo.parsePublicKey()); @@ -511,7 +542,9 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje private static class FrodoConverter extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); @@ -525,7 +558,9 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje private static class PicnicConverter extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); @@ -537,10 +572,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class NtruConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); @@ -551,10 +588,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class FalconConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); // FalconPublicKey falconPublicKey = FalconPublicKey.getInstance(keyInfo.parsePublicKey()); @@ -567,10 +606,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class KyberConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { KyberParameters kyberParameters = Utils.kyberParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); @@ -580,8 +621,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); return new KyberPublicKeyParameters(kyberParameters, kyberKey.getT(), kyberKey.getRho()); - } - catch (Exception e) + } catch (Exception e) { // we're a raw encoding return new KyberPublicKeyParameters(kyberParameters, keyInfo.getPublicKeyData().getOctets()); @@ -590,10 +630,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class NTRULPrimeConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); @@ -604,10 +646,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class SNTRUPrimeConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); @@ -616,19 +660,23 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje return new SNTRUPrimePublicKeyParameters(ntruLPRimeParams, keyEnc); } } - + static class DilithiumConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { DilithiumParameters dilithiumParams = Utils.dilithiumParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); return getPublicKeyParams(dilithiumParams, keyInfo.getPublicKeyData()); } - static DilithiumPublicKeyParameters getPublicKeyParams(DilithiumParameters dilithiumParams, ASN1BitString publicKeyData) + static DilithiumPublicKeyParameters getPublicKeyParams( + DilithiumParameters dilithiumParams, + ASN1BitString publicKeyData) { try { @@ -638,8 +686,8 @@ static DilithiumPublicKeyParameters getPublicKeyParams(DilithiumParameters dilit ASN1Sequence keySeq = ASN1Sequence.getInstance(obj); return new DilithiumPublicKeyParameters(dilithiumParams, - ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), - ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets()); + ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets()); } else { @@ -647,8 +695,7 @@ static DilithiumPublicKeyParameters getPublicKeyParams(DilithiumParameters dilit return new DilithiumPublicKeyParameters(dilithiumParams, encKey); } - } - catch (Exception e) + } catch (Exception e) { // we're a raw encoding return new DilithiumPublicKeyParameters(dilithiumParams, publicKeyData.getOctets()); @@ -659,7 +706,9 @@ static DilithiumPublicKeyParameters getPublicKeyParams(DilithiumParameters dilit private static class BIKEConverter extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) throws IOException { try @@ -669,8 +718,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje BIKEParameters bikeParams = Utils.bikeParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); return new BIKEPublicKeyParameters(bikeParams, keyEnc); - } - catch (Exception e) + } catch (Exception e) { byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); @@ -684,7 +732,9 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje private static class HQCConverter extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) throws IOException { try @@ -694,8 +744,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje HQCParameters hqcParams = Utils.hqcParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); return new HQCPublicKeyParameters(hqcParams, keyEnc); - } - catch (Exception e) + } catch (Exception e) { // raw encoding byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); @@ -708,10 +757,12 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } private static class RainbowConverter - extends SubjectPublicKeyInfoConverter + extends SubjectPublicKeyInfoConverter { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException + AsymmetricKeyParameter getPublicKeyParameters( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) + throws IOException { byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java index a6062e2ce2..ba5c687775 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java @@ -39,6 +39,7 @@ import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters; import org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceCCA2PublicKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPublicKeyParameters; +import org.bouncycastle.tls.injection.Asn1BridgeForInjectedSigAlgs; /** * Factory to create ASN.1 subject public key info objects from lightweight public keys. @@ -58,33 +59,39 @@ private SubjectPublicKeyInfoFactory() * @throws java.io.IOException on an error encoding the key */ public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) - throws IOException + throws IOException { + // #tls-injection: + if (Asn1BridgeForInjectedSigAlgs.theInstance().isSupportedParameter(publicKey)) + { + return Asn1BridgeForInjectedSigAlgs.theInstance().createSubjectPublicKeyInfo(publicKey); + } + if (publicKey instanceof QTESLAPublicKeyParameters) { - QTESLAPublicKeyParameters keyParams = (QTESLAPublicKeyParameters)publicKey; + QTESLAPublicKeyParameters keyParams = (QTESLAPublicKeyParameters) publicKey; AlgorithmIdentifier algorithmIdentifier = Utils.qTeslaLookupAlgID(keyParams.getSecurityCategory()); return new SubjectPublicKeyInfo(algorithmIdentifier, keyParams.getPublicData()); } else if (publicKey instanceof SPHINCSPublicKeyParameters) { - SPHINCSPublicKeyParameters params = (SPHINCSPublicKeyParameters)publicKey; + SPHINCSPublicKeyParameters params = (SPHINCSPublicKeyParameters) publicKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256, - new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest()))); + new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest()))); return new SubjectPublicKeyInfo(algorithmIdentifier, params.getKeyData()); } else if (publicKey instanceof NHPublicKeyParameters) { - NHPublicKeyParameters params = (NHPublicKeyParameters)publicKey; + NHPublicKeyParameters params = (NHPublicKeyParameters) publicKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope); return new SubjectPublicKeyInfo(algorithmIdentifier, params.getPubData()); } else if (publicKey instanceof LMSPublicKeyParameters) { - LMSPublicKeyParameters params = (LMSPublicKeyParameters)publicKey; + LMSPublicKeyParameters params = (LMSPublicKeyParameters) publicKey; byte[] encoding = Composer.compose().u32str(1).bytes(params).build(); @@ -93,7 +100,7 @@ else if (publicKey instanceof LMSPublicKeyParameters) } else if (publicKey instanceof HSSPublicKeyParameters) { - HSSPublicKeyParameters params = (HSSPublicKeyParameters)publicKey; + HSSPublicKeyParameters params = (HSSPublicKeyParameters) publicKey; byte[] encoding = Composer.compose().u32str(params.getL()).bytes(params.getLMSPublicKey()).build(); @@ -102,7 +109,7 @@ else if (publicKey instanceof HSSPublicKeyParameters) } else if (publicKey instanceof SPHINCSPlusPublicKeyParameters) { - SPHINCSPlusPublicKeyParameters params = (SPHINCSPlusPublicKeyParameters)publicKey; + SPHINCSPlusPublicKeyParameters params = (SPHINCSPlusPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); @@ -111,7 +118,7 @@ else if (publicKey instanceof SPHINCSPlusPublicKeyParameters) } else if (publicKey instanceof CMCEPublicKeyParameters) { - CMCEPublicKeyParameters params = (CMCEPublicKeyParameters)publicKey; + CMCEPublicKeyParameters params = (CMCEPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); @@ -121,7 +128,7 @@ else if (publicKey instanceof CMCEPublicKeyParameters) } else if (publicKey instanceof XMSSPublicKeyParameters) { - XMSSPublicKeyParameters keyParams = (XMSSPublicKeyParameters)publicKey; + XMSSPublicKeyParameters keyParams = (XMSSPublicKeyParameters) publicKey; byte[] publicSeed = keyParams.getPublicSeed(); byte[] root = keyParams.getRoot(); @@ -135,14 +142,14 @@ else if (publicKey instanceof XMSSPublicKeyParameters) else { AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss, - new XMSSKeyParams(keyParams.getParameters().getHeight(), Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); + new XMSSKeyParams(keyParams.getParameters().getHeight(), Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); return new SubjectPublicKeyInfo(algorithmIdentifier, new XMSSPublicKey(publicSeed, root)); } } else if (publicKey instanceof XMSSMTPublicKeyParameters) { - XMSSMTPublicKeyParameters keyParams = (XMSSMTPublicKeyParameters)publicKey; + XMSSMTPublicKeyParameters keyParams = (XMSSMTPublicKeyParameters) publicKey; byte[] publicSeed = keyParams.getPublicSeed(); byte[] root = keyParams.getRoot(); @@ -156,13 +163,13 @@ else if (publicKey instanceof XMSSMTPublicKeyParameters) else { AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss_mt, new XMSSMTKeyParams(keyParams.getParameters().getHeight(), keyParams.getParameters().getLayers(), - Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); + Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); return new SubjectPublicKeyInfo(algorithmIdentifier, new XMSSMTPublicKey(keyParams.getPublicSeed(), keyParams.getRoot())); } } else if (publicKey instanceof McElieceCCA2PublicKeyParameters) { - McElieceCCA2PublicKeyParameters pub = (McElieceCCA2PublicKeyParameters)publicKey; + McElieceCCA2PublicKeyParameters pub = (McElieceCCA2PublicKeyParameters) publicKey; McElieceCCA2PublicKey mcEliecePub = new McElieceCCA2PublicKey(pub.getN(), pub.getT(), pub.getG(), Utils.getAlgorithmIdentifier(pub.getDigest())); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.mcElieceCca2); @@ -170,7 +177,7 @@ else if (publicKey instanceof McElieceCCA2PublicKeyParameters) } else if (publicKey instanceof FrodoPublicKeyParameters) { - FrodoPublicKeyParameters params = (FrodoPublicKeyParameters)publicKey; + FrodoPublicKeyParameters params = (FrodoPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); @@ -180,17 +187,17 @@ else if (publicKey instanceof FrodoPublicKeyParameters) } else if (publicKey instanceof SABERPublicKeyParameters) { - SABERPublicKeyParameters params = (SABERPublicKeyParameters)publicKey; + SABERPublicKeyParameters params = (SABERPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.saberOidLookup(params.getParameters())); - + return new SubjectPublicKeyInfo(algorithmIdentifier, new DERSequence(new DEROctetString(encoding))); } else if (publicKey instanceof PicnicPublicKeyParameters) { - PicnicPublicKeyParameters params = (PicnicPublicKeyParameters)publicKey; + PicnicPublicKeyParameters params = (PicnicPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); @@ -199,7 +206,7 @@ else if (publicKey instanceof PicnicPublicKeyParameters) } else if (publicKey instanceof NTRUPublicKeyParameters) { - NTRUPublicKeyParameters params = (NTRUPublicKeyParameters)publicKey; + NTRUPublicKeyParameters params = (NTRUPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); @@ -208,20 +215,20 @@ else if (publicKey instanceof NTRUPublicKeyParameters) } else if (publicKey instanceof FalconPublicKeyParameters) { - FalconPublicKeyParameters params = (FalconPublicKeyParameters)publicKey; + FalconPublicKeyParameters params = (FalconPublicKeyParameters) publicKey; byte[] encoding = params.getH(); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.falconOidLookup(params.getParameters())); byte[] keyEnc = new byte[encoding.length + 1]; - keyEnc[0] = (byte)(0x00 + params.getParameters().getLogN()); + keyEnc[0] = (byte) (0x00 + params.getParameters().getLogN()); System.arraycopy(encoding, 0, keyEnc, 1, encoding.length); return new SubjectPublicKeyInfo(algorithmIdentifier, keyEnc); } else if (publicKey instanceof KyberPublicKeyParameters) { - KyberPublicKeyParameters params = (KyberPublicKeyParameters)publicKey; + KyberPublicKeyParameters params = (KyberPublicKeyParameters) publicKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.kyberOidLookup(params.getParameters())); @@ -229,7 +236,7 @@ else if (publicKey instanceof KyberPublicKeyParameters) } else if (publicKey instanceof NTRULPRimePublicKeyParameters) { - NTRULPRimePublicKeyParameters params = (NTRULPRimePublicKeyParameters)publicKey; + NTRULPRimePublicKeyParameters params = (NTRULPRimePublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.ntrulprimeOidLookup(params.getParameters())); @@ -238,7 +245,7 @@ else if (publicKey instanceof NTRULPRimePublicKeyParameters) } else if (publicKey instanceof SNTRUPrimePublicKeyParameters) { - SNTRUPrimePublicKeyParameters params = (SNTRUPrimePublicKeyParameters)publicKey; + SNTRUPrimePublicKeyParameters params = (SNTRUPrimePublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.sntruprimeOidLookup(params.getParameters())); @@ -247,7 +254,7 @@ else if (publicKey instanceof SNTRUPrimePublicKeyParameters) } else if (publicKey instanceof DilithiumPublicKeyParameters) { - DilithiumPublicKeyParameters params = (DilithiumPublicKeyParameters)publicKey; + DilithiumPublicKeyParameters params = (DilithiumPublicKeyParameters) publicKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.dilithiumOidLookup(params.getParameters())); @@ -265,7 +272,7 @@ else if (publicKey instanceof BIKEPublicKeyParameters) } else if (publicKey instanceof HQCPublicKeyParameters) { - HQCPublicKeyParameters params = (HQCPublicKeyParameters)publicKey; + HQCPublicKeyParameters params = (HQCPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); @@ -275,7 +282,7 @@ else if (publicKey instanceof HQCPublicKeyParameters) } else if (publicKey instanceof RainbowPublicKeyParameters) { - RainbowPublicKeyParameters params = (RainbowPublicKeyParameters)publicKey; + RainbowPublicKeyParameters params = (RainbowPublicKeyParameters) publicKey; byte[] encoding = params.getEncoded(); diff --git a/core/src/main/java/org/bouncycastle/tls/injection/Asn1Bridge.java b/core/src/main/java/org/bouncycastle/tls/injection/Asn1Bridge.java new file mode 100644 index 0000000000..c113e7f88f --- /dev/null +++ b/core/src/main/java/org/bouncycastle/tls/injection/Asn1Bridge.java @@ -0,0 +1,84 @@ +package org.bouncycastle.tls.injection; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; + +import java.io.IOException; + +/** + * #tls-injection + * + * The Asn1Bridge interface defines the bridge between ASN.1 notation for public/private keys and their internal + * representation inside the BC AsymmetricKeyParameter class. + * + * This interface is implemented by the Asn1BridgeForInjectedSigAlgs class (in the BC core package), + * which represents signature algorithms injected into TLS Injection Mechanism (in the BC tls package; + * however there is no compile-time dependency on "tls" from the "core" package). + * + * The Asn1BridgeForInjectedSigAlgs is consulted inside the BC core package in order to add the ability to + * work with public and private keys corresponding to the injected (perhaps, non-standard) signature algorithms. + * + * + */ +public interface Asn1Bridge { + + /** + * Checks whether the signature algorithm with the given object identified (OID) has been injected. + * @param oid the ASN object identifier of the algorithm in question + * @return returns true, iff the algorithm with the given oid has been injected + */ + boolean isSupportedAlgorithm(ASN1ObjectIdentifier oid); + + /** + * Checks whether the given BC key (public or private) can be converted to ASN.1. + * + * @param bcKey an internal BC representation of a public or a private key + * that has to be converted to ASN.1 + * @return returns true, iff bcKey is of known type and can be converted to ASN.1 + * (i.e., a PrivateKeyInfo or SubjectPublicKeyInfo instance) + */ + boolean isSupportedParameter(AsymmetricKeyParameter bcKey); + + /** + * Converts the given private key from ASN.1 to the internal BC representation. + * + * @param asnPrivateKey private key in the ASN.1 notation + * @return internal BC representation of the private key + * @throws IOException + */ + AsymmetricKeyParameter createPrivateKeyParameter(PrivateKeyInfo asnPrivateKey) throws IOException; + + + /** + * Converts the given private key from the internal BC representation to the ASN.1 notation. + * + * @param bcPrivateKey internal BC representation of a private key + * @param attributes ASN.1 attributes to be embedded into the ASN.1 representation + * @return ASN.1 representation of the private key + * @throws IOException + */ + PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter bcPrivateKey, ASN1Set attributes) throws IOException; + + + /** + * Converts the given public key from ASN.1 to the internal BC representation. + * + * @param ansPublicKey public key in the ASN.1 notation + * @param defaultParams some default parameters (currently, null is passed) + * @return internal BC representation of the public key + * @throws IOException + */ + AsymmetricKeyParameter createPublicKeyParameter(SubjectPublicKeyInfo ansPublicKey, Object defaultParams) throws IOException; + + /** + * Converts the given public key from the internal BC representation to the ASN.1 notation. + * + * @param bcPublicKey internal BC representation of a public key + * @return ASN.1 representation of the public key + * @throws IOException + */ + SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter bcPublicKey) throws IOException; +} diff --git a/core/src/main/java/org/bouncycastle/tls/injection/Asn1BridgeForInjectedSigAlgs.java b/core/src/main/java/org/bouncycastle/tls/injection/Asn1BridgeForInjectedSigAlgs.java new file mode 100644 index 0000000000..61bd431e7e --- /dev/null +++ b/core/src/main/java/org/bouncycastle/tls/injection/Asn1BridgeForInjectedSigAlgs.java @@ -0,0 +1,149 @@ +package org.bouncycastle.tls.injection; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; + +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Represents signature algorithms injected into TLS Injection Mechanism in the BC "tls" package. + * However, there is no compile-time dependency on "tls" from our ("core") package. + *

+ * Asn1BridgeForInjectedSigAlgs tries to contact the TLS Injection Mechanism's InjectionPoint class dynamically + * to obtain the corresponding Asn1Bridge implementation representing all the injected signature algorithms. + * If InjectionPoint is not available, the default Asn1Bridge implementation + * (which doesn't support any of the signature algorithms) is used. + * The default Asn1Bridge implementation can be replaced via the replaceWith() call. + */ +public class Asn1BridgeForInjectedSigAlgs + implements Asn1Bridge +{ + + // Bill Pugh Singleton Implementation, see https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/ + private static class BillPughSingleton + { + private static Asn1Bridge INSTANCE = new Asn1BridgeForInjectedSigAlgs(); + // ^^^ not final, since we allow to replace the instance via replaceWith + } + + + private Asn1Bridge delegate = null; + + // private = do not allow to call the constructor directly; force using theInstance() + private Asn1BridgeForInjectedSigAlgs() + { + // Here we try to reach the InjectionPoint class in the dependent "tls" package. If it is present, + // we use that class as an implementation for Asn1Bridge. + try + { + Class c = Class.forName("org.bouncycastle.tls.injection.InjectionPoint"); + Method m1 = c.getMethod("theInstance"); + Object o = m1.invoke(c); + + Method m2 = c.getMethod("asn1Bridge"); + Object bridge = m2.invoke(o); + assert bridge instanceof Asn1Bridge; + this.delegate = (Asn1Bridge) bridge; + } catch (Exception e) + { + // keeping the default implementation + } + } + + public synchronized static Asn1Bridge theInstance() + { + return Asn1BridgeForInjectedSigAlgs.BillPughSingleton.INSTANCE; + } + + public synchronized static void replaceWith(Asn1Bridge newInstance) + { + Asn1BridgeForInjectedSigAlgs.BillPughSingleton.INSTANCE = newInstance; + } + + @Override + public boolean isSupportedAlgorithm(ASN1ObjectIdentifier oid) + { + if (delegate != null) + { + return delegate.isSupportedAlgorithm(oid); + } + else + { + return false; + } + } + + + @Override + public boolean isSupportedParameter(AsymmetricKeyParameter bcKey) + { + if (delegate != null) + { + return delegate.isSupportedParameter(bcKey); + } + else + { + return false; + } + } + + @Override + public AsymmetricKeyParameter createPrivateKeyParameter(PrivateKeyInfo asnPrivateKey) throws IOException + { + if (delegate != null) + { + return delegate.createPrivateKeyParameter(asnPrivateKey); + } + else + { + throw new IOException("No injected signature algorithms to choose from."); + } + } + + @Override + public PrivateKeyInfo createPrivateKeyInfo( + AsymmetricKeyParameter bcPrivateKey, + ASN1Set attributes) throws IOException + { + if (delegate != null) + { + return delegate.createPrivateKeyInfo(bcPrivateKey, attributes); + } + else + { + throw new IOException("No injected signature algorithms to choose from."); + } + } + + @Override + public AsymmetricKeyParameter createPublicKeyParameter( + SubjectPublicKeyInfo ansPublicKey, + Object defaultParams) throws IOException + { + if (delegate != null) + { + return delegate.createPublicKeyParameter(ansPublicKey, defaultParams); + } + else + { + throw new IOException("No injected signature algorithms to choose from."); + } + } + + @Override + public SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter bcPublicKey) throws IOException + { + if (delegate != null) + { + return delegate.createSubjectPublicKeyInfo(bcPublicKey); + } + else + { + throw new IOException("No injected signature algorithms to choose from."); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java index 94cfe757e3..f5f1c23640 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -17,6 +17,7 @@ import org.bouncycastle.jcajce.provider.config.ProviderConfiguration; import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.tls.injection.InjectionPoint; public class BouncyCastlePQCProvider extends Provider @@ -43,6 +44,19 @@ public class BouncyCastlePQCProvider "Dilithium", "NTRUPrime", "BIKE", "HQC", "Rainbow" }; + /** + * #tls-injection + * ALGORITHM_MAPPING_CLASSES contains real classes corresponding to the names given in ALGORITHMS. + * We rely on ALGORITHM_MAPPING_CLASSES when we are unable to load classes by names via reflection + * (important, since we cannot rely fully on reflection in NativeImage from GraalVM). + * #pqc-tls + */ + private static final Class[] ALGORITHM_MAPPING_CLASSES = { + SPHINCS.Mappings.class, LMS.Mappings.class, NH.Mappings.class, XMSS.Mappings.class, SPHINCSPlus.Mappings.class, + CMCE.Mappings.class, Frodo.Mappings.class, SABER.Mappings.class, Picnic.Mappings.class, NTRU.Mappings.class, Falcon.Mappings.class, Kyber.Mappings.class, + Dilithium.Mappings.class, NTRUPrime.Mappings.class, BIKE.Mappings.class, HQC.Mappings.class, Rainbow.Mappings.class + }; + /** * Construct a new provider. This should only be required when * using runtime registration of the provider using the @@ -71,13 +85,20 @@ private void loadAlgorithms(String packageName, String[] names) { for (int i = 0; i != names.length; i++) { - Class clazz = loadClass(BouncyCastlePQCProvider.class, packageName + names[i] + "$Mappings"); + Class clazz; + if (i i String[] names = PropertyUtils.getStringArraySystemProperty(propertyName); if (null == names) { - return CANDIDATES_DEFAULT; + // #tls-injection + // return a concatenation of CANDIDATES_DEFAULT and injected KEMs code points + + int[] candidates = CANDIDATES_DEFAULT; + if (!InjectionPoint.kems().defaultKemsNeeded()) + candidates = new int[] {}; + + int[] before = InjectionPoint.kems().asCodePointCollection(InjectableKEMs.Ordering.BEFORE).stream().mapToInt(Integer::intValue).toArray(); + int[] after = InjectionPoint.kems().asCodePointCollection(InjectableKEMs.Ordering.AFTER).stream().mapToInt(Integer::intValue).toArray(); + + int resultLength = before.length + candidates.length + after.length; + int[] result = new int[resultLength]; + + System.arraycopy(before, 0, result, 0, before.length); + System.arraycopy(candidates, 0, result, before.length, candidates.length); + System.arraycopy(after, 0, result, before.length+candidates.length, after.length); + + return result; } return createCandidates(index, names, propertyName); @@ -504,6 +523,12 @@ private static Map createIndex(boolean isFipsContext, J addNamedGroup(isFipsContext, crypto, disableChar2, disableFFDHE, ng, all); } + // #tls-injection + for (int codePoint : InjectionPoint.kems().asCodePointCollection()) { + NamedGroupInfo ngInfo = new NamedGroupInfo(codePoint, InjectionPoint.kems().kemByCodePoint(codePoint).standardName(), null, true); + ng.put(codePoint, ngInfo); + } + return ng; } @@ -563,41 +588,85 @@ private static boolean hasAnyECDSA(Map local) } return false; } - - private final All all; + + //private final All all; + // for injection, we cannot use final enum All; we need some dynamic + // data structure for storing the corresponding sig scheme info + // #tls-injection + + private final int namedGroup; + private final String name; + private final String text; + private final String jcaAlgorithm; + private final String jcaGroup; + private final boolean char2; + private final boolean supportedPost13; + private final boolean supportedPre13; + private final int bitsECDH; + private final int bitsFFDHE; private final AlgorithmParameters algorithmParameters; private final boolean enabled; NamedGroupInfo(All all, AlgorithmParameters algorithmParameters, boolean enabled) { - this.all = all; + //this.all = all; + // #tls-injection + this.namedGroup = all.namedGroup; + this.name = all.name; + this.text = all.text; + this.jcaAlgorithm = all.jcaAlgorithm; + this.jcaGroup = all.jcaGroup; + this.char2 = all.char2; + this.supportedPost13 = all.supportedPost13; + this.supportedPre13 = all.supportedPre13; + this.bitsECDH = all.bitsECDH; + this.bitsFFDHE = all.bitsFFDHE; + + this.algorithmParameters = algorithmParameters; + this.enabled = enabled; + } + + NamedGroupInfo(int kemCodePoint, String name, AlgorithmParameters algorithmParameters, boolean enabled) + { + // #tls-injection + this.namedGroup = kemCodePoint; + this.name = name; + this.text = name; + this.jcaAlgorithm = name; + this.jcaGroup = name; + this.char2 = false; // not a curve + this.supportedPost13 = true; + this.supportedPre13 = true; + this.bitsECDH = 0; // not a curve + this.bitsFFDHE = 0; // not a curve + this.algorithmParameters = algorithmParameters; this.enabled = enabled; } int getBitsECDH() { - return all.bitsECDH; + return this.bitsECDH; } int getBitsFFDHE() { - return all.bitsFFDHE; + return this.bitsFFDHE; } String getJcaAlgorithm() { - return all.jcaAlgorithm; + return this.jcaAlgorithm; } String getJcaGroup() { - return all.jcaGroup; + return this.jcaGroup; } int getNamedGroup() { - return all.namedGroup; + return this.namedGroup; } boolean isActive(BCAlgorithmConstraints algorithmConstraints, boolean post13Active, boolean pre13Active) @@ -614,18 +683,18 @@ boolean isEnabled() boolean isSupportedPost13() { - return all.supportedPost13; + return this.supportedPost13; } boolean isSupportedPre13() { - return all.supportedPre13; + return this.supportedPre13; } @Override public String toString() { - return all.text; + return this.text; } private boolean isPermittedBy(BCAlgorithmConstraints algorithmConstraints) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java index eb5d60eb4f..5e41e5c9e6 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java @@ -44,6 +44,8 @@ import org.bouncycastle.tls.TlsUtils; import org.bouncycastle.tls.TrustedAuthority; import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.injection.InjectableKEMs; +import org.bouncycastle.tls.injection.InjectionPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.IPAddress; import org.bouncycastle.util.encoders.Hex; @@ -163,7 +165,15 @@ protected Vector getSupportedGroups(@SuppressWarnings("rawtypes") Vecto // @SuppressWarnings("unchecked") // Vector namedGroupRoles = namedGroupRolesRaw; - return NamedGroupInfo.getSupportedGroupsLocalClient(jsseSecurityParameters.namedGroups); + Vector defaultGroups = NamedGroupInfo.getSupportedGroupsLocalClient(jsseSecurityParameters.namedGroups); + + + // #tls-injection + Vector result = new Vector<>(); + result.addAll(InjectionPoint.kems().asCodePointCollection(InjectableKEMs.Ordering.BEFORE)); + result.addAll(NamedGroupInfo.getSupportedGroupsLocalClient(jsseSecurityParameters.namedGroups)); + result.addAll(InjectionPoint.kems().asCodePointCollection(InjectableKEMs.Ordering.AFTER)); + return result; } @Override @@ -217,7 +227,14 @@ protected int[] getSupportedCipherSuites() @Override protected Vector getSupportedSignatureAlgorithms() { - return jsseSecurityParameters.signatureSchemes.getLocalSignatureAndHashAlgorithms(); + Vector result = jsseSecurityParameters.signatureSchemes.getLocalSignatureAndHashAlgorithms(); + if (result == null) + result = new Vector<>(); + // #tls-injection + // adding injected sig algorithms (to TLS client hello) + result.addAll(0, InjectionPoint.sigAlgs().asSigAndHashCollection()); + + return result; } @Override diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509KeyManager.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509KeyManager.java index 269dfff1f1..14700b0bdd 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509KeyManager.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509KeyManager.java @@ -41,10 +41,8 @@ import org.bouncycastle.jsse.BCX509ExtendedKeyManager; import org.bouncycastle.jsse.BCX509Key; import org.bouncycastle.jsse.java.security.BCAlgorithmConstraints; -import org.bouncycastle.tls.KeyExchangeAlgorithm; -import org.bouncycastle.tls.NamedGroup; -import org.bouncycastle.tls.ProtocolVersion; -import org.bouncycastle.tls.TlsUtils; +import org.bouncycastle.tls.*; +import org.bouncycastle.tls.injection.InjectionPoint; class ProvX509KeyManager extends BCX509ExtendedKeyManager @@ -173,6 +171,13 @@ private static Map createFiltersClient() addFilter(filters, DSAPublicKey.class, "DSA"); addFilter(filters, ECPublicKey.class, "EC"); + // #tls-injection + // adding filters for injected signers (e.g., PQC signers) + for (String name: InjectionPoint.sigAlgs().names()) { + if (!filters.containsKey(name)) + addFilter(filters, name); + } + return Collections.unmodifiableMap(filters); } @@ -201,6 +206,13 @@ private static Map createFiltersServer() KeyExchangeAlgorithm.SRP_RSA); addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_KEY_ENCIPHERMENT, "RSA", KeyExchangeAlgorithm.RSA); + // #tls-injection + // adding filters for injected signers (e.g., PQC signers) + for (String name: InjectionPoint.sigAlgs().names()) { + if (!filters.containsKey(name)) + addFilter(filters, name); + } + return Collections.unmodifiableMap(filters); } diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java b/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java index ae80e14d8e..5e6792bc1b 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java @@ -1,14 +1,7 @@ package org.bouncycastle.jsse.provider; import java.security.AlgorithmParameters; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.Vector; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; @@ -20,6 +13,8 @@ import org.bouncycastle.tls.SignatureScheme; import org.bouncycastle.tls.TlsUtils; import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.injection.InjectionPoint; +import org.bouncycastle.tls.injection.sigalgs.InjectedSigAlgorithm; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; @@ -455,7 +450,17 @@ private static int[] createCandidatesFromProperty(Map result = new LinkedList<>(); + + result.addAll(InjectionPoint.sigAlgs().asCodePointCollection()); + + for (int codePoint : CANDIDATES_DEFAULT) + result.add(codePoint); + + return result.stream().mapToInt(Integer::intValue).toArray(); } return createCandidates(index, names, propertyName); @@ -503,12 +508,20 @@ private static int[] createCandidates(Map index, S private static int[] createCandidatesDefault() { All[] values = All.values(); - int[] result = new int[values.length]; + + + Vector result = new Vector(); for (int i = 0; i < values.length; ++i) { - result[i] = values[i].signatureScheme; + result.add(values[i].signatureScheme); } - return result; + + // #tls-injection + for (int codePoint : InjectionPoint.sigAlgs().asCodePointCollection()) { + result.add(codePoint); + } + + return result.stream().mapToInt(Integer::intValue).toArray(); } private static Map createIndex(boolean isFipsContext, JcaTlsCrypto crypto, @@ -519,6 +532,13 @@ private static Map createIndex(boolean isFipsConte { addSignatureScheme(isFipsContext, crypto, ng, ss, all); } + + // #tls-injection + for (InjectedSigAlgorithm sigAlg : InjectionPoint.sigAlgs().asSigAlgCollection()) { + SignatureSchemeInfo ssinfo = new SignatureSchemeInfo(sigAlg.codePoint(), sigAlg.name(), null); + ss.put(sigAlg.codePoint(), ssinfo); + } + return ss; } @@ -532,6 +552,13 @@ private static int getSignatureSchemeByName(String name) } } + // #tls-injection + for (InjectedSigAlgorithm sigAlg : InjectionPoint.sigAlgs().asSigAlgCollection()) { + if (sigAlg.name().equalsIgnoreCase(name)) { + return sigAlg.codePoint(); + } + } + return -1; } @@ -554,50 +581,100 @@ private static boolean isECDSA(int signatureScheme) } } - private final All all; + //private final All all; + // for injection, we cannot use final enum All; we need some dynamic + // data structure for storing the corresponding sig scheme info + // #tls-injection + private final int signatureScheme; + private final String name; + private final String text; + private final String jcaSignatureAlgorithm; + private final String jcaSignatureAlgorithmBC; + private final String keyAlgorithm; + private final String keyType13; + private final AlgorithmParameters algorithmParameters; private final NamedGroupInfo namedGroupInfo; private final boolean enabled; private final boolean disabled13; + private final boolean supportedPre13; + private final boolean supportedPost13; + private final boolean supportedCerts13; + + SignatureSchemeInfo(All all, AlgorithmParameters algorithmParameters, NamedGroupInfo namedGroupInfo, boolean enabled, boolean disabled13) { - this.all = all; + //this.all = all; + //#tls-injection: + this.signatureScheme = all.signatureScheme; + this.name = all.name; + this.text = all.text; + this.jcaSignatureAlgorithm = all.jcaSignatureAlgorithm; + this.jcaSignatureAlgorithmBC = all.jcaSignatureAlgorithmBC; + this.keyAlgorithm = all.keyAlgorithm; + this.keyType13 = all.keyType13; + this.algorithmParameters = algorithmParameters; this.namedGroupInfo = namedGroupInfo; this.enabled = enabled; this.disabled13 = disabled13; + + this.supportedPre13 = all.supportedPre13; + this.supportedPost13 = all.supportedPost13; + this.supportedCerts13 = all.supportedCerts13; } + // #tls-injection + SignatureSchemeInfo(int signatureSchemeCodePoint, String name, AlgorithmParameters algorithmParameters) { + this.signatureScheme = signatureSchemeCodePoint; + this.name = name; + this.text = name; + this.jcaSignatureAlgorithm = name; + this.jcaSignatureAlgorithmBC = name; + this.keyAlgorithm = name; + this.keyType13 = name; + + this.algorithmParameters = algorithmParameters; + this.namedGroupInfo = null; + this.enabled = true; + this.disabled13 = false; + + this.supportedPre13 = false; + this.supportedPost13 = true; + this.supportedCerts13 = true; + } + + short getHashAlgorithm() { - return SignatureScheme.getHashAlgorithm(all.signatureScheme); + return SignatureScheme.getHashAlgorithm(this.signatureScheme); } String getJcaSignatureAlgorithm() { - return all.jcaSignatureAlgorithm; + return this.jcaSignatureAlgorithm; } String getJcaSignatureAlgorithmBC() { - return all.jcaSignatureAlgorithmBC; + return this.jcaSignatureAlgorithmBC; } String getKeyType() { - return all.keyAlgorithm; + return this.keyAlgorithm; } String getKeyType13() { - return all.keyType13; + return this.keyType13; } String getName() { - return all.name; + return this.name; } NamedGroupInfo getNamedGroupInfo() @@ -607,17 +684,17 @@ NamedGroupInfo getNamedGroupInfo() short getSignatureAlgorithm() { - return SignatureScheme.getSignatureAlgorithm(all.signatureScheme); + return SignatureScheme.getSignatureAlgorithm(this.signatureScheme); } SignatureAndHashAlgorithm getSignatureAndHashAlgorithm() { - return getSignatureAndHashAlgorithm(all.signatureScheme); + return getSignatureAndHashAlgorithm(this.signatureScheme); } int getSignatureScheme() { - return all.signatureScheme; + return this.signatureScheme; } boolean isEnabled() @@ -627,23 +704,23 @@ boolean isEnabled() boolean isSupportedPost13() { - return !disabled13 && all.supportedPost13; + return !disabled13 && this.supportedPost13; } boolean isSupportedPre13() { - return all.supportedPre13; + return this.supportedPre13; } boolean isSupportedCerts13() { - return !disabled13 && all.supportedCerts13; + return !disabled13 && this.supportedCerts13; } @Override public String toString() { - return all.text; + return this.text; } // private boolean isActive(BCAlgorithmConstraints algorithmConstraints, boolean post13Active, boolean pre13Active, @@ -673,15 +750,15 @@ private boolean isNamedGroupOK(boolean post13Allowed, boolean pre13Allowed, Name } return (post13Allowed || pre13Allowed) - && (!isECDSA(all.signatureScheme) || NamedGroupInfo.hasAnyECDSALocal(namedGroupInfos)); + && (!isECDSA(this.signatureScheme) || NamedGroupInfo.hasAnyECDSALocal(namedGroupInfos)); } private boolean isPermittedBy(BCAlgorithmConstraints algorithmConstraints) { Set primitives = JsseUtils.SIGNATURE_CRYPTO_PRIMITIVES_BC; - return algorithmConstraints.permits(primitives, all.name, null) - && algorithmConstraints.permits(primitives, all.keyAlgorithm, null) - && algorithmConstraints.permits(primitives, all.jcaSignatureAlgorithm, algorithmParameters); + return algorithmConstraints.permits(primitives, this.name, null) + && algorithmConstraints.permits(primitives, this.keyAlgorithm, null) + && algorithmConstraints.permits(primitives, this.jcaSignatureAlgorithm, algorithmParameters); } } diff --git a/tls/src/main/java/org/bouncycastle/tls/AbstractTlsClient.java b/tls/src/main/java/org/bouncycastle/tls/AbstractTlsClient.java index 446eec9898..823990108f 100644 --- a/tls/src/main/java/org/bouncycastle/tls/AbstractTlsClient.java +++ b/tls/src/main/java/org/bouncycastle/tls/AbstractTlsClient.java @@ -5,6 +5,9 @@ import java.util.Vector; import org.bouncycastle.tls.crypto.TlsCrypto; +import org.bouncycastle.tls.injection.InjectableKEMs; +import org.bouncycastle.tls.injection.InjectionPoint; +import org.bouncycastle.tls.injection.kems.InjectedKEM; import org.bouncycastle.util.Integers; /** @@ -155,23 +158,37 @@ protected Vector getSupportedGroups(Vector namedGroupRoles) TlsCrypto crypto = getCrypto(); Vector supportedGroups = new Vector(); - if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.ecdh))) - { - TlsUtils.addIfSupported(supportedGroups, crypto, - new int[]{ NamedGroup.x25519, NamedGroup.x448 }); + // #tls-injection + // Adding injected KEMs before: + for (InjectedKEM kem : InjectionPoint.kems().kemsByOrdering(InjectableKEMs.Ordering.BEFORE)) { + supportedGroups.add(kem.codePoint()); } - if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.ecdh)) || - namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.ecdsa))) - { - TlsUtils.addIfSupported(supportedGroups, crypto, - new int[]{ NamedGroup.secp256r1, NamedGroup.secp384r1 }); + // #tls-injection + // Skipping the default KEMS, if they are not needed: + if (InjectionPoint.kems().defaultKemsNeeded()) { + + if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.ecdh))) { + TlsUtils.addIfSupported(supportedGroups, crypto, + new int[]{NamedGroup.x25519, NamedGroup.x448}); + } + + if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.ecdh)) || + namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.ecdsa))) { + TlsUtils.addIfSupported(supportedGroups, crypto, + new int[]{NamedGroup.secp256r1, NamedGroup.secp384r1}); + } + + if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.dh))) { + TlsUtils.addIfSupported(supportedGroups, crypto, + new int[]{NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096}); + } } - if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.dh))) - { - TlsUtils.addIfSupported(supportedGroups, crypto, - new int[]{ NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096 }); + // #tls-injection + // Adding injected KEMs after: + for (InjectedKEM kem : InjectionPoint.kems().kemsByOrdering(InjectableKEMs.Ordering.AFTER)) { + supportedGroups.add(kem.codePoint()); } return supportedGroups; @@ -415,6 +432,15 @@ public Vector getEarlyKeyShareGroups() * preferred groups. */ + // #tls-injection + // Adding the first injected KEM (if there exists one): + try { + return TlsUtils.vectorOfOne(InjectionPoint.kems().firstKEM().codePoint()); + } + catch (IllegalStateException e) { + // KEM not found, continue with default algorithms + } + if (null == supportedGroups || supportedGroups.isEmpty()) { return null; diff --git a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java index 2e4bf4d510..dd712bb01a 100644 --- a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java +++ b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java @@ -1,5 +1,9 @@ package org.bouncycastle.tls; + +import org.bouncycastle.tls.injection.InjectionPoint; +import org.bouncycastle.tls.injection.kems.InjectedKEM; + /** * RFC 7919 */ @@ -132,6 +136,8 @@ public class NamedGroup public static boolean canBeNegotiated(int namedGroup, ProtocolVersion version) { + if (InjectionPoint.kems().contain(namedGroup)) + return true; // #tls-injection switch (namedGroup) { case secp256r1: @@ -409,6 +415,16 @@ public static String getStandardName(int namedGroup) return finiteFieldName; } + // #tls-injection + if (InjectionPoint.kems().contain(namedGroup)) + { + String injectedKEMName = InjectionPoint.kems().kemByCodePoint(namedGroup).standardName(); + if (null != injectedKEMName) + { + return injectedKEMName; + } + } + String kemName = getKemName(namedGroup); if (null != kemName) { diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java index 2813ebbed6..aa9b2cb22c 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java @@ -1053,6 +1053,8 @@ protected void process13ServerHello(ServerHello serverHello, boolean afterHelloR throw new TlsFatalAlert(AlertDescription.illegal_parameter); } + + // #tls-injection (everything remains the same here, if KemAgreement is used as TlsAgreement for KEMs) agreement.receivePeerValue(keyShareEntry.getKeyExchange()); sharedSecret = agreement.calculateSecret(); } diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java index a44904459e..51fde50341 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java @@ -11,6 +11,8 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.tls.injection.InjectableKEMs; +import org.bouncycastle.tls.injection.InjectionPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; @@ -1442,7 +1444,22 @@ public static int[] readSupportedGroupsExtension(byte[] extensionData) throws IO TlsProtocol.assertEmpty(buf); - return namedGroups; + // #tls-injection + // adding injected KEMs... + + int[] before = InjectionPoint.kems().asCodePointCollection(InjectableKEMs.Ordering.BEFORE).stream().mapToInt(Integer::intValue).toArray(); + if (!InjectionPoint.kems().defaultKemsNeeded()) + namedGroups = new int[] {}; + int[] after = InjectionPoint.kems().asCodePointCollection(InjectableKEMs.Ordering.AFTER).stream().mapToInt(Integer::intValue).toArray(); + + int resultLength = before.length + namedGroups.length + after.length; + int[] result = new int[resultLength]; + + System.arraycopy(before, 0, result, 0, before.length); + System.arraycopy(namedGroups, 0, result, before.length, namedGroups.length); + System.arraycopy(after, 0, result, before.length+namedGroups.length, after.length); + + return result; } public static short[] readSupportedPointFormatsExtension(byte[] extensionData) throws IOException diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index 3ace602565..460dd36b9e 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -14,6 +14,8 @@ import org.bouncycastle.tls.crypto.TlsECConfig; import org.bouncycastle.tls.crypto.TlsKemConfig; import org.bouncycastle.tls.crypto.TlsSecret; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.injection.InjectionPoint; import org.bouncycastle.util.Arrays; public class TlsServerProtocol @@ -406,6 +408,12 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) { agreement = crypto.createDHDomain(new TlsDHConfig(namedGroup, true)).createDH(); } + else if (InjectionPoint.kems().contain(namedGroup)) + { + // #tls-injection + assert crypto instanceof JcaTlsCrypto; + agreement = InjectionPoint.kems().kemByCodePoint(namedGroup).tlsAgreement((JcaTlsCrypto) crypto, true); + } else if (NamedGroup.refersToASpecificKem(namedGroup)) { agreement = crypto.createKemDomain(new TlsKemConfig(namedGroup, true)).createKem(); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java index 80a33fd617..e7322c2da2 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java @@ -45,6 +45,9 @@ import org.bouncycastle.tls.crypto.TlsStreamSigner; import org.bouncycastle.tls.crypto.TlsStreamVerifier; import org.bouncycastle.tls.crypto.TlsVerifier; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.injection.InjectionPoint; +import org.bouncycastle.tls.injection.sigalgs.InjectedSigAlgorithm; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Shorts; @@ -120,6 +123,11 @@ private static Hashtable createCertSigAlgOIDs() addCertSigAlgOID(h, RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, SignatureAndHashAlgorithm.gostr34102012_512); + // adding injected algorithms #tls-injection + for (InjectedSigAlgorithm alg : InjectionPoint.sigAlgs().asSigAlgCollection()) { + addCertSigAlgOID(h, alg.oid(), alg.signatureAndHashAlgorithm()); + } + // TODO[RFC 8998] // addCertSigAlgOID(h, GMObjectIdentifiers.sm2sign_with_sm3, HashAlgorithm.sm3, SignatureAlgorithm.sm2); @@ -152,6 +160,12 @@ private static Vector createDefaultSupportedSigAlgs() result.addElement(SignatureAndHashAlgorithm.getInstance(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)); result.addElement(SignatureAndHashAlgorithm.getInstance(HashAlgorithm.sha1, SignatureAlgorithm.rsa)); result.addElement(SignatureAndHashAlgorithm.getInstance(HashAlgorithm.sha1, SignatureAlgorithm.dsa)); + + // adding injected signature+hash algorithms #tls-injection + for (InjectedSigAlgorithm alg: InjectionPoint.sigAlgs().asSigAlgCollection()) { + result.addElement(alg.signatureAndHashAlgorithm()); + } + return result; } @@ -1237,6 +1251,9 @@ public static Vector getSupportedSignatureAlgorithms(TlsContext context, Vector { addIfSupported(result, crypto, (SignatureAndHashAlgorithm)candidates.elementAt(i)); } + + // adding injected sig algorithms (to TLS client hello) #tls-injection + result.addAll(0, InjectionPoint.sigAlgs().asSigAndHashCollection()); return result; } @@ -5350,6 +5367,12 @@ else if (NamedGroup.refersToASpecificFiniteField(supportedGroup)) agreement = crypto.createDHDomain(new TlsDHConfig(supportedGroup, true)).createDH(); } } + else if (InjectionPoint.kems().contain(supportedGroup)) + { + // #tls-injection + assert (crypto instanceof JcaTlsCrypto); + agreement = InjectionPoint.kems().kemByCodePoint(supportedGroup).tlsAgreement((JcaTlsCrypto)crypto, false); // assume we are a client + } else if (NamedGroup.refersToASpecificKem(supportedGroup)) { if (crypto.hasKemAgreement()) @@ -5360,7 +5383,8 @@ else if (NamedGroup.refersToASpecificKem(supportedGroup)) if (null != agreement) { - byte[] key_exchange = agreement.generateEphemeral(); + // #tls-injection (everything remains the same here, if KemAgreement is used as TlsAgreement for KEMs) + byte[] key_exchange = agreement.generateEphemeral(); // key_exchange = ephemeral public key for KEMs KeyShareEntry clientShare = new KeyShareEntry(supportedGroup, key_exchange); clientShares.addElement(clientShare); diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index 5cf7cd07d9..805b54b03f 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -67,6 +67,7 @@ import org.bouncycastle.tls.crypto.impl.TlsBlockCipher; import org.bouncycastle.tls.crypto.impl.TlsImplUtils; import org.bouncycastle.tls.crypto.impl.TlsNullCipher; +import org.bouncycastle.tls.injection.InjectionPoint; import org.bouncycastle.util.Arrays; /** @@ -389,6 +390,10 @@ public boolean hasMacAlgorithm(int macAlgorithm) public boolean hasNamedGroup(int namedGroup) { + // #tls-injection + if (InjectionPoint.kems().contain(namedGroup)) { + return true; + } return NamedGroup.refersToASpecificGroup(namedGroup); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaDefaultTlsCredentialedSigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaDefaultTlsCredentialedSigner.java index abb8dd177c..4d34e646b8 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaDefaultTlsCredentialedSigner.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaDefaultTlsCredentialedSigner.java @@ -12,6 +12,7 @@ import org.bouncycastle.tls.SignatureScheme; import org.bouncycastle.tls.crypto.TlsCryptoParameters; import org.bouncycastle.tls.crypto.TlsSigner; +import org.bouncycastle.tls.injection.InjectionPoint; /** * Credentialed class for generating signatures based on the use of primitives from the JCA. @@ -36,6 +37,12 @@ private static TlsSigner makeSigner(JcaTlsCrypto crypto, PrivateKey privateKey, TlsSigner signer; + + if (InjectionPoint.sigAlgs().contain(signatureAndHashAlgorithm)) { // #tls-injection + signer = InjectionPoint.sigAlgs().tlsSignerFor(crypto, privateKey); + return signer; + } + // TODO We probably want better distinction b/w the rsa_pss_pss and rsa_pss_rsae cases here if (privateKey instanceof RSAPrivateKey || "RSA".equalsIgnoreCase(algorithm) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java index dedd63c8fb..bf8cff609b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.math.BigInteger; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.PublicKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -23,12 +24,7 @@ import org.bouncycastle.asn1.x509.Certificate; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.jcajce.util.JcaJceHelper; -import org.bouncycastle.tls.AlertDescription; -import org.bouncycastle.tls.HashAlgorithm; -import org.bouncycastle.tls.SignatureAlgorithm; -import org.bouncycastle.tls.SignatureScheme; -import org.bouncycastle.tls.TlsFatalAlert; -import org.bouncycastle.tls.TlsUtils; +import org.bouncycastle.tls.*; import org.bouncycastle.tls.crypto.Tls13Verifier; import org.bouncycastle.tls.crypto.TlsCertificate; import org.bouncycastle.tls.crypto.TlsCertificateRole; @@ -37,6 +33,7 @@ import org.bouncycastle.tls.crypto.TlsVerifier; import org.bouncycastle.tls.crypto.impl.LegacyTls13Verifier; import org.bouncycastle.tls.crypto.impl.RSAUtil; +import org.bouncycastle.tls.injection.InjectionPoint; /** * Implementation class for a single X.509 certificate based on the JCA. @@ -137,6 +134,10 @@ public TlsEncryptor createEncryptor(int tlsCertificateRole) throws IOException public TlsVerifier createVerifier(short signatureAlgorithm) throws IOException { + if (InjectionPoint.sigAlgs().contain(signatureAlgorithm)) { + // #tls-injection + return InjectionPoint.sigAlgs().tlsVerifierFor(crypto, getPublicKey(), signatureAlgorithm); + } switch (signatureAlgorithm) { case SignatureAlgorithm.ed25519: @@ -191,6 +192,18 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException { validateKeyUsageBit(KU_DIGITAL_SIGNATURE); + // #tls-injection + if (InjectionPoint.sigAlgs().contain(signatureScheme)) { + + try { + return InjectionPoint.sigAlgs().tls13VerifierFor(getPublicKey()); + } catch (InvalidKeyException e) { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + + } + switch (signatureScheme) { case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256: @@ -263,13 +276,32 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException AlgorithmParameterSpec pssSpec = org.bouncycastle.tls.crypto.impl.jcajce.RSAUtil .getPSSParameterSpec(cryptoHashAlgorithm, digestName, crypto.getHelper()); - return crypto.createTls13Verifier(sigName, pssSpec, getPubKeyRSA()); + try { + return crypto.createTls13Verifier(sigName, pssSpec, getPubKeyRSA()); + } + catch(Exception e) { + // #tls-injection fix: using the sig alg name of the SunRsaSign provider + sigName = "RSASSA-PSS"; + return crypto.createTls13Verifier(sigName, pssSpec, getPubKeyRSA()); + } } // TODO[RFC 8998] // case SignatureScheme.sm2sig_sm3: default: + // #tls-injection + if (InjectionPoint.sigAlgs().contain(signatureScheme)) { + + try { + return InjectionPoint.sigAlgs().tls13VerifierFor(getPublicKey()); + } catch (InvalidKeyException e) { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + + } + throw new TlsFatalAlert(AlertDescription.certificate_unknown); } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 5c39e1a115..ec9f09b5f8 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -68,10 +68,20 @@ import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6Client; import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6Server; import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6VerifierGenerator; +import org.bouncycastle.tls.injection.InjectionPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Strings; +import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; +import java.util.Vector; + /** * Class for providing cryptographic services for TLS based on implementations in the JCA/JCE. *

@@ -415,6 +425,12 @@ String getHMACAlgorithmName(int cryptoHashAlgorithm) public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) throws GeneralSecurityException { + // #tls-injection + // for injected KEMs (~NamedGroups), return null + if (InjectionPoint.kems().contain(namedGroup)) + return null; // KEM is supported, no specific parameters (e.g., there are no disabled algorithms) + + if (NamedGroup.refersToAnXDHCurve(namedGroup)) { switch (namedGroup) @@ -476,7 +492,14 @@ public AlgorithmParameters getSignatureSchemeAlgorithmParameters(int signatureSc AlgorithmParameterSpec pssSpec = RSAUtil.getPSSParameterSpec(cryptoHashAlgorithm, digestName, getHelper()); - Signature signer = getHelper().createSignature(sigName); + Signature signer; + try { + signer = getHelper().createSignature(sigName); + } + catch(Exception e) { + signer = Signature.getInstance("RSASSA-PSS", "SunRsaSign"); + // #tls-injection fix: using the sig alg name of the SunRsaSign provider + } // NOTE: We explicitly set them even though they should be the defaults, because providers vary signer.setParameter(pssSpec); @@ -648,6 +671,10 @@ public boolean hasMacAlgorithm(int macAlgorithm) public boolean hasNamedGroup(int namedGroup) { + // #tls-injection + if (InjectionPoint.kems().contain(namedGroup)) { + return true; + } final Integer key = Integers.valueOf(namedGroup); synchronized (supportedNamedGroups) { @@ -749,6 +776,9 @@ public boolean hasSignatureAlgorithm(short signatureAlgorithm) public boolean hasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm) { + if (InjectionPoint.sigAlgs().contain(sigAndHashAlgorithm)) + return true; // #tls-injection + short signature = sigAndHashAlgorithm.getSignature(); switch (sigAndHashAlgorithm.getHash()) @@ -765,6 +795,9 @@ public boolean hasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHash public boolean hasSignatureScheme(int signatureScheme) { + if (InjectionPoint.sigAlgs().contain(signatureScheme)) + return true; // #tls-injection + switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: @@ -1028,6 +1061,14 @@ protected Tls13Verifier createTls13Verifier(String algorithmName, AlgorithmParam { try { + // #tls-injection + // try injected verifier... + try { + return InjectionPoint.sigAlgs().tls13VerifierFor(publicKey); + } catch (Exception e) { + // e.g., not injected, continue as usual + } + JcaJceHelper helper = getHelper(); if (null != parameter) { @@ -1148,6 +1189,11 @@ protected Boolean isSupportedNamedGroup(int namedGroup) { try { + if (InjectionPoint.kems().contain(namedGroup)) + { + return true; // #tls-injection + } + else if (NamedGroup.refersToAnXDHCurve(namedGroup)) { /* diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAPSSSigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAPSSSigner.java index 815431fc02..97e09426cb 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAPSSSigner.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAPSSSigner.java @@ -85,6 +85,13 @@ public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) thro AlgorithmParameterSpec pssSpec = RSAUtil.getPSSParameterSpec(cryptoHashAlgorithm, digestName, crypto.getHelper()); - return crypto.createStreamSigner(sigName, pssSpec, privateKey, true); + try { + return crypto.createStreamSigner(sigName, pssSpec, privateKey, true); + } + catch(Exception e) { + // #tls-injection fix: using the sig alg name of the SunRsaSign provider + sigName = "RSASSA-PSS"; + return crypto.createStreamSigner(sigName, pssSpec, privateKey, true); + } } } diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/InjectableAlgorithms.java b/tls/src/main/java/org/bouncycastle/tls/injection/InjectableAlgorithms.java new file mode 100644 index 0000000000..154f574de4 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/InjectableAlgorithms.java @@ -0,0 +1,88 @@ +package org.bouncycastle.tls.injection; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.tls.injection.kems.KemFactory; +import org.bouncycastle.tls.injection.sigalgs.SigAlgAPI; + +import java.util.Collection; + +public class InjectableAlgorithms +{ + + ///// KEMs + private final InjectableKEMs kems; + private final InjectableSigAlgs sigAlgs; + + + public InjectableAlgorithms() + { + this(new InjectableKEMs(), new InjectableSigAlgs()); + } + + private InjectableAlgorithms( + InjectableKEMs kems, + InjectableSigAlgs sigAlgs) + { + this.kems = kems; + this.sigAlgs = sigAlgs; + } + + private InjectableAlgorithms(InjectableAlgorithms origin) + { // clone constructor + this.kems = new InjectableKEMs(origin.kems); + this.sigAlgs = new InjectableSigAlgs(origin.sigAlgs); + } + + + public InjectableAlgorithms withKEM( + String standardName, + int kemCodePoint, + KemFactory kemFactory, + InjectableKEMs.Ordering ordering) + { + return new InjectableAlgorithms( + this.kems.withKEM(kemCodePoint, standardName, kemFactory, ordering), + new InjectableSigAlgs(this.sigAlgs) + ); + } + + public InjectableAlgorithms withoutKEM(int kemCodePoint) + { + return new InjectableAlgorithms( + this.kems.withoutKEM(kemCodePoint), + this.sigAlgs + ); + } + + public InjectableAlgorithms withoutDefaultKEMs() + { + return new InjectableAlgorithms( + this.kems.withoutDefaultKEMs(), + this.sigAlgs + ); + } + + public InjectableAlgorithms withSigAlg( + String name, + Collection aliases, + ASN1ObjectIdentifier oid, + int signatureSchemeCodePoint, + SigAlgAPI api) + { + InjectableAlgorithms clone = new InjectableAlgorithms(this); + clone.sigAlgs().add(name, aliases, oid, signatureSchemeCodePoint, api); + return clone; + } + + public InjectableKEMs kems() + { + return this.kems; + } + + public InjectableSigAlgs sigAlgs() + { + return this.sigAlgs; + } + + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/InjectableKEMs.java b/tls/src/main/java/org/bouncycastle/tls/injection/InjectableKEMs.java new file mode 100644 index 0000000000..c1dba62068 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/InjectableKEMs.java @@ -0,0 +1,165 @@ +package org.bouncycastle.tls.injection; + +import org.bouncycastle.tls.injection.kems.InjectedKEM; +import org.bouncycastle.tls.injection.kems.KemFactory; + +import java.util.*; + +public class InjectableKEMs +{ + public enum Ordering + {BEFORE, AFTER} + + ; + + ///// KEMs + private final List kemsBefore; + private final boolean useDefaultKems; + private final List kemsAfter; + private final Map code2kem; + + public InjectableKEMs() + { + this.kemsBefore = new LinkedList<>(); + this.useDefaultKems = true; + this.kemsAfter = new LinkedList<>(); + this.code2kem = new HashMap<>(); + + } + + InjectableKEMs(InjectableKEMs origin) + { // clone constructor + this(origin, origin.useDefaultKems); + } + + private InjectableKEMs( + InjectableKEMs origin, + boolean useDefaultKems) + { // clone constructor + this.kemsBefore = new LinkedList<>(origin.kemsBefore); + this.useDefaultKems = useDefaultKems; + this.kemsAfter = new LinkedList<>(origin.kemsAfter); + this.code2kem = new HashMap<>(origin.code2kem); + } + + public InjectableKEMs withKEM( + int kemCodePoint, + String standardName, + KemFactory kemFactory, + Ordering ordering) + { + if (code2kem.containsKey(kemCodePoint)) + { + throw new RuntimeException("KEM code point " + kemCodePoint + " already exists."); + } + + InjectedKEM kem = new InjectedKEM(kemCodePoint, standardName, kemFactory); + + InjectableKEMs clone = new InjectableKEMs(this); + clone.code2kem.put(kemCodePoint, kem); + if (ordering == Ordering.BEFORE) + { + clone.kemsBefore.add(0, kemCodePoint); // adding BEFORE all + } + else + { + clone.kemsAfter.add(kemCodePoint); + } + return clone; + } + + public InjectableKEMs withoutKEM(int kemCodePoint) + { + if (!code2kem.containsKey(kemCodePoint)) + { + throw new RuntimeException("KEM code point " + kemCodePoint + " does not exist."); + } + + InjectableKEMs clone = new InjectableKEMs(this); + clone.code2kem.remove(kemCodePoint); + + int i = clone.kemsBefore.indexOf(kemCodePoint); + if (i >= 0) + { + clone.kemsBefore.remove(i); + } + + i = clone.kemsAfter.indexOf(kemCodePoint); + if (i >= 0) + { + clone.kemsAfter.remove(i); + } + return clone; + } + + public InjectableKEMs withoutDefaultKEMs() + { + return new InjectableKEMs(this, false); + } + + public boolean contain(int codePoint) + { + return code2kem.containsKey(codePoint); + } + + public InjectedKEM kemByCodePoint(int codePoint) + { + return code2kem.get(codePoint); + } + + public Collection kemsByOrdering(Ordering ordering) + { + if (ordering == Ordering.BEFORE) + { + return kemsBefore.stream().map(code2kem::get).toList(); + } + else + { + return kemsAfter.stream().map(code2kem::get).toList(); + } + } + + public Collection asCodePointCollection(Ordering ordering) + { + if (ordering == Ordering.BEFORE) + { + return new LinkedList<>(kemsBefore); + } + else + { + return new LinkedList<>(kemsAfter); + } + } + + public Collection asCodePointCollection() + { + List result = new LinkedList<>(kemsBefore); + result.addAll(kemsAfter); + return result; + } + + public boolean defaultKemsNeeded() + { + return useDefaultKems; + } + + public InjectedKEM firstKEM() + { + + + Collection kems = kemsByOrdering(InjectableKEMs.Ordering.BEFORE); + if (!kems.isEmpty()) + { + return kems.iterator().next(); + } + + kems = kemsByOrdering(InjectableKEMs.Ordering.AFTER); + if (!kems.isEmpty()) + { + return kems.iterator().next(); + } + + throw new IllegalStateException("No KEM injected"); + } + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/InjectableSigAlgs.java b/tls/src/main/java/org/bouncycastle/tls/injection/InjectableSigAlgs.java new file mode 100644 index 0000000000..e5cbe2601d --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/InjectableSigAlgs.java @@ -0,0 +1,245 @@ +package org.bouncycastle.tls.injection; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.tls.SignatureAndHashAlgorithm; +import org.bouncycastle.tls.crypto.TlsSigner; +import org.bouncycastle.tls.crypto.TlsVerifier; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.injection.sigalgs.*; + +import java.io.IOException; +import java.security.*; +import java.util.*; + +public class InjectableSigAlgs +{ + + private final List orderedSigs; + private final Map code2sig; + private final Map oid2sig; + private final InjectedSigners signers; + private final InjectedSigVerifiers verifiers; + + public InjectableSigAlgs() + { + this.orderedSigs = new LinkedList<>(); + this.code2sig = new HashMap<>(); + this.oid2sig = new HashMap<>(); + this.signers = new InjectedSigners(); + this.verifiers = new InjectedSigVerifiers(); + } + + InjectableSigAlgs(InjectableSigAlgs origin) + { // clone constructor + this.orderedSigs = new LinkedList<>(origin.orderedSigs); + this.code2sig = new HashMap<>(origin.code2sig); + this.oid2sig = new HashMap<>(origin.oid2sig); + this.signers = new InjectedSigners(origin.signers); + this.verifiers = new InjectedSigVerifiers(origin.verifiers); + } + + public void add( + String algorithmFullName, + Collection aliases, + ASN1ObjectIdentifier oid, + int signatureSchemeCodePoint, + SigAlgAPI api) + { + InjectedSigAlgorithm newAlg = new InjectedSigAlgorithm(algorithmFullName, aliases, oid, signatureSchemeCodePoint, api); + orderedSigs.add(newAlg); + code2sig.put(signatureSchemeCodePoint, newAlg); + oid2sig.put(oid.toString(), newAlg); + signers.add(algorithmFullName, api::sign); + verifiers.add(signatureSchemeCodePoint, api::verifySignature, api::internalEncodingFor); + } + + public boolean contain(int codePoint) + { + return code2sig.containsKey(codePoint); + } + + public boolean contain(SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + int codePoint = SignatureAndHashAlgorithmFactory.codePointFromSignatureAndHashAlgorithm(signatureAndHashAlgorithm); + return code2sig.containsKey(codePoint); + } + + public boolean contain(ASN1ObjectIdentifier oid) + { + for (InjectedSigAlgorithm sigAlgorithm : orderedSigs) + { + if (oid.equals(sigAlgorithm.oid())) + { + return true; + } + } + return false; + } + + public Collection asSigAndHashCollection() + { + return orderedSigs.stream().map(InjectedSigAlgorithm::signatureAndHashAlgorithm).toList(); + } + + public Collection asCodePointCollection() + { + return orderedSigs.stream().map(InjectedSigAlgorithm::codePoint).toList(); + } + + public Collection asSigAlgCollection() + { + return orderedSigs; + } + + public Iterable names() + { + return signers.getNames(); + } + + public InjectedSigAlgorithm byOid(ASN1ObjectIdentifier oid) + { + String algKey = oid.toString(); + return oid2sig.get(algKey); + } + + ///// for BC TLS + + public boolean isSupportedPublicKey(Key someKey) + { + for (InjectedSigAlgorithm sigAlg : orderedSigs) + if (sigAlg.isSupportedPublicKey(someKey)) + { + return true; + } + + return false; + } + + public boolean isSupportedPrivateKey(Key someKey) + { + for (InjectedSigAlgorithm sigAlg : orderedSigs) + if (sigAlg.isSupportedPrivateKey(someKey)) + { + return true; + } + + return false; + } + + public byte[] internalEncodingFor(PublicKey publicKey) + { + for (InjectedSigAlgorithm sigAlg : orderedSigs) + if (sigAlg.isSupportedPublicKey(publicKey)) + { + return sigAlg.internalEncodingFor(publicKey); + } + throw new RuntimeException("Public key is not supported."); + } + + public byte[] internalEncodingFor(PrivateKey privateKey) + { + for (InjectedSigAlgorithm sigAlg : orderedSigs) + if (sigAlg.isSupportedPrivateKey(privateKey)) + { + return sigAlg.internalEncodingFor(privateKey); + } + throw new RuntimeException("Private key is not supported."); + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + for (InjectedSigAlgorithm sigAlg : orderedSigs) + if (sigAlg.oid().equals(algOid)) + { + return sigAlg.converter().generatePublic(keyInfo); + } + + throw new RuntimeException("Public key generation for the algorithm " + algOid + " is not supported."); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + for (InjectedSigAlgorithm sigAlg : orderedSigs) + if (sigAlg.oid().equals(algOid)) + { + return sigAlg.converter().generatePrivate(keyInfo); + } + + throw new RuntimeException("Private key generation for the algorithm " + algOid + " is not supported."); + } + + + public MyTls13Verifier tls13VerifierFor(PublicKey key) throws InvalidKeyException + { + SignatureSpi spi = signatureSpiFor(key); + + + return new MyTls13Verifier(key, spi); + } + + public TlsVerifier tlsVerifierFor( + JcaTlsCrypto crypto, + PublicKey publicKey, + int sigSchemeCodePoint) + { + return verifiers.tlsVerifier(crypto, publicKey, sigSchemeCodePoint); + } + + public TlsSigner tlsSignerFor( + JcaTlsCrypto crypto, + PrivateKey privateKey) + { + for (InjectedSigAlgorithm sigAlg : orderedSigs) + { + SignatureSpi result = null; + try + { + result = sigAlg.signatureSpi(privateKey); + } catch (Exception e) + { + // SignatureSpi could not been created with this factory, continue with the next one + } + if (result != null) + { + // found some sigAlg that can handle our privateKey; + // use sigAlg.name() to obtain the signer + return signers.tlsSigner(crypto, privateKey, sigAlg.name()); + } + } + throw new RuntimeException("Private key with algorithm " + privateKey.getAlgorithm() + " is not supported" + + "(perhaps, with these particular parameters)."); + } + + public SignatureSpi signatureSpiFor(Key publicOrPrivateKey) throws InvalidKeyException + { + SignatureSpi result = null; + for (InjectedSigAlgorithm sigAlg : orderedSigs) + { + try + { + result = sigAlg.signatureSpi(publicOrPrivateKey); + } catch (Exception e) + { + // SignatureSpi could not been created with this factory, continue with the next one + } + if (result != null) + { + break; + } + } + + if (result == null) + { + throw new InvalidKeyException("No known SignatureSpi for the passed public key of type " + publicOrPrivateKey.getClass().getName()); + } + + return result; + } + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/InjectionPoint.java b/tls/src/main/java/org/bouncycastle/tls/injection/InjectionPoint.java new file mode 100644 index 0000000000..c0a41f3995 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/InjectionPoint.java @@ -0,0 +1,321 @@ +package org.bouncycastle.tls.injection; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; +import org.bouncycastle.tls.injection.sigalgs.InjectedSigAlgorithm; +import org.bouncycastle.tls.injection.sigalgs.InjectedSigAlgsProvider; + +import java.io.IOException; +import java.security.Provider; +import java.security.Security; +import java.util.Collection; +import java.util.Stack; + +public class InjectionPoint +{ + private final Stack injectionStack; + + + // Bill Pugh Singleton Implementation, see https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/ + private static class BillPughSingleton + { + private static final InjectionPoint INSTANCE = new InjectionPoint(); + } + + // private = do not allow to call the constructor directly; force using _new + private InjectionPoint() + { + this.injectionStack = new Stack<>(); + } + + public static InjectionPoint theInstance() + { + return BillPughSingleton.INSTANCE; + } + + /** + * Checks whether the stack of injected algorithms is empty. + * @return true, iff no InjectableAlgorithms have been pushed (or all have been popped out) + */ + public synchronized boolean isEmpty() { + return injectionStack.isEmpty(); + } + + /** + * Injects (pushes) the given algorithms into BouncyCastle TLS implementation. + * + * @param newAlgorithms the algorithms to inject + * @throws IllegalStateException if another set of InjectableAlgorithms has already been injected (pushed). + * In this case, use pushAfter() to be able to push the new algorithms instead of the previous. + * Alternatively, use pop() to withdraw all previously injected algorithms and push() the new set of algorithms. + */ + public synchronized void push(InjectableAlgorithms newAlgorithms) throws IllegalStateException + { + if (!injectionStack.isEmpty()) + { + throw new IllegalStateException("Some other algorithms have been already injected (pushed)."); + } + + injectionStack.push(newAlgorithms); + + // Inserting forcefully (to the second place) the BC TLS provider + // and (to the first place) our provider for injected signature algorithms: + BouncyCastleJsseProvider jsseProvider = new BouncyCastleJsseProvider(); + Security.insertProviderAt(jsseProvider, 1); + + InjectedSigAlgsProvider injProvider = new InjectedSigAlgsProvider(); + Security.insertProviderAt(injProvider, 1); + + // adding the BC provider as the last (since we want to prioritize Java built-in algorithms): + BouncyCastleProvider jceProvider = new BouncyCastleProvider(); + Security.addProvider(jceProvider); + } + + /** + * Injects (pushes) the current InjectableAlgorithms into BouncyCastle TLS implementation + * by replacing (staging into the stack) the previously injected algorithms. + * + * @param newAlgorithms the algorithms to inject + * @param previous the previously injected algorithms (works as a "token" that allows us to "overwrite" them) + * @throws IllegalStateException if the previously injected algorithms do not match the " previous" argument. + * In this case, use pop() to withdraw all previously injected algorithms and push() the new set of algorithms. + */ + public synchronized void pushAfter( + InjectableAlgorithms newAlgorithms, + InjectableAlgorithms previous) throws IllegalStateException + { + if (injectionStack.isEmpty()) + { + throw new IllegalStateException("No previously injected (pushed) algorithms found."); + } + if (!injectionStack.peek().equals(previous)) + { + throw new IllegalStateException("The previously injected (pushed) algorithms do not match the previous argument."); + } + injectionStack.push(newAlgorithms); + } + + /** + * Withdraws (pops) the current set of algorithms and restores the previously injected algorithms (if any). + * + * @param current the currently used injected algorithms (act as a key to withdraw) + * @throws IllegalStateException if no InjectableAlgorithms have been pushed + */ + public synchronized void pop(InjectableAlgorithms current) throws IllegalStateException + { + if (injectionStack.isEmpty()) + { + throw new IllegalStateException("No previously injected (pushed) algorithms found."); + } + if (!injectionStack.peek().equals(current)) + { + throw new IllegalStateException("The currently used injected (pushed) algorithms do not match the current argument."); + } + injectionStack.pop(); + } + + + ///// for BC TLS + + private static InjectableKEMs dummyKems = new InjectableKEMs(); + private static InjectableSigAlgs dummySigAlgs = new InjectableSigAlgs(); + + public static InjectableKEMs kems() + { + InjectableAlgorithms algs = BillPughSingleton.INSTANCE.injectionStack.peek(); + if (algs == null) + { + return dummyKems; + } + return algs.kems(); + } + + public static InjectableSigAlgs sigAlgs() + { + InjectableAlgorithms algs = BillPughSingleton.INSTANCE.injectionStack.peek(); + if (algs == null) + { + return dummySigAlgs; + } + return algs.sigAlgs(); + } + + public synchronized static void configureProvider(ConfigurableProvider provider) + { + + // TODO: call not only from BouncyCastlePQCProvider, but also from JSSE? + InjectableAlgorithms algs = BillPughSingleton.INSTANCE.injectionStack.peek(); + for (InjectedSigAlgorithm alg : sigAlgs().asSigAlgCollection()) + { + + new SigAlgRegistrar(alg.oid(), alg.name(), alg.aliases(), alg.converter()).configure(provider); + } + } + + private static class SigAlgRegistrar + extends AsymmetricAlgorithmProvider + { + private final ASN1ObjectIdentifier oid; + private final String name; + private final Collection aliases; + private final AsymmetricKeyInfoConverter converter; + + public SigAlgRegistrar( + ASN1ObjectIdentifier oid, + String name, + Collection aliases, + AsymmetricKeyInfoConverter converter) + { + super(); + this.oid = oid; + this.name = name; + this.aliases = aliases; + this.converter = converter; + } + + @Override + public void configure(ConfigurableProvider provider) + { + try + { + provider.addAlgorithm("Alg.Alias.Signature." + this.oid, this.name); + provider.addAlgorithm("Alg.Alias.Signature.OID." + this.oid, this.name); + } catch (IllegalStateException e) + { + // ignore, if duplicate (needed for injected RSA) + } + + // remove previous values in order to avoid the duplicate key exception + if (provider instanceof java.security.Provider) + { + java.security.Provider p = (java.security.Provider) provider; + p.remove("Signature." + this.name); + for (String alias : this.aliases) + { + p.remove("Signature." + alias); + p.remove("Alg.Alias.Signature." + alias); + } + p.remove("Alg.Alias.Signature." + this.oid); + p.remove("Alg.Alias.Signature.OID." + this.oid); + + p.remove("Alg.Alias.KeyFactory." + this.oid); + p.remove("Alg.Alias.KeyFactory.OID." + this.oid); + + p.remove("Alg.Alias.KeyPairGenerator." + this.oid); + p.remove("Alg.Alias.KeyPairGenerator.OID." + this.oid); + } + // = provider.addSignatureAlgorithm(provider, "SPHINCSPLUS", PREFIX + "SignatureSpi$Direct", BCObjectIdentifiers.sphincsPlus); + provider.addAlgorithm("Signature." + this.name, "org.bouncycastle.tls.injection.signaturespi.DirectSignatureSpi"); + provider.addAlgorithm("Alg.Alias.Signature." + this.oid, this.name); + provider.addAlgorithm("Alg.Alias.Signature.OID." + this.oid, this.name); + + provider.addAlgorithm("KeyFactory." + this.name, "org.bouncycastle.tls.injection.signaturespi.UniversalKeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory." + this.oid, this.name); + provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + this.oid, this.name); + + /* Maybe, in the future, we'll implement a universal KeyPairGenerator. + We need to check parameters carefully during the initialization + in order to find out whether the parameters correspond to one of the injected algorithms... + + provider.addAlgorithm("KeyPairGenerator." + this.name, "org.bouncycastle.tls.injection.signaturespi.UniversalKeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + this.oid, this.name); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.OID." + this.oid, this.name); + */ + + + try + { + registerOid(provider, this.oid, this.name, converter); + registerOidAlgorithmParameters(provider, this.oid, this.name); + } catch (IllegalStateException e) + { + // ignore, if duplicate (needed for injected RSA) + } + provider.addKeyInfoConverter(this.oid, converter); + } + } + + public Asn1Bridge asn1Bridge() + { + return new Asn1Bridge() + { + @Override + public boolean isSupportedAlgorithm(ASN1ObjectIdentifier oid) + { + return sigAlgs().contain(oid); + } + + @Override + public boolean isSupportedParameter(AsymmetricKeyParameter bcKey) + { + for (InjectedSigAlgorithm sigAlg : sigAlgs().asSigAlgCollection()) + { + if (sigAlg.isSupportedParameter(bcKey)) + { + return true; + } + } + return false; + } + + @Override + public AsymmetricKeyParameter createPrivateKeyParameter(PrivateKeyInfo asnPrivateKey) throws IOException + { + AlgorithmIdentifier algId = asnPrivateKey.getPrivateKeyAlgorithm(); + ASN1ObjectIdentifier algOID = algId.getAlgorithm(); + return sigAlgs().byOid(algOID).createPrivateKeyParameter(asnPrivateKey); + } + + @Override + public PrivateKeyInfo createPrivateKeyInfo( + AsymmetricKeyParameter bcPrivateKey, + ASN1Set attributes) throws IOException + { + for (InjectedSigAlgorithm sigAlg : sigAlgs().asSigAlgCollection()) + { + if (sigAlg.isSupportedParameter(bcPrivateKey)) + { + return sigAlg.createPrivateKeyInfo(bcPrivateKey, attributes); + } + } + throw new RuntimeException("Unsupported private key params were given"); + } + + @Override + public AsymmetricKeyParameter createPublicKeyParameter( + SubjectPublicKeyInfo ansPublicKey, + Object defaultParams) throws IOException + { + AlgorithmIdentifier algId = ansPublicKey.getAlgorithm(); + ASN1ObjectIdentifier algOID = algId.getAlgorithm(); + String algKey = algOID.toString(); + return sigAlgs().byOid(algOID).createPublicKeyParameter(ansPublicKey, defaultParams); + } + + @Override + public SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter bcPublicKey) throws IOException + { + for (InjectedSigAlgorithm sigAlg : sigAlgs().asSigAlgCollection()) + { + if (sigAlg.isSupportedParameter(bcPublicKey)) + { + return sigAlg.createSubjectPublicKeyInfo(bcPublicKey); + } + } + throw new RuntimeException("Unsupported public key params were given"); + } + }; + } + + ; + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/kems/InjectedKEM.java b/tls/src/main/java/org/bouncycastle/tls/injection/kems/InjectedKEM.java new file mode 100644 index 0000000000..5ad65acbd1 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/kems/InjectedKEM.java @@ -0,0 +1,64 @@ +package org.bouncycastle.tls.injection.kems; + +import org.bouncycastle.tls.crypto.TlsAgreement; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; + +/** + * The class for storing information of an injected key encapsulation mechanism (KEM ~ named group ~ curve). + * (For the needs of Post-Quantum Cryptography, DH/ECC groups/curves have been replaced by KEMs.) + * #tls-injection + * + * @author Sergejs Kozlovics + */ +public class InjectedKEM +{ + + public interface TlsAgreementFactory + { + TlsAgreement create( + JcaTlsCrypto crypto, + boolean isServer); + } + + private final int codePoint; + private final String standardName; + private final TlsAgreementFactory tlsAgreementFactory; + + public InjectedKEM( + int kemCodePoint, + String standardName, + KemFactory kemFactory) + { + this(kemCodePoint, + standardName, + (crypto, isServer) -> new TlsAgreementForKEM(crypto, isServer, kemFactory.create())); + + } + + public InjectedKEM( + int codePoint, + String standardName, + TlsAgreementFactory tlsAgreementFactory) + { + this.codePoint = codePoint; + this.standardName = standardName; + this.tlsAgreementFactory = tlsAgreementFactory; + } + + public int codePoint() + { + return this.codePoint; + } + + public String standardName() + { + return this.standardName; + } + + public TlsAgreement tlsAgreement( + JcaTlsCrypto crypto, + boolean isServer) + { + return this.tlsAgreementFactory.create(crypto, isServer); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/kems/KEM.java b/tls/src/main/java/org/bouncycastle/tls/injection/kems/KEM.java new file mode 100644 index 0000000000..f0184f7ed1 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/kems/KEM.java @@ -0,0 +1,48 @@ +package org.bouncycastle.tls.injection.kems; + +import org.openquantumsafe.Pair; + +/** + * A KEM (key encapsulation mechanism) is a set of functions that can be used to obtain + * a symmetric encryption key from asymmetric keys. + * The term KEM is a generalisation of Diffie-Hellman key exchange. + * The KEM abstraction can also be used to obtain quantumly exchanged keys via QKD + * (a KEM then needs to exchange only key IDs) + *

+ * The three KEM functions actually define a half-KEM: keyGen() and decapsulate() are called at one side (e.g., the client), + * while encapsulate() is called at the other side (e.g., the server). + *

+ * This interface defines the three functions that are present in any KEM. + * All keys/secrets/ciphertexts are byte[]-encoded. + * #tls-injection + * + * @author Sergejs Kozlovics + */ +public interface KEM +{ + /** + * Generates a new key pair (pk, sk). + * + * @return a public key pk and its corresponding private key (=secret key) sk + */ + Pair keyGen() throws Exception; + + /** + * Generates a secret (=symmetric key K) and encapsulates it to be sent to the partner. + * + * @param partnerPublicKey partner's public key received during the TLS handshake + * @return a generated symmetric key K and a ciphertext ct (=K encrypted with partner's public Key) + */ + Pair encapsulate(byte[] partnerPublicKey) throws Exception; + + /** + * Decapsulates the ciphertext (=secret K encrypted with our public key) received from the partner. + * + * @param secretKey our secret key to use to decrypt the ciphertext + * @param ciphertext the ciphertext + * @return the shared secret K + */ + byte[] decapsulate( + byte[] secretKey, + byte[] ciphertext) throws Exception; +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/kems/KemFactory.java b/tls/src/main/java/org/bouncycastle/tls/injection/kems/KemFactory.java new file mode 100644 index 0000000000..9f9174a3c4 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/kems/KemFactory.java @@ -0,0 +1,6 @@ +package org.bouncycastle.tls.injection.kems; + +public interface KemFactory +{ + KEM create(); +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/kems/TlsAgreementForKEM.java b/tls/src/main/java/org/bouncycastle/tls/injection/kems/TlsAgreementForKEM.java new file mode 100644 index 0000000000..a067b40b46 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/kems/TlsAgreementForKEM.java @@ -0,0 +1,131 @@ +package org.bouncycastle.tls.injection.kems; + +import org.bouncycastle.tls.crypto.TlsAgreement; +import org.bouncycastle.tls.crypto.TlsSecret; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.crypto.impl.jcajce.JceTlsSecret; +import org.openquantumsafe.Pair; + +import java.io.IOException; + + +/** + * #tls-injection + * Wraps a Half-KEM and represents it as a BouncyCastle TlsAgreement. + * In the Half-KEM, keyGen() is invoked at the client side, encapsulate() at the server side, + * and decapsulate() at the client side. + * + * @author Sergejs Kozlovics + */ +public class TlsAgreementForKEM + implements TlsAgreement +{ + private JcaTlsCrypto crypto; + private boolean isServer; + private KEM kem; // delegate + + + // writable object state (=assignable "coordinates"): + private byte[] peerEncapsulated; + protected byte[] clientSecretKey; + protected byte[] serverSecret; + + public TlsAgreementForKEM( + JcaTlsCrypto crypto, + boolean isServer, + KEM kem) + { + this.crypto = crypto; + this.isServer = isServer; + this.kem = kem; + this.peerEncapsulated = null; + this.clientSecretKey = null; + this.serverSecret = null; + } + + public byte[] generateEphemeral() throws IOException + { + + + + if (isServer) + { + // Half-KEM Step2: client <--- peerEncapsulated ciphertext <--- server + if (this.peerEncapsulated == null) + { + throw new IOException("receivePeerValue must be called before generateEphemeral for KEMs"); + } + + Pair p; + + try + { + p = kem.encapsulate(this.peerEncapsulated); // peerEncapsulated === client public key + } catch (Exception e) + { + throw new RuntimeException(e); + } + this.serverSecret = p.getLeft(); // server secret + return p.getRight(); + } + else + { + // Half-KEM Step1: client ---> generated pk ---> server + Pair p; + + try + { + p = kem.keyGen(); + } catch (Exception e) + { + throw new RuntimeException(e); + } + byte[] pk = p.getLeft(); + byte[] sk = p.getRight(); + this.clientSecretKey = sk; + return pk; + } + } + + public void receivePeerValue(byte[] peerEncapsulated) throws IOException + { + this.peerEncapsulated = peerEncapsulated; + } + + public TlsSecret calculateSecret() throws IOException + { + + if (isServer) + { + if (this.serverSecret == null) + { + throw new IOException("Server-side secret has not been generated: generateEphemeral must be called before calculateSecret"); + } + return new JceTlsSecret(this.crypto, this.serverSecret.clone()); // added .clone() since BC may alter the key + } + else + { + if (this.clientSecretKey == null) + { + throw new IOException("Client-side key pair has not been generated: generateEphemeral must be called before calculateSecret"); + } + if (this.peerEncapsulated == null) + { + throw new IOException("receivePeerValue must be called before calculateSecret for KEMs"); + } + + try + { + // Half-KEM Step3: decapsulate at the client + byte[] receivedSecret = kem.decapsulate(this.clientSecretKey, this.peerEncapsulated); + return new JceTlsSecret(this.crypto, receivedSecret.clone()); // added .clone() since BC may alter the key + } catch (Exception e) + { + throw new RuntimeException(e); + } + } + + } + + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/CipherParametersByteKey.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/CipherParametersByteKey.java new file mode 100644 index 0000000000..e0ece96470 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/CipherParametersByteKey.java @@ -0,0 +1,8 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.crypto.CipherParameters; + +public interface CipherParametersByteKey +{ + byte[] byteKey(CipherParameters params); +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigAlgorithm.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigAlgorithm.java new file mode 100644 index 0000000000..fcb0fd6a7c --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigAlgorithm.java @@ -0,0 +1,137 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.tls.SignatureAndHashAlgorithm; + +import java.io.IOException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureSpi; +import java.util.Collection; + +/** + * A class representing injected signature algorithms. #tls-injection + * + * @author Sergejs Kozlovics + */ +public class InjectedSigAlgorithm +{ + private final String algorithmFullName; + private final Collection aliases; + private final ASN1ObjectIdentifier oid; + private final int signatureSchemeCodePoint; + private final SignatureAndHashAlgorithm signatureAndHashAlgorithm; + // ^^^ Just splits the code point (a 2-byte integer) into two separate bytes: + // HighestByte(signatureSchemeCodePoint), LowestByte(signatureSchemeCodePoint). + // Actually, the highest (the second) byte does not necessarily correspond to the hash algorithm, + // but we still use the BC SignatureAndHashAlgorithm class since it is needed internally + // in many places within BC code. + + private final SigAlgAPI api; + + + public InjectedSigAlgorithm( + String algorithmFullName, + Collection aliases, + ASN1ObjectIdentifier oid, + int signatureSchemeCodePoint, + SigAlgAPI api) + { + this.algorithmFullName = algorithmFullName; + this.aliases = aliases; + this.oid = oid; + this.signatureSchemeCodePoint = signatureSchemeCodePoint; + this.signatureAndHashAlgorithm = SignatureAndHashAlgorithmFactory.newFromCodePoint(signatureSchemeCodePoint); + this.api = api; + } + + public String name() + { + return this.algorithmFullName; + } + + public Collection aliases() + { + return this.aliases; + } + + public ASN1ObjectIdentifier oid() + { + return this.oid; + } + + public int codePoint() + { + return this.signatureSchemeCodePoint; + } + + public SignatureAndHashAlgorithm signatureAndHashAlgorithm() + { + return this.signatureAndHashAlgorithm; + } + + public boolean isSupportedParameter(AsymmetricKeyParameter param) + { + return this.api.isSupportedParameter(param); + } + + public boolean isSupportedPublicKey(Key someKey) + { + return this.api.isSupportedPublicKey(someKey); + } + + public boolean isSupportedPrivateKey(Key someKey) + { + return this.api.isSupportedPrivateKey(someKey); + } + + public AsymmetricKeyParameter createPrivateKeyParameter(PrivateKeyInfo keyInfo) throws IOException + { + return this.api.createPrivateKeyParameter(keyInfo); + } + + public PrivateKeyInfo createPrivateKeyInfo( + AsymmetricKeyParameter param, + ASN1Set attributes) throws IOException + { + return this.api.createPrivateKeyInfo(param, attributes); + } + + public AsymmetricKeyParameter createPublicKeyParameter( + SubjectPublicKeyInfo keyInfo, + Object defaultParams) throws IOException + { + return this.api.createPublicKeyParameter(keyInfo, defaultParams); + } + + public SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) throws IOException + { + return this.api.createSubjectPublicKeyInfo(publicKey); + } + + public AsymmetricKeyInfoConverter converter() + { + return this.api; + } + + public byte[] internalEncodingFor(PublicKey key) + { + return this.api.internalEncodingFor(key); + } + + public byte[] internalEncodingFor(PrivateKey key) + { + return this.api.internalEncodingFor(key); + } + + public SignatureSpi signatureSpi(Key key) + { + return this.api.signatureSpi(key); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigAlgsProvider.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigAlgsProvider.java new file mode 100644 index 0000000000..34f928cdb9 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigAlgsProvider.java @@ -0,0 +1,214 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivateKey; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.config.ProviderConfiguration; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.tls.injection.InjectionPoint; + +public class InjectedSigAlgsProvider + extends Provider + implements ConfigurableProvider +{ + private static String info = "TLS Injection Mechanism (TLS-IM) Provider for Injected Signature Algorithms"; + + public static String PROVIDER_NAME = "TLS-IM"; + + public static final ProviderConfiguration CONFIGURATION = null; + + + private static final Map keyInfoConverters = new HashMap(); + + public InjectedSigAlgsProvider() + { + super(PROVIDER_NAME, 1.0, info); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + InjectionPoint.configureProvider(InjectedSigAlgsProvider.this); + return null; + } + }); + } + + + public void setParameter( + String parameterName, + Object parameter) + { + synchronized (CONFIGURATION) + { + //((BouncyCastleProviderConfiguration)CONFIGURATION).setParameter(parameterName, parameter); + } + } + + public boolean hasAlgorithm( + String type, + String name) + { + return containsKey(type + "." + name) || containsKey("Alg.Alias." + type + "." + name); + } + + public void addAlgorithm( + String key, + String value) + { + if (containsKey(key)) + { + throw new IllegalStateException("duplicate provider key (" + key + ") found"); + } + + put(key, value); + } + + public void addAlgorithm( + String key, + String value, + Map attributes) + { + addAlgorithm(key, value); + addAttributes(key, attributes); + } + + public void addAlgorithm( + String type, + ASN1ObjectIdentifier oid, + String className) + { + if (!containsKey(type + "." + className)) + { + throw new IllegalStateException("primary key (" + type + "." + className + ") not found"); + } + + addAlgorithm(type + "." + oid, className); + addAlgorithm(type + ".OID." + oid, className); + } + + public void addAlgorithm( + String type, + ASN1ObjectIdentifier oid, + String className, + Map attributes) + { + addAlgorithm(type, oid, className); + addAttributes(type + "." + oid, attributes); + addAttributes(type + ".OID." + oid, attributes); + } + + public void addKeyInfoConverter( + ASN1ObjectIdentifier oid, + AsymmetricKeyInfoConverter keyInfoConverter) + { + synchronized (keyInfoConverters) + { + keyInfoConverters.put(oid, keyInfoConverter); + } + } + + public AsymmetricKeyInfoConverter getKeyInfoConverter(ASN1ObjectIdentifier oid) + { + return (AsymmetricKeyInfoConverter) keyInfoConverters.get(oid); + } + + public void addAttributes( + String key, + Map attributeMap) + { + for (Iterator it = attributeMap.keySet().iterator(); it.hasNext(); ) + { + String attributeName = (String) it.next(); + String attributeKey = key + " " + attributeName; + if (containsKey(attributeKey)) + { + throw new IllegalStateException("duplicate provider attribute key (" + attributeKey + ") found"); + } + + put(attributeKey, attributeMap.get(attributeName)); + } + } + + private static AsymmetricKeyInfoConverter getAsymmetricKeyInfoConverter(ASN1ObjectIdentifier algorithm) + { + synchronized (keyInfoConverters) + { + return (AsymmetricKeyInfoConverter) keyInfoConverters.get(algorithm); + } + } + + public static PublicKey getPublicKey(SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + AsymmetricKeyInfoConverter converter = getAsymmetricKeyInfoConverter(publicKeyInfo.getAlgorithm().getAlgorithm()); + + if (converter == null) + { + return null; + } + + return converter.generatePublic(publicKeyInfo); + } + + public static PrivateKey getPrivateKey(PrivateKeyInfo privateKeyInfo) + throws IOException + { + AsymmetricKeyInfoConverter converter = getAsymmetricKeyInfoConverter(privateKeyInfo.getPrivateKeyAlgorithm().getAlgorithm()); + + if (converter == null) + { + return null; + } + + return converter.generatePrivate(privateKeyInfo); + } + + static Class loadClass( + Class sourceClass, + final String className) + { + try + { + ClassLoader loader = sourceClass.getClassLoader(); + if (loader != null) + { + return loader.loadClass(className); + } + else + { + return (Class) AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + return Class.forName(className); + } catch (Exception e) + { + // ignore - maybe log? + } + + return null; + } + }); + } + } catch (ClassNotFoundException e) + { + // ignore - maybe log? + } + + return null; + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigVerifiers.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigVerifiers.java new file mode 100644 index 0000000000..26c0726ba7 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigVerifiers.java @@ -0,0 +1,192 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.DigitallySigned; +import org.bouncycastle.tls.crypto.TlsStreamVerifier; +import org.bouncycastle.tls.crypto.TlsVerifier; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; + +/** + * The class holds signature verifiers (e.g., for PQC) + * and is able to build a TlsVerifier for the given public key. + *

+ * #tls-injection + * + * @author Sergejs Kozlovics + */ +public class InjectedSigVerifiers +{ + + public interface VerifySignatureFunction + { + boolean verifySignature( + byte[] data, + byte[] key, + DigitallySigned signature); + } + + private final Map verifiers; // code point -> verifier fn + private final Map converters; // code point -> encoder fn + + public InjectedSigVerifiers() + { + this.verifiers = new HashMap<>(); + this.converters = new HashMap<>(); + } + + public InjectedSigVerifiers(InjectedSigVerifiers origin) + { // clone + this.verifiers = new HashMap<>(origin.verifiers); + this.converters = new HashMap<>(origin.converters); + } + + public void add( + int sigSchemeCodePoint, + VerifySignatureFunction fn, + PublicKeyToByteKey fn2) + { + verifiers.put(sigSchemeCodePoint, fn); + converters.put(sigSchemeCodePoint, fn2); + } + + public boolean contain(int sigSchemeCodePoint) + { + return verifiers.containsKey(sigSchemeCodePoint); + } + + public TlsVerifier tlsVerifier( + JcaTlsCrypto crypto, + PublicKey publicKey, + int sigSchemeCodePoint) + { + VerifySignatureFunction fn = verifiers.get(sigSchemeCodePoint); + PublicKeyToByteKey fn2 = converters.get(sigSchemeCodePoint); + + return new MyTlsVerifier(crypto, publicKey, sigSchemeCodePoint, fn, fn2); + } + + // implementing TlsVerifier via VerifySignatureFunction + private class MyTlsVerifier + implements TlsVerifier + { + private final JcaTlsCrypto crypto; + private final PublicKey publicKey; + private final int signatureScheme; + private final VerifySignatureFunction fn; + private final PublicKeyToByteKey fn2; + + public MyTlsVerifier( + JcaTlsCrypto crypto, + PublicKey publicKey, + int signatureSchemeCodePoint, + VerifySignatureFunction fn, + PublicKeyToByteKey fn2) + { + if (null == crypto) + { + throw new NullPointerException("crypto"); + } + if (null == publicKey) + { + throw new NullPointerException("publicKey"); + } + if (!contain(signatureSchemeCodePoint)) + { + throw new IllegalArgumentException("signatureSchemeCodePoint"); + } + + this.crypto = crypto; + this.publicKey = publicKey; + this.signatureScheme = signatureSchemeCodePoint; + this.fn = fn; + this.fn2 = fn2; + } + + public boolean verifyRawSignature( + DigitallySigned signature, + byte[] hash) throws IOException + { + byte[] encoded = fn2.byteKey(publicKey); + boolean b = fn.verifySignature(hash, encoded, signature); + return b; + } + + private class MyStreamVerifier + implements TlsStreamVerifier + { + + private final PublicKey publicKey; + private final DigitallySigned signature; + private final ByteArrayOutputStream stream; + private final int signatureScheme; + + public MyStreamVerifier( + PublicKey publicKey, + DigitallySigned signature, + int signatureScheme) + { + this.publicKey = publicKey; + this.signature = signature; + this.stream = new ByteArrayOutputStream(); + this.signatureScheme = signatureScheme; + } + + @Override + public OutputStream getOutputStream() throws IOException + { + return this.stream; + } + + @Override + public boolean isVerified() throws IOException + { + + byte[] data = this.stream.toByteArray(); + byte[] key = publicKey.getEncoded(); + + int from = 0; + int priorTo = key.length; + + + /* if liboqs +JNI+DLL is used: // TODO + if (this.signatureScheme==SignatureScheme.oqs_rainbowIclassic) { + from = 24; + Signature verifier = new Signature("Rainbow-I-Classic"); + key = Arrays.copyOfRange(key, from, priorTo); + + boolean b = verifier.verify(data, signature.getSignature(), key); + verifier.dispose_sig(); + return b; + } + else*/ + /* for signatureScheme==SignatureScheme.oqs_sphincsshake256128frobust: + from = 26; // see der.md + + SPHINCSPlusSigner signer = new SPHINCSPlusSigner(); + byte[] pubKey = Arrays.copyOfRange(key, from, priorTo); + SPHINCSPlusPublicKeyParameters params = new SPHINCSPlusPublicKeyParameters(SPHINCSPlusParameters.shake256_128f, pubKey); + signer.init(false, params); + boolean b = signer.verifySignature(data, signature.getSignature()); + return b; + + */ + + // the main functionality of MyTlsVerifier: + return fn.verifySignature(data, key, signature); + } + } + + public TlsStreamVerifier getStreamVerifier(DigitallySigned signature) throws IOException + { + return new MyStreamVerifier(this.publicKey, signature, this.signatureScheme); + + } + } + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigners.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigners.java new file mode 100644 index 0000000000..c3a2d7bccf --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/InjectedSigners.java @@ -0,0 +1,72 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.tls.crypto.TlsSigner; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; + +import java.io.IOException; +import java.security.PrivateKey; +import java.util.HashMap; +import java.util.Map; + +/** + * This class holds a set of signers (e.g., for PQC) + * and can build a TlsSigner for the given private key. + * #tls-injection + * + * @author Sergejs Kozlovics + */ +public class InjectedSigners +{ + + private final Map injectedSigners; + + public InjectedSigners() + { + this.injectedSigners = new HashMap<>(); + } + + public InjectedSigners(InjectedSigners origin) + { // clone + this.injectedSigners = new HashMap<>(origin.injectedSigners); + } + + public void add( + String algorithmFullName, + SignerFunction fn) + { + injectedSigners.put(algorithmFullName, fn); + } + + public boolean contain(String algorithmFullName) + { + return injectedSigners.containsKey(algorithmFullName); + } + + public Iterable getNames() + { + return injectedSigners.keySet(); + } + + public TlsSigner tlsSigner( + JcaTlsCrypto crypto, + PrivateKey privateKey, + String algorithmFullName) + { + // privateKey.getAlgorithm() returns some generinc name, e.g., "DSA" or "SPHINCS+" + // however, we assume that the full algorithm name (with params) has been registered with the signer function; + // thus, we require algorithmFullName to be passed as an argument + + Object fn = injectedSigners.get(algorithmFullName); + if (fn == null) + { + throw new RuntimeException("Algorithm " + algorithmFullName + " not found among signers."); + } + + byte[] skEncoded = privateKey.getEncoded(); + PrivateKeyInfo info = PrivateKeyInfo.getInstance(skEncoded); + + byte[] skBytes = info.getPrivateKey().getOctets(); + return new MyTlsSigner(crypto, skBytes, (SignerFunction) fn); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyMessageSigner.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyMessageSigner.java new file mode 100644 index 0000000000..9bcbbdc1ab --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyMessageSigner.java @@ -0,0 +1,68 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.tls.DigitallySigned; +import org.bouncycastle.tls.SignatureAndHashAlgorithm; + +public class MyMessageSigner + implements MessageSigner +{ + + private SignatureAndHashAlgorithm algorithm; + private SignerFunction fnSign; + private VerifierFunction fnVerify; + private CipherParametersByteKey paramsToPublicKey, paramsToPrivateKey; + + // the following fields are initialized by BC by invoking init(): + private CipherParameters params; + + public MyMessageSigner( + int signatureSchemeCodePoint, + SignerFunction fnSign, + VerifierFunction fnVerify, + CipherParametersByteKey paramsToPublicKey, + CipherParametersByteKey paramsToPrivateKey) + { + this.algorithm = new SignatureAndHashAlgorithm((short) (signatureSchemeCodePoint >> 8), (short) (signatureSchemeCodePoint & 0xFF)); + this.fnSign = fnSign; + this.fnVerify = fnVerify; + this.paramsToPublicKey = paramsToPublicKey; + this.paramsToPrivateKey = paramsToPrivateKey; + } + + @Override + public void init( + boolean forSigning, + CipherParameters param) + { + this.params = param; + } + + + @Override + public byte[] generateSignature(byte[] message) + { + byte[] sk = this.paramsToPrivateKey.byteKey(params); //skParams.getEncoded(); + + byte[] bcSignature = new byte[0]; + try + { + bcSignature = fnSign.sign(null, message, sk); // TODO: do we need to pass crypto instead of null? + } catch (Exception e) + { + throw new RuntimeException(e); + } + return bcSignature; + } + + @Override + public boolean verifySignature( + byte[] message, + byte[] signature) + { + byte[] pk = this.paramsToPublicKey.byteKey(params); + boolean isValid = fnVerify.verify(message, pk, new DigitallySigned(algorithm, signature)); + return isValid; + } +} \ No newline at end of file diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyStreamSigner.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyStreamSigner.java new file mode 100644 index 0000000000..e154ff5cd9 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyStreamSigner.java @@ -0,0 +1,50 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.crypto.TlsStreamSigner; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class MyStreamSigner + implements TlsStreamSigner +{ + + private JcaTlsCrypto crypto; + + private SignerFunction fn; + private byte[] key; + private ByteArrayOutputStream os = new ByteArrayOutputStream(); + + public MyStreamSigner( + JcaTlsCrypto crypto, + byte[] key, + SignerFunction fn) + { + this.crypto = crypto; + this.fn = fn; + this.key = key; + } + + @Override + public OutputStream getOutputStream() throws IOException + { + return os; + } + + @Override + public byte[] getSignature() throws IOException + { + byte[] data = os.toByteArray(); + byte[] signature = new byte[0]; + try + { + signature = fn.sign(this.crypto, data, key); + } catch (Exception e) + { + throw new IOException(e); + } + return signature; + } +} \ No newline at end of file diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyTls13Verifier.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyTls13Verifier.java new file mode 100644 index 0000000000..70a426bda5 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyTls13Verifier.java @@ -0,0 +1,100 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.crypto.Tls13Verifier; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.security.*; + +public class MyTls13Verifier + implements Tls13Verifier +{ + private final PublicKey publicKey; + private final SignatureSpi verifier; + + private ByteArrayOutputStream os = new ByteArrayOutputStream(); + + public MyTls13Verifier( + PublicKey publicKey, + SignatureSpi verifier) + { + this.publicKey = publicKey; + this.verifier = verifier; + } + + private Method findDirectOrInheritedMethod( + Class c, + String methodName, + Class... args) + { + Method m = null; + while (c != null) + { + for (Method mm : c.getDeclaredMethods()) + { + // this is an optimization: we don't check all arg types, just their number + // (for SignatureSpi-s that's sufficient) + if (mm.getName().equals(methodName) && (args.length == mm.getParameterTypes().length)) + { + m = mm; + } + } + if (m != null) + { + break; + } + c = c.getSuperclass(); + } + m.setAccessible(true); + return m; + } + + public final OutputStream getOutputStream() throws IOException + { + return os; + } + + public final boolean verifySignature(byte[] signature) throws IOException + { + try + { + Class c = verifier.getClass(); + + Method m = this.findDirectOrInheritedMethod(c, "engineInitVerify", PublicKey.class); + m.invoke(verifier, publicKey); + + m = this.findDirectOrInheritedMethod(c, "engineUpdate", byte[].class, int.class, int.class); + byte[] data = os.toByteArray(); + m.invoke(verifier, data, 0, data.length); + + m = this.findDirectOrInheritedMethod(c, "engineVerify", byte[].class); + Object result = m.invoke(verifier, signature); + + return (boolean) result; + } catch (Exception e) + { + throw new RuntimeException(e); + } + } + + public static boolean isPresentInCallStack() + { + Exception e = new Exception(); + + StackTraceElement[] stack = e.getStackTrace(); + + // ignore the top of the stack since it will always be MyTls13Verifier + for (int i = 1; i < stack.length; i++) + { + StackTraceElement call = stack[i]; + if (call.getClassName().equals(MyTls13Verifier.class.getName())) + { + return true; + } + } + return false; + } + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyTlsSigner.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyTlsSigner.java new file mode 100644 index 0000000000..cf8466b1db --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/MyTlsSigner.java @@ -0,0 +1,45 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.SignatureAndHashAlgorithm; +import org.bouncycastle.tls.crypto.TlsSigner; +import org.bouncycastle.tls.crypto.TlsStreamSigner; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; + +import java.io.IOException; + +public class MyTlsSigner + implements TlsSigner +{ + private JcaTlsCrypto crypto; + private SignerFunction fn; + private byte[] key; + + public MyTlsSigner( + JcaTlsCrypto crypto, + byte[] key, + SignerFunction fn) + { + this.crypto = crypto; + this.fn = fn; + this.key = key; + } + + public byte[] generateRawSignature( + SignatureAndHashAlgorithm algorithm, + byte[] hash) + throws IOException + { + try + { + return this.fn.sign(this.crypto, hash, key); + } catch (Exception e) + { + throw new IOException(e); + } + } + + public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) throws IOException + { + return new MyStreamSigner(this.crypto, key, this.fn); + } +} \ No newline at end of file diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PrivateKeyToCipherParameters.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PrivateKeyToCipherParameters.java new file mode 100644 index 0000000000..2800deca15 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PrivateKeyToCipherParameters.java @@ -0,0 +1,11 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.crypto.CipherParameters; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; + +public interface PrivateKeyToCipherParameters +{ + CipherParameters parameters(PrivateKey privateKey) throws InvalidKeyException; +} \ No newline at end of file diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PublicKeyToByteKey.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PublicKeyToByteKey.java new file mode 100644 index 0000000000..a4da7a0123 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PublicKeyToByteKey.java @@ -0,0 +1,8 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import java.security.PublicKey; + +public interface PublicKeyToByteKey +{ + byte[] byteKey(PublicKey key); +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PublicKeyToCipherParameters.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PublicKeyToCipherParameters.java new file mode 100644 index 0000000000..facc400034 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/PublicKeyToCipherParameters.java @@ -0,0 +1,11 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.crypto.CipherParameters; + +import java.security.InvalidKeyException; +import java.security.PublicKey; + +public interface PublicKeyToCipherParameters +{ + CipherParameters parameters(PublicKey publicKey) throws InvalidKeyException; +} \ No newline at end of file diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SigAlgAPI.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SigAlgAPI.java new file mode 100644 index 0000000000..bce8df9455 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SigAlgAPI.java @@ -0,0 +1,118 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.tls.DigitallySigned; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.injection.Asn1Bridge; + +import java.io.IOException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureSpi; + +public interface SigAlgAPI + extends AsymmetricKeyInfoConverter, + Asn1Bridge +{ + + ///// BC <-> ASN.1 converters ///// + + /** + * Checks whether the given BC key (public or private) can be converted to ASN.1. + * + * @param bcKey an internal BC representation of a public or a private key + * that has to be converted to ASN.1 + * @return returns true, iff bcKey is of known type and can be converted to ASN.1 + * (i.e., a PrivateKeyInfo or SubjectPublicKeyInfo instance) + */ + boolean isSupportedParameter(AsymmetricKeyParameter bcKey); + + boolean isSupportedPublicKey(Key key); + + boolean isSupportedPrivateKey(Key key); + + /** + * Converts the given private key from ASN.1 to the internal BC representation. + * + * @param asnPrivateKey private key in the ASN.1 notation + * @return internal BC representation of the private key + * @throws IOException + */ + AsymmetricKeyParameter createPrivateKeyParameter(PrivateKeyInfo asnPrivateKey) throws IOException; + + + /** + * Converts the given private key from the internal BC representation to the ASN.1 notation. + * + * @param bcPrivateKey internal BC representation of a private key + * @param attributes ASN.1 attributes to be embedded into the ASN.1 representation + * @return ASN.1 representation of the private key + * @throws IOException + */ + PrivateKeyInfo createPrivateKeyInfo( + AsymmetricKeyParameter bcPrivateKey, + ASN1Set attributes) throws IOException; + + + /** + * Converts the given public key from ASN.1 to the internal BC representation. + * + * @param ansPublicKey public key in the ASN.1 notation + * @param defaultParams some default parameters (currently, null is passed) + * @return internal BC representation of the public key + * @throws IOException + */ + AsymmetricKeyParameter createPublicKeyParameter( + SubjectPublicKeyInfo ansPublicKey, + Object defaultParams) throws IOException; + + /** + * Converts the given public key from the internal BC representation to the ASN.1 notation. + * + * @param bcPublicKey internal BC representation of a public key + * @return ASN.1 representation of the public key + * @throws IOException + */ + SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter bcPublicKey) throws IOException; + + + ///// AsymmetricKeyInfoConverter ///// + PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException; + + PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException; + + ///// Encodings ///// + byte[] internalEncodingFor(PublicKey key); + + byte[] internalEncodingFor(PrivateKey key); + + ///// sign & verify ///// + byte[] sign( + JcaTlsCrypto crypto, + byte[] message, + byte[] privateKey) + throws IOException, Exception; + + boolean verifySignature( + byte[] message, + byte[] publicKey, + DigitallySigned signature); + + ///// SPI ///// + + /** + * Constructs a Java Service Provider Interface (SPI) driver for the current signature algorithm. + * This driver will be used by the DirectSignatureSpi of the TLS injection mechanism. + * + * @param key a public or a private key + * @return a SignatureSpi instance (we suggest using our UniversalSignatureSpi class for that) + */ + SignatureSpi signatureSpi(Key key); +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SignatureAndHashAlgorithmFactory.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SignatureAndHashAlgorithmFactory.java new file mode 100644 index 0000000000..f086b1cb2e --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SignatureAndHashAlgorithmFactory.java @@ -0,0 +1,17 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.SignatureAndHashAlgorithm; + +public class SignatureAndHashAlgorithmFactory +{ + public static SignatureAndHashAlgorithm newFromCodePoint(int signatureSchemeCodePoint) + { + return new SignatureAndHashAlgorithm((short) (signatureSchemeCodePoint >> 8), (short) (signatureSchemeCodePoint & 0xFF)); + } + + public static int codePointFromSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm) + { + int codePoint = (sigAndHashAlgorithm.getHash() << 8) | sigAndHashAlgorithm.getSignature(); + return codePoint; + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SignerFunction.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SignerFunction.java new file mode 100644 index 0000000000..2b43fa8f7c --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/SignerFunction.java @@ -0,0 +1,11 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; + +public interface SignerFunction +{ + byte[] sign( + JcaTlsCrypto crypto, + byte[] data, + byte[] key) throws Exception; +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/VerifierFunction.java b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/VerifierFunction.java new file mode 100644 index 0000000000..984c349812 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/sigalgs/VerifierFunction.java @@ -0,0 +1,11 @@ +package org.bouncycastle.tls.injection.sigalgs; + +import org.bouncycastle.tls.DigitallySigned; + +public interface VerifierFunction +{ + boolean verify( + byte[] message, + byte[] publicKey, + DigitallySigned signature); +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/DirectSignatureSpi.java b/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/DirectSignatureSpi.java new file mode 100644 index 0000000000..f2ace5c99a --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/DirectSignatureSpi.java @@ -0,0 +1,177 @@ +package org.bouncycastle.tls.injection.signaturespi; + + +import org.bouncycastle.tls.injection.InjectionPoint; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.*; + +/** + * The DirectSignatureSpi class acts as a proxy for all injected SignatureSpi-s. + * DirectSignatureSpi can be registered in JCA/JCE providers, since it has the no-arg constructor. + * See, for example, the InjectedSigAlgorithms class, which registers the full class name + * "org.bouncycastle.tls.injection.signaturespi.DirectSignatureSpi" when configuring a provider. + * + * Internally, DirectSignatureSpi tries all injected SignatureSpi factories until + * some factory returns a valid SignatureSpi. Then this SignatureSpi is used as a delegate + * to which SignatureSpi method invocations are forwarded (via Java reflection due to protected method declarations). + * + * #tls-injection + * + * @author Sergejs Kozlovics + */ +public class DirectSignatureSpi extends java.security.SignatureSpi +{ + + + private java.security.SignatureSpi delegate = null; // will be initialized in engineInitVerify + + public DirectSignatureSpi() // must be no-arg constructor, full class name is used within the provider + { + } + + private Method findDirectOrInheritedMethod(Class c, String methodName, Class... args) { + Method m = null; + while (c!=null) { + for (Method mm : c.getDeclaredMethods()) { + // this is an optimization: we don't check all arg types, just their number + // (for SignatureSpi-s that's sufficient) + if (mm.getName().equals(methodName) && (args.length == mm.getParameterTypes().length)) + m = mm; + } + if (m!=null) + break; + c = c.getSuperclass(); + } + return m; + } + + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + + delegate = InjectionPoint.sigAlgs().signatureSpiFor(publicKey); + + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineInitVerify", PublicKey.class); + if (m==null) + throw new RuntimeException("Method engineInitVerify not found"); + + try { + m.setAccessible(true); + m.invoke(delegate, publicKey); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + delegate = InjectionPoint.sigAlgs().signatureSpiFor(privateKey); + + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineInitSign", PrivateKey.class); + if (m==null) + throw new RuntimeException("Method engineInitSign not found"); + + try { + m.setAccessible(true); + m.invoke(delegate, privateKey); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineUpdate", Byte.TYPE); + if (m==null) + throw new RuntimeException("Method engineUpdate(1) not found"); + + try { + m.setAccessible(true); + m.invoke(delegate, b); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineUpdate", Array.class, Integer.TYPE, Integer.TYPE); + if (m==null) + throw new RuntimeException("Method engineUpdate(3) not found"); + try { + m.setAccessible(true); + m.invoke(delegate, b, off, len); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected byte[] engineSign() throws SignatureException { + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineSign"); + if (m==null) + throw new RuntimeException("Method engineSign not found"); + + try { + m.setAccessible(true); + return (byte[])m.invoke(delegate); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineVerify", Array.class); + if (m==null) + throw new RuntimeException("Method engineVerify not found"); + + try { + m.setAccessible(true); + return (boolean) m.invoke(delegate, sigBytes); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineSetParameter", String.class, Object.class); + if (m==null) + throw new RuntimeException("Method engineSetParameter not found"); + + try { + m.setAccessible(true); + m.invoke(delegate, param, value); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + Class c = delegate.getClass(); // searching for the method in the class or in base classes + Method m = findDirectOrInheritedMethod(c, "engineGetParameter", String.class); + if (m==null) + throw new RuntimeException("Method engineGetParameter not found"); + + try { + m.setAccessible(true); + return m.invoke(delegate, param); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/UniversalKeyFactorySpi.java b/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/UniversalKeyFactorySpi.java new file mode 100644 index 0000000000..31df686418 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/UniversalKeyFactorySpi.java @@ -0,0 +1,120 @@ +package org.bouncycastle.tls.injection.signaturespi; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.tls.injection.InjectionPoint; + +public class UniversalKeyFactorySpi + extends KeyFactorySpi + implements AsymmetricKeyInfoConverter +{ + public UniversalKeyFactorySpi() { // must be no-arg constructor, full class name is used within the provider + } + + public PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PKCS8EncodedKeySpec) + { + // get the DER-encoded Key according to PKCS#8 from the spec + byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + + try + { + return generatePrivate(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey))); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + throw new InvalidKeySpecException("Unsupported key specification: " + + keySpec.getClass() + "."); + } + + public PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof X509EncodedKeySpec) + { + // get the DER-encoded Key according to X.509 from the spec + byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded(); + + // decode the SubjectPublicKeyInfo data structure to the pki object + try + { + return generatePublic(SubjectPublicKeyInfo.getInstance(encKey)); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + throw new InvalidKeySpecException("Unknown key specification: " + keySpec + "."); + } + + public final KeySpec engineGetKeySpec(Key someKey, Class keySpec) + throws InvalidKeySpecException + { + if (InjectionPoint.sigAlgs().isSupportedPrivateKey(someKey)) + { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new PKCS8EncodedKeySpec(someKey.getEncoded()); + } + } + else if (InjectionPoint.sigAlgs().isSupportedPublicKey(someKey)) + { + if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new X509EncodedKeySpec(someKey.getEncoded()); + } + } + else + { + throw new InvalidKeySpecException("Unsupported key type: " + + someKey.getClass() + "."); + } + + throw new InvalidKeySpecException("Unknown key specification: " + + keySpec + "."); + } + + public final Key engineTranslateKey(Key someKey) + throws InvalidKeyException + { + if (InjectionPoint.sigAlgs().isSupportedPublicKey(someKey) || InjectionPoint.sigAlgs().isSupportedPrivateKey(someKey)) + { + return someKey; + } + + throw new InvalidKeyException("Unsupported key type: "+someKey.getClass()+"."); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + return InjectionPoint.sigAlgs().generatePrivate(keyInfo); + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + return InjectionPoint.sigAlgs().generatePublic(keyInfo); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/UniversalSignatureSpi.java b/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/UniversalSignatureSpi.java new file mode 100644 index 0000000000..d8b8a91bce --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/injection/signaturespi/UniversalSignatureSpi.java @@ -0,0 +1,108 @@ +package org.bouncycastle.tls.injection.signaturespi; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.tls.injection.sigalgs.PrivateKeyToCipherParameters; +import org.bouncycastle.tls.injection.sigalgs.PublicKeyToCipherParameters; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +public class UniversalSignatureSpi + extends java.security.SignatureSpi { + private final Digest digest; + private final MessageSigner signer; + + + private PublicKeyToCipherParameters pkToParams; + private PrivateKeyToCipherParameters skToParams; + + public UniversalSignatureSpi(Digest digest, MessageSigner signer, + PublicKeyToCipherParameters pkToParams, + PrivateKeyToCipherParameters skToParams) { + this.digest = digest; + this.signer = signer; + this.pkToParams = pkToParams; + this.skToParams = skToParams; + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + CipherParameters params = this.pkToParams.parameters(publicKey); + signer.init(false, params); + } + + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + this.appRandom = random; + engineInitSign(privateKey); + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + CipherParameters params = this.skToParams.parameters(privateKey); + signer.init(false, params); + + + if (appRandom != null) { + signer.init(true, new ParametersWithRandom(params, appRandom)); + } else { + signer.init(true, params); + } + } + + protected void engineUpdate(byte b) + throws SignatureException { + digest.update(b); + } + + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + try { + byte[] sig = signer.generateSignature(hash); + + return sig; + } catch (Exception e) { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + + boolean result = signer.verifySignature(hash, sigBytes); + return result; + } + + protected void engineSetParameter(AlgorithmParameterSpec params) { + // TODO + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec) + */ + protected void engineSetParameter(String param, Object value) { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter(String param) { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + +} +