Skip to content

Commit 20f3907

Browse files
authored
Merge pull request eclipse-biscuit#108 from itstheceo/ec_p256
Add ECDSA key support (SECP256R1) again
2 parents 4e91e47 + a27f89b commit 20f3907

30 files changed

+679
-2552
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@
256256
<artifactId>gson</artifactId>
257257
<version>${gson.version}</version>
258258
</dependency>
259+
<dependency>
260+
<groupId>org.bouncycastle</groupId>
261+
<artifactId>bcprov-jdk18on</artifactId>
262+
<version>1.79</version>
263+
</dependency>
259264
<dependency>
260265
<groupId>org.junit.jupiter</groupId>
261266
<artifactId>junit-jupiter</artifactId>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.biscuitsec.biscuit.crypto;
2+
3+
import biscuit.format.schema.Schema;
4+
import net.i2p.crypto.eddsa.EdDSAEngine;
5+
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
6+
import net.i2p.crypto.eddsa.EdDSAPublicKey;
7+
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
8+
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
9+
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
10+
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
11+
import org.biscuitsec.biscuit.token.builder.Utils;
12+
13+
import java.security.MessageDigest;
14+
import java.security.NoSuchAlgorithmException;
15+
import java.security.PrivateKey;
16+
import java.security.SecureRandom;
17+
import java.security.Signature;
18+
19+
final class Ed25519KeyPair extends KeyPair {
20+
21+
static final int SIGNATURE_LENGTH = 64;
22+
23+
private final EdDSAPrivateKey privateKey;
24+
private final EdDSAPublicKey publicKey;
25+
26+
private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
27+
28+
public Ed25519KeyPair(byte[] bytes) {
29+
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, ed25519);
30+
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
31+
32+
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
33+
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
34+
35+
this.privateKey = privKey;
36+
this.publicKey = pubKey;
37+
}
38+
39+
public Ed25519KeyPair(SecureRandom rng) {
40+
byte[] b = new byte[32];
41+
rng.nextBytes(b);
42+
43+
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
44+
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
45+
46+
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
47+
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
48+
49+
this.privateKey = privKey;
50+
this.publicKey = pubKey;
51+
}
52+
53+
public Ed25519KeyPair(String hex) {
54+
this(Utils.hexStringToByteArray(hex));
55+
}
56+
57+
public static java.security.PublicKey decode(byte[] data) {
58+
return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, ed25519));
59+
}
60+
61+
public static Signature getSignature() throws NoSuchAlgorithmException {
62+
return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
63+
}
64+
65+
@Override
66+
public byte[] toBytes() {
67+
return privateKey.getSeed();
68+
}
69+
70+
@Override
71+
public String toHex() {
72+
return Utils.byteArrayToHexString(toBytes());
73+
}
74+
75+
@Override
76+
public java.security.PublicKey publicKey() {
77+
return publicKey;
78+
}
79+
80+
@Override
81+
public PrivateKey private_key() {
82+
return privateKey;
83+
}
84+
85+
@Override
86+
public PublicKey public_key() {
87+
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.publicKey);
88+
}
89+
}
Lines changed: 26 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,63 @@
11
package org.biscuitsec.biscuit.crypto;
22

33

4-
import biscuit.format.schema.Schema;
54
import biscuit.format.schema.Schema.PublicKey.Algorithm;
6-
import net.i2p.crypto.eddsa.EdDSAEngine;
7-
import org.biscuitsec.biscuit.token.builder.Utils;
8-
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
9-
import net.i2p.crypto.eddsa.EdDSAPublicKey;
10-
import net.i2p.crypto.eddsa.spec.*;
5+
import net.i2p.crypto.eddsa.Utils;
116

12-
import java.security.MessageDigest;
137
import java.security.NoSuchAlgorithmException;
148
import java.security.SecureRandom;
159
import java.security.Signature;
1610

1711
/**
18-
* Private and public key
12+
* Private and public key.
1913
*/
20-
public final class KeyPair {
21-
public final EdDSAPrivateKey private_key;
22-
public final EdDSAPublicKey public_key;
14+
public abstract class KeyPair {
2315

24-
private static final int ED25519_PUBLIC_KEYSIZE = 32;
25-
private static final int ED25519_PRIVATE_KEYSIZE = 64;
26-
private static final int ED25519_SEED_SIZE = 32;
27-
public static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
28-
29-
public KeyPair() {
30-
this(new SecureRandom());
16+
public static KeyPair generate(Algorithm algorithm) {
17+
return generate(algorithm, new SecureRandom());
3118
}
3219

33-
public KeyPair(final SecureRandom rng) {
34-
byte[] b = new byte[32];
35-
rng.nextBytes(b);
36-
37-
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
38-
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
39-
40-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
41-
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
42-
43-
this.private_key = privKey;
44-
this.public_key = pubKey;
20+
public static KeyPair generate(Algorithm algorithm, String hex) {
21+
return generate(algorithm, Utils.hexToBytes(hex));
4522
}
4623

47-
public static KeyPair generate(Algorithm algorithm) {
48-
return generate(algorithm, new SecureRandom());
24+
public static KeyPair generate(Algorithm algorithm, byte[] bytes) {
25+
if (algorithm == Algorithm.Ed25519) {
26+
return new Ed25519KeyPair(bytes);
27+
} else if (algorithm == Algorithm.SECP256R1) {
28+
return new SECP256R1KeyPair(bytes);
29+
} else {
30+
throw new IllegalArgumentException("Unsupported algorithm");
31+
}
4932
}
5033

5134
public static KeyPair generate(Algorithm algorithm, SecureRandom rng) {
5235
if (algorithm == Algorithm.Ed25519) {
53-
return new KeyPair(rng);
36+
return new Ed25519KeyPair(rng);
37+
} else if (algorithm == Algorithm.SECP256R1) {
38+
return new SECP256R1KeyPair(rng);
5439
} else {
5540
throw new IllegalArgumentException("Unsupported algorithm");
5641
}
5742
}
5843

5944
public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException {
6045
if (algorithm == Algorithm.Ed25519) {
61-
return KeyPair.getSignature();
46+
return Ed25519KeyPair.getSignature();
47+
} else if (algorithm == Algorithm.SECP256R1) {
48+
return SECP256R1KeyPair.getSignature();
6249
} else {
6350
throw new NoSuchAlgorithmException("Unsupported algorithm");
6451
}
6552
}
6653

67-
public byte[] toBytes() {
68-
return this.private_key.getSeed();
69-
}
70-
71-
public KeyPair(byte[] b) {
72-
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
73-
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
74-
75-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
76-
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
77-
78-
this.private_key = privKey;
79-
this.public_key = pubKey;
80-
}
81-
82-
public String toHex() {
83-
return Utils.byteArrayToHexString(this.toBytes());
84-
}
85-
86-
public KeyPair(String hex) {
87-
byte[] b = Utils.hexStringToByteArray(hex);
88-
89-
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
90-
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
54+
public abstract byte[] toBytes();
9155

92-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
93-
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
56+
public abstract String toHex();
9457

95-
this.private_key = privKey;
96-
this.public_key = pubKey;
97-
}
58+
public abstract java.security.PublicKey publicKey();
9859

99-
public static Signature getSignature() throws NoSuchAlgorithmException {
100-
return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
101-
}
60+
public abstract java.security.PrivateKey private_key();
10261

103-
public PublicKey public_key() {
104-
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.public_key);
105-
}
62+
public abstract PublicKey public_key();
10663
}

src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,46 @@
22

33
import biscuit.format.schema.Schema;
44
import biscuit.format.schema.Schema.PublicKey.Algorithm;
5+
import net.i2p.crypto.eddsa.EdDSAPublicKey;
56
import org.biscuitsec.biscuit.error.Error;
67
import org.biscuitsec.biscuit.token.builder.Utils;
78
import com.google.protobuf.ByteString;
8-
import net.i2p.crypto.eddsa.EdDSAPublicKey;
9-
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
9+
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
1010

11-
import static org.biscuitsec.biscuit.crypto.KeyPair.ed25519;
11+
import java.util.Optional;
12+
import java.util.Set;
1213

1314
public class PublicKey {
1415

15-
public final EdDSAPublicKey key;
16+
public final java.security.PublicKey key;
1617
public final Algorithm algorithm;
1718

18-
public PublicKey(Algorithm algorithm, EdDSAPublicKey public_key) {
19+
private static final Set<Algorithm> SUPPORTED_ALGORITHMS = Set.of(Algorithm.Ed25519, Algorithm.SECP256R1);
20+
21+
public PublicKey(Algorithm algorithm, java.security.PublicKey public_key) {
1922
this.key = public_key;
2023
this.algorithm = algorithm;
2124
}
2225

2326
public PublicKey(Algorithm algorithm, byte[] data) {
24-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(data, ed25519);
25-
this.key = new EdDSAPublicKey(pubKeySpec);
27+
if (algorithm == Algorithm.Ed25519) {
28+
this.key = Ed25519KeyPair.decode(data);
29+
} else if (algorithm == Algorithm.SECP256R1) {
30+
this.key = SECP256R1KeyPair.decode(data);
31+
} else {
32+
throw new IllegalArgumentException("Invalid algorithm");
33+
}
2634
this.algorithm = algorithm;
2735
}
2836

2937
public byte[] toBytes() {
30-
return this.key.getAbyte();
38+
if (algorithm == Algorithm.Ed25519) {
39+
return ((EdDSAPublicKey) key).getAbyte();
40+
} else if (algorithm == Algorithm.SECP256R1) {
41+
return ((BCECPublicKey) key).getQ().getEncoded(true); // true = compressed
42+
} else {
43+
throw new IllegalArgumentException("Invalid algorithm");
44+
}
3145
}
3246

3347
public String toHex() {
@@ -36,8 +50,13 @@ public String toHex() {
3650

3751
public PublicKey(Algorithm algorithm, String hex) {
3852
byte[] data = Utils.hexStringToByteArray(hex);
39-
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(data, ed25519);
40-
this.key = new EdDSAPublicKey(pubKeySpec);
53+
if (algorithm == Algorithm.Ed25519) {
54+
this.key = Ed25519KeyPair.decode(data);
55+
} else if (algorithm == Algorithm.SECP256R1) {
56+
this.key = SECP256R1KeyPair.decode(data);
57+
} else {
58+
throw new IllegalArgumentException("Invalid algorithm");
59+
}
4160
this.algorithm = algorithm;
4261
}
4362

@@ -49,13 +68,28 @@ public Schema.PublicKey serialize() {
4968
}
5069

5170
static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError.DeserializationError {
52-
if(!pk.hasAlgorithm() || !pk.hasKey() || pk.getAlgorithm() != Algorithm.Ed25519) {
71+
if(!pk.hasAlgorithm() || !pk.hasKey() || !SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) {
5372
throw new Error.FormatError.DeserializationError("Invalid public key");
5473
}
55-
5674
return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray());
5775
}
5876

77+
public static Optional<Error> validateSignatureLength(Algorithm algorithm, int length) {
78+
Optional<Error> error = Optional.empty();
79+
if (algorithm == Algorithm.Ed25519) {
80+
if (length != Ed25519KeyPair.SIGNATURE_LENGTH) {
81+
error = Optional.of(new Error.FormatError.Signature.InvalidSignatureSize(length));
82+
}
83+
} else if (algorithm == Algorithm.SECP256R1) {
84+
if (length < SECP256R1KeyPair.MINIMUM_SIGNATURE_LENGTH || length > SECP256R1KeyPair.MAXIMUM_SIGNATURE_LENGTH) {
85+
error = Optional.of(new Error.FormatError.Signature.InvalidSignatureSize(length));
86+
}
87+
} else {
88+
error = Optional.of(new Error.FormatError.Signature.InvalidSignature("unsupported algorithm"));
89+
}
90+
return error;
91+
}
92+
5993
@Override
6094
public boolean equals(Object o) {
6195
if (this == o) return true;
@@ -73,6 +107,12 @@ public int hashCode() {
73107

74108
@Override
75109
public String toString() {
76-
return "ed25519/" + toHex().toLowerCase();
110+
if (algorithm == Algorithm.Ed25519) {
111+
return "ed25519/" + toHex().toLowerCase();
112+
} else if (algorithm == Algorithm.SECP256R1) {
113+
return "secp256r1/" + toHex().toLowerCase();
114+
} else {
115+
return null;
116+
}
77117
}
78118
}

0 commit comments

Comments
 (0)