Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite post-quantum signatures experimental implementation #1546

Merged
merged 6 commits into from
Mar 16, 2024

Conversation

Honzaik
Copy link
Contributor

@Honzaik Honzaik commented Dec 10, 2023

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 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

if (key instanceof CompositePublicKey && X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
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 and others added 2 commits March 5, 2024 12:01
…xception that the Dilithium private key seems to have a different encoding. separated samples from CertTest into resource files.
@dghgit dghgit self-assigned this Mar 7, 2024
@hubot hubot merged commit ed53532 into bcgit:main Mar 16, 2024
@dghgit
Copy link
Contributor

dghgit commented Mar 19, 2024

Merged with minor revisions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants