Skip to content

Commit f43938f

Browse files
authored
Merge pull request #129 from preuss-adam/apreuss/crypto-providers
Support injection of alternate crypto libraries
2 parents d53cc2d + 821d2c3 commit f43938f

File tree

14 files changed

+294
-77
lines changed

14 files changed

+294
-77
lines changed

pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
<protobuf.version>3.25.5</protobuf.version>
3737
<protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>
3838
<os-maven-plugin.version>1.7.1</os-maven-plugin.version>
39-
<net.i2p.crypto.eddsa.version>0.3.0</net.i2p.crypto.eddsa.version>
4039
<vavr.version>0.10.3</vavr.version>
4140
<re2j.version>1.6</re2j.version>
4241
<gson.version>2.8.9</gson.version>

src/main/java/org/eclipse/biscuit/crypto/Ed25519KeyPair.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
1313
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
1414
import org.bouncycastle.crypto.signers.Ed25519Signer;
15+
import org.bouncycastle.math.ec.rfc8032.Ed25519;
16+
import org.eclipse.biscuit.error.Error;
1517
import org.eclipse.biscuit.token.builder.Utils;
1618

1719
final class Ed25519KeyPair extends KeyPair {
@@ -20,7 +22,11 @@ final class Ed25519KeyPair extends KeyPair {
2022
private final Ed25519PrivateKeyParameters privateKey;
2123
private final Ed25519PublicKeyParameters publicKey;
2224

23-
Ed25519KeyPair(byte[] bytes) {
25+
Ed25519KeyPair(byte[] bytes) throws Error.FormatError.InvalidKeySize {
26+
if (bytes.length != Ed25519.SECRET_KEY_SIZE) {
27+
throw new Error.FormatError.InvalidKeySize(bytes.length);
28+
}
29+
2430
Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(bytes);
2531
Ed25519PublicKeyParameters publicKey = privateKey.generatePublicKey();
2632

src/main/java/org/eclipse/biscuit/crypto/Ed25519PublicKey.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Arrays;
1010
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
1111
import org.bouncycastle.crypto.signers.Ed25519Signer;
12+
import org.eclipse.biscuit.error.Error;
1213

1314
class Ed25519PublicKey extends PublicKey {
1415
private final Ed25519PublicKeyParameters publicKey;
@@ -18,8 +19,14 @@ class Ed25519PublicKey extends PublicKey {
1819
this.publicKey = publicKey;
1920
}
2021

21-
static Ed25519PublicKey loadEd25519(byte[] data) {
22-
return new Ed25519PublicKey(new Ed25519PublicKeyParameters(data));
22+
static Ed25519PublicKey loadEd25519(byte[] data) throws Error.FormatError.InvalidKey {
23+
Ed25519PublicKeyParameters params;
24+
try {
25+
params = new Ed25519PublicKeyParameters(data);
26+
} catch (IllegalArgumentException e) {
27+
throw new Error.FormatError.InvalidKey(e.getMessage());
28+
}
29+
return new Ed25519PublicKey(params);
2330
}
2431

2532
@Override

src/main/java/org/eclipse/biscuit/crypto/KeyPair.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,84 @@
77

88
import biscuit.format.schema.Schema.PublicKey.Algorithm;
99
import java.security.SecureRandom;
10+
import org.eclipse.biscuit.error.Error;
1011
import org.eclipse.biscuit.token.builder.Utils;
1112

1213
/** Private and public key. */
1314
public abstract class KeyPair implements Signer {
15+
public interface Factory {
16+
KeyPair generate(byte[] bytes) throws Error.FormatError.InvalidKeySize;
17+
18+
KeyPair generate(SecureRandom rng);
19+
}
20+
21+
public static final Factory DEFAULT_ED25519_FACTORY =
22+
new Factory() {
23+
@Override
24+
public KeyPair generate(byte[] bytes) throws Error.FormatError.InvalidKeySize {
25+
return new Ed25519KeyPair(bytes);
26+
}
27+
28+
@Override
29+
public KeyPair generate(SecureRandom rng) {
30+
return new Ed25519KeyPair(rng);
31+
}
32+
};
33+
34+
public static final Factory DEFAULT_SECP256R1_FACTORY =
35+
new Factory() {
36+
@Override
37+
public KeyPair generate(byte[] bytes) throws Error.FormatError.InvalidKeySize {
38+
return new SECP256R1KeyPair(bytes);
39+
}
40+
41+
@Override
42+
public KeyPair generate(SecureRandom rng) {
43+
return new SECP256R1KeyPair(rng);
44+
}
45+
};
46+
47+
private static volatile Factory ed25519Factory = DEFAULT_ED25519_FACTORY;
48+
private static volatile Factory secp256r1Factory = DEFAULT_SECP256R1_FACTORY;
1449

1550
public static KeyPair generate(Algorithm algorithm) {
1651
return generate(algorithm, new SecureRandom());
1752
}
1853

19-
public static KeyPair generate(Algorithm algorithm, String hex) {
54+
public static KeyPair generate(Algorithm algorithm, String hex)
55+
throws Error.FormatError.InvalidKeySize {
2056
return generate(algorithm, Utils.hexStringToByteArray(hex));
2157
}
2258

23-
public static KeyPair generate(Algorithm algorithm, byte[] bytes) {
59+
public static KeyPair generate(Algorithm algorithm, byte[] bytes)
60+
throws Error.FormatError.InvalidKeySize {
2461
if (algorithm == Algorithm.Ed25519) {
25-
return new Ed25519KeyPair(bytes);
62+
return ed25519Factory.generate(bytes);
2663
} else if (algorithm == Algorithm.SECP256R1) {
27-
return new SECP256R1KeyPair(bytes);
64+
return secp256r1Factory.generate(bytes);
2865
} else {
2966
throw new IllegalArgumentException("Unsupported algorithm");
3067
}
3168
}
3269

3370
public static KeyPair generate(Algorithm algorithm, SecureRandom rng) {
3471
if (algorithm == Algorithm.Ed25519) {
35-
return new Ed25519KeyPair(rng);
72+
return ed25519Factory != null ? ed25519Factory.generate(rng) : new Ed25519KeyPair(rng);
3673
} else if (algorithm == Algorithm.SECP256R1) {
37-
return new SECP256R1KeyPair(rng);
74+
return secp256r1Factory != null ? secp256r1Factory.generate(rng) : new SECP256R1KeyPair(rng);
3875
} else {
3976
throw new IllegalArgumentException("Unsupported algorithm");
4077
}
4178
}
4279

80+
public static void setEd25519Factory(Factory factory) {
81+
ed25519Factory = factory;
82+
}
83+
84+
public static void setSECP256R1Factory(Factory factory) {
85+
secp256r1Factory = factory;
86+
}
87+
4388
public abstract byte[] toBytes();
4489

4590
public abstract String toHex();

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,32 @@
1717
import org.eclipse.biscuit.token.builder.Utils;
1818

1919
public abstract class PublicKey {
20+
public interface Factory {
21+
PublicKey load(byte[] bytes) throws Error.FormatError.InvalidKey;
22+
}
23+
24+
public static final Factory DEFAULT_ED25519_FACTORY =
25+
bytes -> Ed25519PublicKey.loadEd25519(bytes);
26+
public static final Factory DEFAULT_SECP256R1_FACTORY =
27+
bytes -> SECP256R1PublicKey.loadSECP256R1(bytes);
28+
29+
private static volatile Factory ed25519Factory = DEFAULT_ED25519_FACTORY;
30+
private static volatile Factory secp256r1Factory = DEFAULT_SECP256R1_FACTORY;
2031

2132
private static final Set<Algorithm> SUPPORTED_ALGORITHMS =
2233
Set.of(Algorithm.Ed25519, Algorithm.SECP256R1);
2334

24-
public static PublicKey load(Algorithm algorithm, byte[] data) {
35+
public static PublicKey load(Algorithm algorithm, byte[] data) throws Error.FormatError {
2536
if (algorithm == Algorithm.Ed25519) {
26-
return Ed25519PublicKey.loadEd25519(data);
37+
return ed25519Factory.load(data);
2738
} else if (algorithm == Algorithm.SECP256R1) {
28-
return SECP256R1PublicKey.loadSECP256R1(data);
39+
return secp256r1Factory.load(data);
2940
} else {
3041
throw new IllegalArgumentException("Unsupported algorithm");
3142
}
3243
}
3344

34-
public static PublicKey load(Algorithm algorithm, String hex) {
45+
public static PublicKey load(Algorithm algorithm, String hex) throws Error.FormatError {
3546
return load(algorithm, Utils.hexStringToByteArray(hex));
3647
}
3748

@@ -48,8 +59,7 @@ public Schema.PublicKey serialize() {
4859
return publicKey.build();
4960
}
5061

51-
public static PublicKey deserialize(Schema.PublicKey pk)
52-
throws Error.FormatError.DeserializationError {
62+
public static PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError {
5363
if (!pk.hasAlgorithm() || !pk.hasKey() || !SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) {
5464
throw new Error.FormatError.DeserializationError("Invalid public key");
5565
}
@@ -74,6 +84,14 @@ public static Optional<Error> validateSignatureLength(Algorithm algorithm, int l
7484
return error;
7585
}
7686

87+
public static void setEd25519Factory(Factory factory) {
88+
ed25519Factory = factory;
89+
}
90+
91+
public static void setSECP256R1Factory(Factory factory) {
92+
secp256r1Factory = factory;
93+
}
94+
7795
public abstract Algorithm getAlgorithm();
7896

7997
public abstract boolean verify(byte[] data, byte[] signature)

src/main/java/org/eclipse/biscuit/crypto/SECP256R1KeyPair.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55

66
package org.eclipse.biscuit.crypto;
77

8-
import java.security.InvalidKeyException;
9-
import java.security.NoSuchAlgorithmException;
8+
import java.io.IOException;
109
import java.security.SecureRandom;
11-
import java.security.Security;
12-
import java.security.Signature;
13-
import java.security.SignatureException;
10+
import org.bouncycastle.crypto.digests.SHA256Digest;
11+
import org.bouncycastle.crypto.signers.ECDSASigner;
12+
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
1413
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
1514
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
1615
import org.bouncycastle.jce.ECNamedCurveTable;
@@ -19,6 +18,7 @@
1918
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
2019
import org.bouncycastle.jce.spec.ECPublicKeySpec;
2120
import org.bouncycastle.util.BigIntegers;
21+
import org.eclipse.biscuit.error.Error;
2222
import org.eclipse.biscuit.token.builder.Utils;
2323

2424
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
@@ -31,15 +31,14 @@ final class SECP256R1KeyPair extends KeyPair {
3131
private final BCECPrivateKey privateKey;
3232
private final BCECPublicKey publicKey;
3333

34-
static {
35-
Security.addProvider(new BouncyCastleProvider());
36-
}
37-
3834
static final String ALGORITHM = "ECDSA";
3935
static final String CURVE = "secp256r1";
4036
static final ECNamedCurveParameterSpec SECP256R1 = ECNamedCurveTable.getParameterSpec(CURVE);
4137

42-
SECP256R1KeyPair(byte[] bytes) {
38+
SECP256R1KeyPair(byte[] bytes) throws Error.FormatError.InvalidKeySize {
39+
if (bytes.length != BUFFER_SIZE) {
40+
throw new Error.FormatError.InvalidKeySize(bytes.length);
41+
}
4342
var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1);
4443
var privateKey =
4544
new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION);
@@ -68,18 +67,22 @@ final class SECP256R1KeyPair extends KeyPair {
6867
this.publicKey = publicKey;
6968
}
7069

71-
static Signature getSignature() throws NoSuchAlgorithmException {
72-
return Signature.getInstance(
73-
"SHA256withECDSA", Security.getProvider(BouncyCastleProvider.PROVIDER_NAME));
74-
}
75-
7670
@Override
77-
public byte[] sign(byte[] data)
78-
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
79-
Signature sgr = getSignature();
80-
sgr.initSign(privateKey);
81-
sgr.update(data);
82-
return sgr.sign();
71+
public byte[] sign(byte[] data) {
72+
var digest = new SHA256Digest();
73+
digest.update(data, 0, data.length);
74+
var hash = new byte[digest.getDigestSize()];
75+
digest.doFinal(hash, 0);
76+
77+
var signer = new ECDSASigner();
78+
signer.init(true, privateKey.engineGetKeyParameters());
79+
var sig = signer.generateSignature(hash);
80+
81+
try {
82+
return StandardDSAEncoding.INSTANCE.encode(signer.getOrder(), sig[0], sig[1]);
83+
} catch (IOException e) {
84+
throw new IllegalStateException(e.toString());
85+
}
8386
}
8487

8588
@Override

src/main/java/org/eclipse/biscuit/crypto/SECP256R1PublicKey.java

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,53 @@
66
package org.eclipse.biscuit.crypto;
77

88
import static org.eclipse.biscuit.crypto.SECP256R1KeyPair.CURVE;
9-
import static org.eclipse.biscuit.crypto.SECP256R1KeyPair.getSignature;
109

1110
import biscuit.format.schema.Schema.PublicKey.Algorithm;
12-
import java.security.InvalidKeyException;
13-
import java.security.NoSuchAlgorithmException;
14-
import java.security.SignatureException;
11+
import java.io.IOException;
12+
import java.math.BigInteger;
1513
import java.util.Arrays;
14+
import org.bouncycastle.asn1.sec.SECNamedCurves;
15+
import org.bouncycastle.asn1.x9.X9ECParameters;
16+
import org.bouncycastle.crypto.digests.SHA256Digest;
17+
import org.bouncycastle.crypto.params.ECDomainParameters;
18+
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
19+
import org.bouncycastle.crypto.signers.ECDSASigner;
20+
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
21+
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
1622
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
1723
import org.bouncycastle.jce.ECNamedCurveTable;
1824
import org.bouncycastle.jce.provider.BouncyCastleProvider;
1925
import org.bouncycastle.jce.spec.ECPublicKeySpec;
26+
import org.bouncycastle.math.ec.ECPoint;
27+
import org.eclipse.biscuit.error.Error;
2028

2129
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
2230
class SECP256R1PublicKey extends PublicKey {
2331

32+
private static final X9ECParameters x9ECParameters = SECNamedCurves.getByName("secp256r1");
33+
private static final ECDomainParameters domainParameters =
34+
new ECDomainParameters(
35+
x9ECParameters.getCurve(),
36+
x9ECParameters.getG(),
37+
x9ECParameters.getN(),
38+
x9ECParameters.getH());
39+
2440
private final BCECPublicKey publicKey;
2541

2642
SECP256R1PublicKey(BCECPublicKey publicKey) {
2743
super();
2844
this.publicKey = publicKey;
2945
}
3046

31-
static SECP256R1PublicKey loadSECP256R1(byte[] data) {
47+
static SECP256R1PublicKey loadSECP256R1(byte[] data) throws Error.FormatError.InvalidKey {
3248
var params = ECNamedCurveTable.getParameterSpec(CURVE);
33-
var spec = new ECPublicKeySpec(params.getCurve().decodePoint(data), params);
49+
ECPoint ecPoint;
50+
try {
51+
ecPoint = params.getCurve().decodePoint(data);
52+
} catch (IllegalArgumentException e) {
53+
throw new Error.FormatError.InvalidKey(e.getMessage());
54+
}
55+
var spec = new ECPublicKeySpec(ecPoint, params);
3456
return new SECP256R1PublicKey(
3557
new BCECPublicKey(SECP256R1KeyPair.ALGORITHM, spec, BouncyCastleProvider.CONFIGURATION));
3658
}
@@ -69,11 +91,22 @@ public Algorithm getAlgorithm() {
6991
}
7092

7193
@Override
72-
public boolean verify(byte[] data, byte[] signature)
73-
throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
74-
var sgr = getSignature();
75-
sgr.initVerify(this.publicKey);
76-
sgr.update(data);
77-
return sgr.verify(signature);
94+
public boolean verify(byte[] data, byte[] signature) {
95+
var digest = new SHA256Digest();
96+
digest.update(data, 0, data.length);
97+
var hash = new byte[digest.getDigestSize()];
98+
digest.doFinal(hash, 0);
99+
100+
var signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
101+
signer.init(false, new ECPublicKeyParameters(publicKey.getQ(), domainParameters));
102+
103+
BigInteger[] sig;
104+
try {
105+
sig = StandardDSAEncoding.INSTANCE.decode(signer.getOrder(), signature);
106+
} catch (IOException e) {
107+
throw new IllegalStateException(e.toString());
108+
}
109+
110+
return signer.verifySignature(hash, sig[0], sig[1]);
78111
}
79112
}

0 commit comments

Comments
 (0)