bc-java icon indicating copy to clipboard operation
bc-java copied to clipboard

Composite post-quantum signatures experimental implementation

Open Honzaik opened this issue 2 years ago • 0 comments

Hello, I prepared an implementation for post-quantum hybrid composite signatures (from now on just "composites") according to the draft RFC https://www.ietf.org/archive/id/draft-ounsworth-pq-composite-sigs-13.html.

I noticed that there is already some very experimental implementation from 2020-2022 mainly https://github.com/bcgit/bc-java/commit/54b11f8e5ec3ff027a08c3bb5b329257e2bb06fe and https://github.com/bcgit/bc-java/commit/fb00c39eb5e7223c0a05ddee6491aa5139276937.

However, I feel like it is not very user friendly, mainly because the user needs to construct his own composite as shown here for example https://github.com/bcgit/bc-java/commit/fb00c39eb5e7223c0a05ddee6491aa5139276937#diff-23db0e8dde3d96a583b3055a4216b14f33754f0d6ace1d8486bf94b7b1adcb33R197. I understand that it is meant to be a flexible approach to implementing composites but I think that in the future, there will be only a fixed set of recommended combinations (as defined https://www.ietf.org/archive/id/draft-ounsworth-pq-composite-sigs-13.html#name-algorithm-identifiers) and the lack of flexibility is not going to be a major issue. There also seems to be a security benefit to limiting the options for developers as there is a lower chance to make a mistake, e.g., the simplicity of Ed25519 (fixed curve, fixed hash function).

Other drawbacks of the currently implemented approach is that it does not act like a "regular" signature with a fixed name and consequently classes such as https://github.com/bcgit/bc-java/blob/main/pkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java need to handle a special composite key case. In my implementation the composite is just another signature algorithm and there does not need to be a special case for certificate signing/verification etc. Additionally, this allows simpler integration of composites into other libraries that use BouncyCastle, such as Apache Santuario, as adding the support for composites is no different from adding support for another "regular" signature algorithm.

How to work with the composite signatures is illustrated in the provided tests, e.g., https://github.com/Honzaik/bc-java/blob/main/prov/src/test/java/org/bouncycastle/jcajce/provider/test/CompositeSignaturesTest.java#L144C20.

Each composite from https://www.ietf.org/archive/id/draft-ounsworth-pq-composite-sigs-13.html#name-algorithm-identifiers is registered in the provider under a human readable simple name (the naming conventions are described in https://github.com/Honzaik/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositesignatures/CompositeSignaturesConstants.java) and under the OID string. For example, the composite id-MLDSA44-ECDSA-P256-SHA256 is registered under 2.16.840.1.114027.80.8.1.4 and alternatively MLDSA44andECDSAP256.

Adding a new composite signature

Although the implementation does not allow the user to create his own composite, updating the list of composite signatures is fairly simple.

  1. Define the new OID identifier https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/core/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java#L172 (~1 line of code)
  2. Add the OID into the list of supportedIdentifiers, create a new enum value and make a new entry into the compositeNameASN1IdentifierMap https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositesignatures/CompositeSignaturesConstants.java (~3 lines of code)
  3. Add two new cases into switches in https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositesignatures/KeyFactorySpi.java (~8 lines of code)
  4. Create a new boilerplate subclass in https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositesignatures/KeyPairGeneratorSpi.java and specify a new case for its key generators. (~10 lines of code)
  5. Similar thing in https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositesignatures/SignatureSpi.java (~10 lines of code)

In total, adding a new composite combination required only about 30-40 lines of code (assuming the composite components are already implemented in BouncyCastle).

Currently, the only defined composites are always 1 classical signature + 1 post-quantum signature but the construction does not limit the number of composites.

Tests

I have created a few tests for this implementation at https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/prov/src/test/java/org/bouncycastle/jcajce/provider/test/CompositeSignaturesTest.java and also at https://github.com/Honzaik/bc-java/blob/8dce4871d438ab18fc5bc2118405387508673f32/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java#L5426

Legacy COMPOSITE

I have reused some code from the current implementation of composites, mainly in CompositePublicKey and CompositePrivateKey classes. I have decided to keep the current implementation since I did not want to break any current implementations that might use it even experimentally. The current composites implementation is also not really consistent since in https://github.com/bcgit/bc-java/blob/main/pkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java the verification seems to in "AND" mode, i.e., all component signatures need to verify correctly but in https://github.com/bcgit/bc-java/blob/d22bc14853a6dd63564f7a797dc2f840b94ba7f9/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java#L623 it seems to be in "OR" mode, i.e., one of them needs to verify.

Limitations

There is an inconsistency I encountered concerning the compatibility with a sample in the draft https://www.ietf.org/archive/id/draft-ounsworth-pq-composite-sigs-13.html.

  • The sample MLDSA44-ECDSA-P256 private key from A.1.2 has a different encoding for the component Dilithium private key. This is technically unrelated to composites since that is an issue on lower level (encoding of Dilithium private keys). The sample in https://www.ietf.org/archive/id/draft-ounsworth-pq-composite-sigs-13.html seems to be OneAsymmetricKey version 1 (v1) where the privateKey is an OCTET STRING which seems to contain the public key as well (based its size). The BouncyCastle implementation produces OneAsymmetricKey version 2 (v2) with the public key being in its separate field and the privateKey is an OCTET STRING containing another OCTET STRING.

Also, I would like to mention that this is my first time contributing to BouncyCastle therefore I do not know the correct procedures/code style so please correct me where possible. Also note, that I am a beginner regarding ASN.1, therefore, it is possible I misunderstood the draft RFC. Cheers

Honzaik avatar Dec 10 '23 09:12 Honzaik