Composite post-quantum signatures experimental implementation #1546
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 54b11f8
and fb00c39.
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 fb00c39#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 under2.16.840.1.114027.80.8.1.4
and alternativelyMLDSA44andECDSAP256
.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.
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
bc-java/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
Line 623 in d22bc14
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.
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