Skip to content

Commit ba45087

Browse files
authored
Merge pull request #1000 from sigstore/ed25519-support
Add support for Ed25519 signatures
2 parents 9658259 + 085b33e commit ba45087

File tree

3 files changed

+131
-7
lines changed

3 files changed

+131
-7
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2025 The Sigstore Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package dev.sigstore.encryption.signers;
17+
18+
import java.security.InvalidKeyException;
19+
import java.security.NoSuchAlgorithmException;
20+
import java.security.PublicKey;
21+
import java.security.Signature;
22+
import java.security.SignatureException;
23+
24+
/** ECDSA verifier, instantiated by {@link Verifiers#newVerifier(PublicKey)}. */
25+
class Ed25519Verifier implements Verifier {
26+
27+
private final PublicKey publicKey;
28+
29+
Ed25519Verifier(PublicKey publicKey) {
30+
this.publicKey = publicKey;
31+
}
32+
33+
@Override
34+
public PublicKey getPublicKey() {
35+
return publicKey;
36+
}
37+
38+
/** Ed25519 verifiers hash implicitly for ed25519 keys. */
39+
@Override
40+
public boolean verify(byte[] artifact, byte[] signature)
41+
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
42+
var verifier = Signature.getInstance("Ed25519");
43+
verifier.initVerify(publicKey);
44+
verifier.update(artifact);
45+
return verifier.verify(signature);
46+
}
47+
48+
@Override
49+
public boolean verifyDigest(byte[] digest, byte[] signature) {
50+
throw new UnsupportedOperationException(
51+
"Ed25519 verification requires an artifact, not a digest.");
52+
}
53+
}

sigstore-java/src/main/java/dev/sigstore/encryption/signers/Verifiers.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.security.NoSuchAlgorithmException;
1919
import java.security.PublicKey;
20+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
21+
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
2022

2123
/** Autodetection for verification algorithms based on public keys used. */
2224
public class Verifiers {
@@ -28,9 +30,21 @@ public static Verifier newVerifier(PublicKey publicKey) throws NoSuchAlgorithmEx
2830
if (publicKey.getAlgorithm().equals("EC") || publicKey.getAlgorithm().equals("ECDSA")) {
2931
return new EcdsaVerifier(publicKey);
3032
}
33+
if (publicKey.getAlgorithm().equals("Ed25519")) {
34+
return new Ed25519Verifier(publicKey);
35+
}
36+
if (publicKey.getAlgorithm().equals("EdDSA")) {
37+
SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
38+
if (spki.getAlgorithm() != null
39+
&& new ASN1ObjectIdentifier("1.3.101.112").equals(spki.getAlgorithm().getAlgorithm())) {
40+
return new Ed25519Verifier(publicKey);
41+
}
42+
throw new NoSuchAlgorithmException(
43+
"Cannot verify signatures for non-Ed25519 EdDSA key types, this client only supports RSA, ECDSA, and Ed25519 verification");
44+
}
3145
throw new NoSuchAlgorithmException(
3246
"Cannot verify signatures for key type '"
3347
+ publicKey.getAlgorithm()
34-
+ "', this client only supports RSA and ECDSA verification");
48+
+ "', this client only supports RSA, ECDSA, and Ed25519 verification");
3549
}
3650
}

sigstore-java/src/test/java/dev/sigstore/encryption/signers/VerifiersTest.java

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,90 @@
1515
*/
1616
package dev.sigstore.encryption.signers;
1717

18+
import java.nio.charset.StandardCharsets;
19+
import java.security.KeyPair;
1820
import java.security.KeyPairGenerator;
1921
import java.security.NoSuchAlgorithmException;
22+
import java.security.Security;
23+
import java.security.Signature;
24+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
2025
import org.junit.jupiter.api.Assertions;
2126
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.condition.EnabledForJreRange;
28+
import org.junit.jupiter.api.condition.JRE;
2229

2330
/** VerifiersTest for failure cases, passing cases are handled in {@link SignerTest}. */
2431
public class VerifiersTest {
32+
private static final byte[] CONTENT = "abcdef".getBytes(StandardCharsets.UTF_8);
2533

2634
@Test
27-
public void signatureAlgorithm_unknown() throws Exception {
28-
var kp = KeyPairGenerator.getInstance("DSA").generateKeyPair();
35+
public void verify_ed25519_withBcProvider() throws Exception {
36+
var kp = genKeyPairWithBcProvider("ed25519");
37+
var signature = genSignature(kp, "ed25519");
38+
var verifier = Verifiers.newVerifier(kp.getPublic());
39+
Assertions.assertTrue(verifier.verify(CONTENT, signature));
40+
}
41+
42+
@Test
43+
public void verify_ed25519_withoutBcProvider() throws Exception {
44+
var kp = genKeyPair("ed25519");
45+
var signature = genSignature(kp, "ed25519");
46+
var verifier = Verifiers.newVerifier(kp.getPublic());
47+
Assertions.assertTrue(verifier.verify(CONTENT, signature));
48+
}
49+
50+
@Test
51+
public void verify_ed448_withBcProvider() throws Exception {
52+
var kp = genKeyPairWithBcProvider("ed448");
53+
var signature = genSignature(kp, "ed448");
2954
var exception =
3055
Assertions.assertThrows(
3156
NoSuchAlgorithmException.class, () -> Verifiers.newVerifier(kp.getPublic()));
3257
Assertions.assertEquals(
33-
exception.getMessage(),
34-
"Cannot verify signatures for key type 'DSA', this client only supports RSA and ECDSA verification");
58+
"Cannot verify signatures for key type 'Ed448', this client only supports RSA, ECDSA, and Ed25519 verification",
59+
exception.getMessage());
60+
}
61+
62+
@Test
63+
@EnabledForJreRange(min = JRE.JAVA_15)
64+
public void verify_ed448_withoutBcProvider() throws Exception {
65+
var kp = genKeyPair("ed448");
66+
var signature = genSignature(kp, "ed448");
67+
var exception =
68+
Assertions.assertThrows(
69+
NoSuchAlgorithmException.class, () -> Verifiers.newVerifier(kp.getPublic()));
70+
Assertions.assertEquals(
71+
"Cannot verify signatures for non-Ed25519 EdDSA key types, this client only supports RSA, ECDSA, and Ed25519 verification",
72+
exception.getMessage());
3573
}
3674

3775
@Test
38-
public void signatureAlgorithmForDigests_unknown() throws Exception {
76+
public void verify_unknown() throws Exception {
3977
var kp = KeyPairGenerator.getInstance("DSA").generateKeyPair();
4078
var exception =
4179
Assertions.assertThrows(
4280
NoSuchAlgorithmException.class, () -> Verifiers.newVerifier(kp.getPublic()));
4381
Assertions.assertEquals(
4482
exception.getMessage(),
45-
"Cannot verify signatures for key type 'DSA', this client only supports RSA and ECDSA verification");
83+
"Cannot verify signatures for key type 'DSA', this client only supports RSA, ECDSA, and Ed25519 verification");
84+
}
85+
86+
private KeyPair genKeyPair(String algorithm) throws Exception {
87+
KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm);
88+
return kpGen.generateKeyPair();
89+
}
90+
91+
private KeyPair genKeyPairWithBcProvider(String algorithm) throws Exception {
92+
Security.addProvider(new BouncyCastleProvider());
93+
94+
KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC");
95+
return kpGen.generateKeyPair();
96+
}
97+
98+
private byte[] genSignature(KeyPair keyPair, String algorithm) throws Exception {
99+
Signature signature = Signature.getInstance(algorithm);
100+
signature.initSign(keyPair.getPrivate());
101+
signature.update(CONTENT);
102+
return signature.sign();
46103
}
47104
}

0 commit comments

Comments
 (0)