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

Add Ed25519 DSA #394

Merged
merged 14 commits into from
Aug 28, 2024
Merged

Add Ed25519 DSA #394

merged 14 commits into from
Aug 28, 2024

Conversation

sp717
Copy link
Contributor

@sp717 sp717 commented Jul 29, 2024

Issue #, if available:

Description of changes:

Description:

This PR adds Ed25519 digital signatures. The relevant classes and functions are added as part of ACCP’s EVP Key, Signature, and Keyfactory classes. Ed25519 pre hash mode has not been implemented, as it is dependent on the JDK 15+ interfaces that we cannot currently access.

We are unable to use the NamedParameterSpec, EdECKey, EdECPrivateKey, and EdECPublicKey classes for our implementation, as ACCP must be compiled on JDK 8, and the EdDSA classes are only available on JDK versions 15+. As a result, the implementation utilizes the PrivateKey and PublicKey parent classes available in older java versions for Ed keys.

SignatureRaw from the EVP API was used for Ed25519 signatures, as this form of digital signatures does not support digest updates. The signing process can only take place after the entire message has been received (one-shot signing). EvpSignatureRaw matches this functionality (only updating the message via a java buffer and passing the complete message to native methods for signing at the end), whereas the standard EvpSignature allows for partial signing and verifying.

The pkcs82evp native method was updated to allow the ACCP key factory to import BouncyCastle (BC) private keys. BC encodes the EdDSA public key as an additional field in the PKCS8 encoding of the private key. This caused issues when importing the private key to ACCP, as the method used for the translation (d2i_PKCS8_PRIV_KEY_INFO) was unable to process the additional information. We switched the implementation to use d2i_PrivateKey instead, which successfully pulls the private key from the PKCS8 encoding, regardless of the additional field.

To fix versioning issues for JDK < 15, JUnit flags were used for tests, and a boolean checking the Java version was used to conditionally register Ed25519 in AmazonCorrettoCryptoProvider.java.

Testing:

New unit tests are included in EdDSATest.java. These include tests for invalid inputs, key generation, sign/verify functionality, and interoperability with the BC and SunEC providers.

To Do:

  • - Update Readme
  • - Update Changelog
  • - Add any missing unit tests
  • - Update Threadstorm test -
    private static final KeyPairGenerator RSA_KEY_GEN;
    private static final KeyPairGenerator EC_KEY_GEN;
    private static final KeyPair PAIR_RSA_1024_OR_DEFAULT;
    private static final KeyPair PAIR_RSA_2048;
    private static final KeyPair PAIR_RSA_4096;
    private static final KeyPair PAIR_EC_P256;
    private static final KeyPair PAIR_EC_P384;
    private static final KeyPair PAIR_EC_P521;

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@sp717 sp717 marked this pull request as ready for review August 7, 2024 18:38
@sp717 sp717 requested a review from a team as a code owner August 7, 2024 18:38
@geedo0
Copy link
Contributor

geedo0 commented Aug 7, 2024

Git soapbox incoming - It's good practice to squash your commits prior to submitting your code a review (especially when you've got a messy history like this). That way, you can hide all of the intermediate states of the code from the reviewers and starts the whole thing off on a cleaner slate. Subsequent iterations once the review has started should not be squashed to allow for inter-revision diffs. Once the PR is approved we'll do a squash and rebase to cleanly apply it to the trunk.

@geedo0
Copy link
Contributor

geedo0 commented Aug 8, 2024

We are unable to use the NamedParameterSpec, EdECKey, EdECPrivateKey, and EdECPublicKey classes for our implementation, as ACCP must be compiled on JDK 8, and the EdDSA classes are only available on JDK versions 15+. As a result, the implementation utilizes the PrivateKey and PublicKey parent classes available in older java versions for Ed keys.

This sounds like a short term one-way door decision that we'd be stuck with for a long time. This breaks forwards compatibility with Java code that implements against the expected JDK classes. e.g. If the JSSE were to use EdDSA it would not be able to delegate to ACCP for the implementation without an upstream code change. Do we have a story around how we may add compatibility for the standard Java classes? How much investigation was done around working around the JDK 8 limitation and solving that? Do we have hard requirements to support Ed25519 for JDK8 code bases?

Copy link
Contributor

@geedo0 geedo0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covered key generation. Pending KeyFactory, Signature, and Tests.

src/com/amazon/corretto/crypto/provider/EvpKeyType.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EdGen.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EdGen.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java Outdated Show resolved Hide resolved
csrc/ed_gen.cpp Outdated Show resolved Hide resolved
csrc/java_evp_keys.cpp Outdated Show resolved Hide resolved
csrc/keyutils.cpp Outdated Show resolved Hide resolved
csrc/sign.cpp Show resolved Hide resolved
Copy link
Contributor

@geedo0 geedo0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clicked the wrong button

csrc/java_evp_keys.cpp Outdated Show resolved Hide resolved
csrc/keyutils.cpp Outdated Show resolved Hide resolved
@sp717
Copy link
Contributor Author

sp717 commented Aug 8, 2024

We are unable to use the NamedParameterSpec, EdECKey, EdECPrivateKey, and EdECPublicKey classes for our implementation, as ACCP must be compiled on JDK 8, and the EdDSA classes are only available on JDK versions 15+. As a result, the implementation utilizes the PrivateKey and PublicKey parent classes available in older java versions for Ed keys.

This sounds like a short term one-way door decision that we'd be stuck with for a long time. This breaks forwards compatibility with Java code that implements against the expected JDK classes. e.g. If the JSSE were to use EdDSA it would not be able to delegate to ACCP for the implementation without an upstream code change. Do we have a story around how we may add compatibility for the standard Java classes? How much investigation was done around working around the JDK 8 limitation and solving that? Do we have hard requirements to support Ed25519 for JDK8 code bases?

I don't think it would be too much work to add compatibility for the standard Java classes if they became available. The EvpEdPrivateKey class would just need to implement EdEcPrivateKey (the java class name) instead of PrivateKey, and an override tag would need to be added to the getPrivateKey method. The EvpEdPublicKey class would need the parent class to be changed, and also the getPoint method to be implemented (the EdEcPoint class doesn't exist until JDK 15, so the method can't be implemented yet). The EvpEdKey parent class would have to implement the EdEcKey class from Java, and a getParams method would need to be added (returns relevant NamedParameterSpec).

NamedParameterSpec is used to specify default parameters for Ed25519, Ed448, X25519, and X448. This isn't an issue at the moment, since we just default to Ed25519, but might become necessary in the future if the rest are added.

The reason we must support JDK8 is that to build ACCP, everything must compile on JDK 8 (See this file). We tried removing that line (and the --release 9 line above it), but it caused errors with JNI.

@geedo0
Copy link
Contributor

geedo0 commented Aug 12, 2024

The reason we must support JDK8 is that to build ACCP, everything must compile on JDK 8 (See this file). We tried removing that line (and the --release 9 line above it), but it caused errors with JNI.

This is what I was most curious about. How far along this line of inquiry you went... It would be interesting to know if there's some way for us to conditionally compile source code that imports JDK15 classes if the correct JDK is available to make this body of work "more complete" and compatible with JDK15+ code bases. I also understand that the build as it stands requires JDK8, but I wanted to know if we had a specific requirement to support ed25519 on JDK8. Lacking that requirement opens the door for us to avoid these kinds of private namespace workarounds.

csrc/ed_gen.cpp Outdated Show resolved Hide resolved
csrc/java_evp_keys.cpp Outdated Show resolved Hide resolved
csrc/sign.cpp Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EdGen.java Outdated Show resolved Hide resolved
csrc/ed_gen.cpp Show resolved Hide resolved
@sp717 sp717 requested a review from amirhosv August 16, 2024 18:12
geedo0
geedo0 previously approved these changes Aug 19, 2024
Copy link
Contributor

@geedo0 geedo0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks for getting this all to work!


EdGen(AmazonCorrettoCryptoProvider provider) {
Loader.checkNativeLibraryAvailability();
provider_ = provider;
try {
kf = KeyFactory.getInstance("EdDSA", "SunEC");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit concerned about the FIPS implications. It looks like you're trying to export the KeyPair using JDK based interfaces by tumbling our Key classes into the SunEC KeyFactory. This helps with the interop story, but like we covered earlier there's a whole lot more work to get full interop working and doing it piecemeal can be counterproductive. Since this is along the lines of ASN.1 encode/decode it's probably okay, but it does break down our "all crypto comes from AWS-LC" story. Would like to get some more consensus from the team on how we should handle this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • There are places that ACCP relies on other providers for ASN.1 encode/decode, like GCM parameters.
  • Without this, one cannot use keys generated by ACCP to perform sign/verify with another provider.
  • It is expected that the private (public) key generated by ACCP can be type cast to EdECPrivateKey (EdECPublicKey).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to increasing our tacit dependency on JDK providers, running this through SunEC's KeyFactory also has some performance costs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in favor of keeping this change to increase compatibility. This issue was accidentally caught by one of our own basic cross-compatibility unit tests between SunEC and ACCP. Any existing Ed25519 users coming from SunJCE who are thinking about migrating to ACCP will likely add their own similar cross-compatibility unit test, and could easily run into this exact same issue themselves. It's better for ACCP to solve it once, than have many customers re-discover and re-solve this issue themselves.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given likely use-cases for Ed25519 signature key generation, I agree that compatibility/user experience outweigh performance here. We can optimize the latter in the future dependent on customer need.

Copy link
Contributor

@amirhosv amirhosv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments.

final Signature jceSig = Signature.getInstance("Ed25519", "SunEC");

// Sign with ACCP and verify with SunEC
final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have more tests here? Maybe more messages and of different lengths: from 1 byte to 1024?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@sp717 sp717 requested a review from geedo0 August 26, 2024 19:46
geedo0
geedo0 previously approved these changes Aug 26, 2024
Copy link
Contributor

@amirhosv amirhosv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments.

csrc/ed_gen.cpp Outdated Show resolved Hide resolved

size_t bufSize;

CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, NULL, &bufSize) == 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does EVP_PKEY_get_raw_private_key(key, NULL, &bufSize) always return the exact size? If yes, please document it here.

Copy link
Contributor Author

@sp717 sp717 Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sets bufSize to the size and returns 1 on success. The == 1 check here is just to confirm that the method was successful.

csrc/java_evp_keys.cpp Outdated Show resolved Hide resolved
csrc/java_evp_keys.cpp Show resolved Hide resolved
csrc/keyutils.cpp Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EdGen.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EdGen.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java Outdated Show resolved Hide resolved
src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java Outdated Show resolved Hide resolved
geedo0
geedo0 previously approved these changes Aug 28, 2024
alexw91
alexw91 previously approved these changes Aug 28, 2024
@sp717 sp717 dismissed stale reviews from alexw91 and geedo0 via 85fd519 August 28, 2024 17:50
@alexw91 alexw91 merged commit 0bfd4f2 into corretto:main Aug 28, 2024
10 checks passed
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.

6 participants