Skip to content

Commit d92eee4

Browse files
authored
Merge pull request #113 from Korbik/remote_signer_2
Signer interface definition and implementation
2 parents 922cb92 + fddf7a3 commit d92eee4

File tree

14 files changed

+291
-182
lines changed

14 files changed

+291
-182
lines changed

Diff for: pom.xml

+23-1
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@
3232
<vavr.version>0.10.3</vavr.version>
3333
<re2j.version>1.6</re2j.version>
3434
<gson.version>2.8.9</gson.version>
35+
<bcprov.version>1.79</bcprov.version>
3536

3637
<!-- test dependencies -->
3738
<junit.version>5.8.2</junit.version>
39+
<awssdk-kms.version>2.30.2</awssdk-kms.version>
40+
<testcontainers-junit-jupiter.version>1.20.4</testcontainers-junit-jupiter.version>
41+
<testcontainers-localstack.version>1.20.4</testcontainers-localstack.version>
3842
</properties>
3943

4044
<licenses>
@@ -259,13 +263,31 @@
259263
<dependency>
260264
<groupId>org.bouncycastle</groupId>
261265
<artifactId>bcprov-jdk18on</artifactId>
262-
<version>1.79</version>
266+
<version>${bcprov.version}</version>
263267
</dependency>
264268
<dependency>
265269
<groupId>org.junit.jupiter</groupId>
266270
<artifactId>junit-jupiter</artifactId>
267271
<scope>test</scope>
268272
</dependency>
273+
<dependency>
274+
<groupId>software.amazon.awssdk</groupId>
275+
<artifactId>kms</artifactId>
276+
<version>${awssdk-kms.version}</version>
277+
<scope>test</scope>
278+
</dependency>
279+
<dependency>
280+
<groupId>org.testcontainers</groupId>
281+
<artifactId>junit-jupiter</artifactId>
282+
<version>${testcontainers-junit-jupiter.version}</version>
283+
<scope>test</scope>
284+
</dependency>
285+
<dependency>
286+
<groupId>org.testcontainers</groupId>
287+
<artifactId>localstack</artifactId>
288+
<version>${testcontainers-localstack.version}</version>
289+
<scope>test</scope>
290+
</dependency>
269291
</dependencies>
270292
<dependencyManagement>
271293
<dependencies>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.biscuitsec.biscuit.crypto;
2+
3+
import org.biscuitsec.biscuit.token.format.ExternalSignature;
4+
5+
import java.nio.ByteBuffer;
6+
import java.nio.ByteOrder;
7+
import java.util.Optional;
8+
9+
public class BlockSignatureBuffer {
10+
public static byte[] getBufferSignature(PublicKey nextPubKey, byte[] data) {
11+
return getBufferSignature(nextPubKey, data, Optional.empty());
12+
}
13+
14+
public static byte[] getBufferSignature(PublicKey nextPubKey, byte[] data, Optional<ExternalSignature> externalSignature) {
15+
var buffer = ByteBuffer.allocate(4 + data.length + nextPubKey.toBytes().length + externalSignature.map((a) -> a.signature.length).orElse(0)).order(ByteOrder.LITTLE_ENDIAN);
16+
buffer.put(data);
17+
externalSignature.ifPresent(signature -> buffer.put(signature.signature));
18+
buffer.putInt(nextPubKey.algorithm.getNumber());
19+
buffer.put(nextPubKey.toBytes());
20+
buffer.flip();
21+
return buffer.array();
22+
}
23+
24+
public static byte[] getBufferSealedSignature(PublicKey nextPubKey, byte[] data, byte[] blockSignature) {
25+
var buffer = ByteBuffer.allocate(4 + data.length + nextPubKey.toBytes().length + blockSignature.length).order(ByteOrder.LITTLE_ENDIAN);
26+
buffer.put(data);
27+
buffer.putInt(nextPubKey.algorithm.getNumber());
28+
buffer.put(nextPubKey.toBytes());
29+
buffer.put(blockSignature);
30+
buffer.flip();
31+
return buffer.array();
32+
}
33+
}

Diff for: src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
1111
import org.biscuitsec.biscuit.token.builder.Utils;
1212

13+
import java.security.InvalidKeyException;
1314
import java.security.MessageDigest;
1415
import java.security.NoSuchAlgorithmException;
15-
import java.security.PrivateKey;
1616
import java.security.SecureRandom;
1717
import java.security.Signature;
18+
import java.security.SignatureException;
1819

1920
final class Ed25519KeyPair extends KeyPair {
2021

@@ -62,6 +63,14 @@ public static Signature getSignature() throws NoSuchAlgorithmException {
6263
return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
6364
}
6465

66+
@Override
67+
public byte[] sign(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
68+
Signature sgr = KeyPair.generateSignature(Schema.PublicKey.Algorithm.Ed25519);
69+
sgr.initSign(privateKey);
70+
sgr.update(data);
71+
return sgr.sign();
72+
}
73+
6574
@Override
6675
public byte[] toBytes() {
6776
return privateKey.getSeed();
@@ -72,18 +81,9 @@ public String toHex() {
7281
return Utils.byteArrayToHexString(toBytes());
7382
}
7483

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-
8584
@Override
8685
public PublicKey public_key() {
8786
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.publicKey);
8887
}
88+
8989
}

Diff for: src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
import biscuit.format.schema.Schema.PublicKey.Algorithm;
55
import net.i2p.crypto.eddsa.Utils;
66

7+
import java.security.InvalidKeyException;
78
import java.security.NoSuchAlgorithmException;
89
import java.security.SecureRandom;
910
import java.security.Signature;
11+
import java.security.SignatureException;
1012

1113
/**
1214
* Private and public key.
1315
*/
14-
public abstract class KeyPair {
16+
public abstract class KeyPair implements Signer {
1517

1618
public static KeyPair generate(Algorithm algorithm) {
1719
return generate(algorithm, new SecureRandom());
@@ -51,13 +53,17 @@ public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgo
5153
}
5254
}
5355

56+
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
57+
Signature sgr = KeyPair.generateSignature(publicKey.algorithm);
58+
sgr.initVerify(publicKey.key);
59+
sgr.update(data);
60+
return sgr.verify(signature);
61+
}
62+
5463
public abstract byte[] toBytes();
5564

5665
public abstract String toHex();
5766

58-
public abstract java.security.PublicKey publicKey();
59-
60-
public abstract java.security.PrivateKey private_key();
61-
67+
@Override
6268
public abstract PublicKey public_key();
6369
}

Diff for: src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
import org.bouncycastle.jce.spec.ECPublicKeySpec;
1212
import org.bouncycastle.util.BigIntegers;
1313

14+
import java.security.InvalidKeyException;
1415
import java.security.NoSuchAlgorithmException;
15-
import java.security.PrivateKey;
1616
import java.security.SecureRandom;
1717
import java.security.Security;
1818
import java.security.Signature;
19+
import java.security.SignatureException;
1920

2021
final class SECP256R1KeyPair extends KeyPair {
2122

@@ -72,6 +73,14 @@ public static Signature getSignature() throws NoSuchAlgorithmException {
7273
return Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
7374
}
7475

76+
@Override
77+
public byte[] sign(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
78+
Signature sgr = KeyPair.generateSignature(Schema.PublicKey.Algorithm.SECP256R1);
79+
sgr.initSign(privateKey);
80+
sgr.update(data);
81+
return sgr.sign();
82+
}
83+
7584
@Override
7685
public byte[] toBytes() {
7786
return BigIntegers.asUnsignedByteArray(privateKey.getD());
@@ -82,18 +91,9 @@ public String toHex() {
8291
return Utils.byteArrayToHexString(toBytes());
8392
}
8493

85-
@Override
86-
public java.security.PublicKey publicKey() {
87-
return publicKey;
88-
}
89-
90-
@Override
91-
public PrivateKey private_key() {
92-
return privateKey;
93-
}
94-
9594
@Override
9695
public PublicKey public_key() {
9796
return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, publicKey);
9897
}
98+
9999
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.biscuitsec.biscuit.crypto;
2+
3+
import java.security.InvalidKeyException;
4+
import java.security.NoSuchAlgorithmException;
5+
import java.security.SignatureException;
6+
7+
/**
8+
* Interface to enable the cryptographic signature of payload.
9+
* It can be adapted depending of the needs
10+
*/
11+
public interface Signer {
12+
/**
13+
* Sign the payload with the signer key
14+
*
15+
* @param payload
16+
* @return the signature of payload by
17+
* @throws NoSuchAlgorithmException
18+
* @throws InvalidKeyException
19+
* @throws SignatureException
20+
*/
21+
public byte[] sign(byte[] payload) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException;
22+
23+
/**
24+
* Return the public key of the signer and the associated algorithm
25+
*
26+
* @return
27+
*/
28+
public PublicKey public_key();
29+
}

Diff for: src/main/java/org/biscuitsec/biscuit/crypto/Token.java

+12-34
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import org.biscuitsec.biscuit.error.Error;
44
import io.vavr.control.Either;
55

6-
import java.nio.ByteBuffer;
7-
import java.nio.ByteOrder;
8-
import java.security.*;
6+
import java.security.InvalidKeyException;
7+
import java.security.NoSuchAlgorithmException;
8+
import java.security.SignatureException;
99
import java.util.ArrayList;
1010

1111
import static io.vavr.API.Left;
@@ -17,17 +17,11 @@ class Token {
1717
public final ArrayList<byte[]> signatures;
1818
public final KeyPair next;
1919

20-
public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
21-
Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
22-
ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
23-
algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
24-
algo_buf.flip();
25-
sgr.initSign(rootKeyPair.private_key());
26-
sgr.update(message);
27-
sgr.update(algo_buf);
28-
sgr.update(next.public_key().toBytes());
20+
public Token(final Signer rootSigner, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
2921

30-
byte[] signature = sgr.sign();
22+
byte[] payload = BlockSignatureBuffer.getBufferSignature(next.public_key(), message);
23+
24+
byte[] signature = rootSigner.sign(payload);
3125

3226
this.blocks = new ArrayList<>();
3327
this.blocks.add(message);
@@ -47,16 +41,8 @@ public Token(final ArrayList<byte[]> blocks, final ArrayList<PublicKey> keys, fi
4741
}
4842

4943
public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
50-
Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
51-
sgr.initSign(this.next.private_key());
52-
ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
53-
algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
54-
algo_buf.flip();
55-
sgr.update(message);
56-
sgr.update(algo_buf);
57-
sgr.update(keyPair.public_key().toBytes());
58-
59-
byte[] signature = sgr.sign();
44+
byte[] payload = BlockSignatureBuffer.getBufferSignature(keyPair.public_key(), message);
45+
byte[] signature = this.next.sign(payload);
6046

6147
Token token = new Token(this.blocks, this.keys, this.signatures, keyPair);
6248
token.blocks.add(message);
@@ -74,23 +60,15 @@ public Either<Error, Void> verify(PublicKey root) throws NoSuchAlgorithmExceptio
7460
PublicKey next_key = this.keys.get(i);
7561
byte[] signature = this.signatures.get(i);
7662

77-
ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
78-
algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
79-
algo_buf.flip();
80-
Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
81-
sgr.initVerify(current_key.key);
82-
sgr.update(block);
83-
sgr.update(algo_buf);
84-
sgr.update(next_key.toBytes());
85-
86-
if (sgr.verify(signature)) {
63+
byte[] payload = BlockSignatureBuffer.getBufferSignature(next_key, block);
64+
if (KeyPair.verify(current_key, payload, signature)) {
8765
current_key = next_key;
8866
} else {
8967
return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"));
9068
}
9169
}
9270

93-
if(this.next.publicKey() == current_key.key) {
71+
if (this.next.public_key().equals(current_key)) {
9472
return Right(null);
9573
} else {
9674
return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"));

Diff for: src/main/java/org/biscuitsec/biscuit/token/Biscuit.java

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.biscuitsec.biscuit.token;
22

3-
import biscuit.format.schema.Schema;
43
import biscuit.format.schema.Schema.PublicKey.Algorithm;
54
import org.biscuitsec.biscuit.crypto.KeyDelegate;
65
import org.biscuitsec.biscuit.crypto.KeyPair;
@@ -27,7 +26,7 @@ public class Biscuit extends UnverifiedBiscuit {
2726
* @param root root private key
2827
* @return
2928
*/
30-
public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final KeyPair root) {
29+
public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final org.biscuitsec.biscuit.crypto.Signer root) {
3130
return new org.biscuitsec.biscuit.token.builder.Biscuit(new SecureRandom(), root);
3231
}
3332

@@ -47,11 +46,11 @@ public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureR
4746
/**
4847
* Creates a token builder
4948
*
50-
* @param rng random number generator
51-
* @param root root private key
49+
* @param rng random number generator
50+
* @param root root private key
5251
* @return
5352
*/
54-
public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureRandom rng, final KeyPair root, final Option<Integer> root_key_id) {
53+
public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Option<Integer> root_key_id) {
5554
return new org.biscuitsec.biscuit.token.builder.Biscuit(rng, root, root_key_id);
5655
}
5756

@@ -63,7 +62,7 @@ public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureR
6362
* @param authority authority block
6463
* @return Biscuit
6564
*/
66-
public static Biscuit make(final SecureRandom rng, final KeyPair root, final Block authority) throws Error.FormatError {
65+
public static Biscuit make(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Block authority) throws Error.FormatError {
6766
return Biscuit.make(rng, root, Option.none(), authority);
6867
}
6968

@@ -75,7 +74,7 @@ public static Biscuit make(final SecureRandom rng, final KeyPair root, final Blo
7574
* @param authority authority block
7675
* @return Biscuit
7776
*/
78-
public static Biscuit make(final SecureRandom rng, final KeyPair root, final Integer root_key_id, final Block authority) throws Error.FormatError {
77+
public static Biscuit make(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Integer root_key_id, final Block authority) throws Error.FormatError {
7978
return Biscuit.make(rng, root, Option.of(root_key_id), authority);
8079
}
8180

@@ -87,7 +86,7 @@ public static Biscuit make(final SecureRandom rng, final KeyPair root, final Int
8786
* @param authority authority block
8887
* @return Biscuit
8988
*/
90-
static private Biscuit make(final SecureRandom rng, final KeyPair root, final Option<Integer> root_key_id, final Block authority) throws Error.FormatError {
89+
static private Biscuit make(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Option<Integer> root_key_id, final Block authority) throws Error.FormatError {
9190
ArrayList<Block> blocks = new ArrayList<>();
9291

9392
KeyPair next = KeyPair.generate(root.public_key().algorithm, rng);

0 commit comments

Comments
 (0)