diff --git a/pom.xml b/pom.xml
index 6943970b..0486a826 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,9 +32,13 @@
0.10.3
1.6
2.8.9
+ 1.79
5.8.2
+ 2.30.2
+ 1.20.4
+ 1.20.4
@@ -259,13 +263,31 @@
org.bouncycastle
bcprov-jdk18on
- 1.79
+ ${bcprov.version}
org.junit.jupiter
junit-jupiter
test
+
+ software.amazon.awssdk
+ kms
+ ${awssdk-kms.version}
+ test
+
+
+ org.testcontainers
+ junit-jupiter
+ ${testcontainers-junit-jupiter.version}
+ test
+
+
+ org.testcontainers
+ localstack
+ ${testcontainers-localstack.version}
+ test
+
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/BlockSignatureBuffer.java b/src/main/java/org/biscuitsec/biscuit/crypto/BlockSignatureBuffer.java
new file mode 100644
index 00000000..dafb3afe
--- /dev/null
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/BlockSignatureBuffer.java
@@ -0,0 +1,33 @@
+package org.biscuitsec.biscuit.crypto;
+
+import org.biscuitsec.biscuit.token.format.ExternalSignature;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Optional;
+
+public class BlockSignatureBuffer {
+ public static byte[] getBufferSignature(PublicKey nextPubKey, byte[] data) {
+ return getBufferSignature(nextPubKey, data, Optional.empty());
+ }
+
+ public static byte[] getBufferSignature(PublicKey nextPubKey, byte[] data, Optional externalSignature) {
+ var buffer = ByteBuffer.allocate(4 + data.length + nextPubKey.toBytes().length + externalSignature.map((a) -> a.signature.length).orElse(0)).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.put(data);
+ externalSignature.ifPresent(signature -> buffer.put(signature.signature));
+ buffer.putInt(nextPubKey.algorithm.getNumber());
+ buffer.put(nextPubKey.toBytes());
+ buffer.flip();
+ return buffer.array();
+ }
+
+ public static byte[] getBufferSealedSignature(PublicKey nextPubKey, byte[] data, byte[] blockSignature) {
+ var buffer = ByteBuffer.allocate(4 + data.length + nextPubKey.toBytes().length + blockSignature.length).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.put(data);
+ buffer.putInt(nextPubKey.algorithm.getNumber());
+ buffer.put(nextPubKey.toBytes());
+ buffer.put(blockSignature);
+ buffer.flip();
+ return buffer.array();
+ }
+}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
index 051ee5fa..dd290f29 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
@@ -10,11 +10,12 @@
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.biscuitsec.biscuit.token.builder.Utils;
+import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
+import java.security.SignatureException;
final class Ed25519KeyPair extends KeyPair {
@@ -62,6 +63,14 @@ public static Signature getSignature() throws NoSuchAlgorithmException {
return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
}
+ @Override
+ public byte[] sign(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ Signature sgr = KeyPair.generateSignature(Schema.PublicKey.Algorithm.Ed25519);
+ sgr.initSign(privateKey);
+ sgr.update(data);
+ return sgr.sign();
+ }
+
@Override
public byte[] toBytes() {
return privateKey.getSeed();
@@ -72,18 +81,9 @@ public String toHex() {
return Utils.byteArrayToHexString(toBytes());
}
- @Override
- public java.security.PublicKey publicKey() {
- return publicKey;
- }
-
- @Override
- public PrivateKey private_key() {
- return privateKey;
- }
-
@Override
public PublicKey public_key() {
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.publicKey);
}
+
}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
index 9014271c..f6107b75 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
@@ -4,14 +4,16 @@
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.Utils;
+import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Signature;
+import java.security.SignatureException;
/**
* Private and public key.
*/
-public abstract class KeyPair {
+public abstract class KeyPair implements Signer {
public static KeyPair generate(Algorithm algorithm) {
return generate(algorithm, new SecureRandom());
@@ -51,13 +53,17 @@ public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgo
}
}
+ public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ Signature sgr = KeyPair.generateSignature(publicKey.algorithm);
+ sgr.initVerify(publicKey.key);
+ sgr.update(data);
+ return sgr.verify(signature);
+ }
+
public abstract byte[] toBytes();
public abstract String toHex();
- public abstract java.security.PublicKey publicKey();
-
- public abstract java.security.PrivateKey private_key();
-
+ @Override
public abstract PublicKey public_key();
}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
index 2afdddf8..dc9222e2 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
@@ -11,11 +11,12 @@
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.util.BigIntegers;
+import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
+import java.security.SignatureException;
final class SECP256R1KeyPair extends KeyPair {
@@ -72,6 +73,14 @@ public static Signature getSignature() throws NoSuchAlgorithmException {
return Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
}
+ @Override
+ public byte[] sign(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ Signature sgr = KeyPair.generateSignature(Schema.PublicKey.Algorithm.SECP256R1);
+ sgr.initSign(privateKey);
+ sgr.update(data);
+ return sgr.sign();
+ }
+
@Override
public byte[] toBytes() {
return BigIntegers.asUnsignedByteArray(privateKey.getD());
@@ -82,18 +91,9 @@ public String toHex() {
return Utils.byteArrayToHexString(toBytes());
}
- @Override
- public java.security.PublicKey publicKey() {
- return publicKey;
- }
-
- @Override
- public PrivateKey private_key() {
- return privateKey;
- }
-
@Override
public PublicKey public_key() {
return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, publicKey);
}
+
}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Signer.java b/src/main/java/org/biscuitsec/biscuit/crypto/Signer.java
new file mode 100644
index 00000000..cf3266ac
--- /dev/null
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/Signer.java
@@ -0,0 +1,29 @@
+package org.biscuitsec.biscuit.crypto;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+
+/**
+ * Interface to enable the cryptographic signature of payload.
+ * It can be adapted depending of the needs
+ */
+public interface Signer {
+ /**
+ * Sign the payload with the signer key
+ *
+ * @param payload
+ * @return the signature of payload by
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws SignatureException
+ */
+ public byte[] sign(byte[] payload) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException;
+
+ /**
+ * Return the public key of the signer and the associated algorithm
+ *
+ * @return
+ */
+ public PublicKey public_key();
+}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java
index 18ed8caf..010125c7 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java
@@ -3,9 +3,9 @@
import org.biscuitsec.biscuit.error.Error;
import io.vavr.control.Either;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.*;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
import java.util.ArrayList;
import static io.vavr.API.Left;
@@ -17,17 +17,11 @@ class Token {
public final ArrayList signatures;
public final KeyPair next;
- public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
- Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
- algo_buf.flip();
- sgr.initSign(rootKeyPair.private_key());
- sgr.update(message);
- sgr.update(algo_buf);
- sgr.update(next.public_key().toBytes());
+ public Token(final Signer rootSigner, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
- byte[] signature = sgr.sign();
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(next.public_key(), message);
+
+ byte[] signature = rootSigner.sign(payload);
this.blocks = new ArrayList<>();
this.blocks.add(message);
@@ -47,16 +41,8 @@ public Token(final ArrayList blocks, final ArrayList keys, fi
}
public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
- Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
- sgr.initSign(this.next.private_key());
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
- algo_buf.flip();
- sgr.update(message);
- sgr.update(algo_buf);
- sgr.update(keyPair.public_key().toBytes());
-
- byte[] signature = sgr.sign();
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(keyPair.public_key(), message);
+ byte[] signature = this.next.sign(payload);
Token token = new Token(this.blocks, this.keys, this.signatures, keyPair);
token.blocks.add(message);
@@ -74,23 +60,15 @@ public Either verify(PublicKey root) throws NoSuchAlgorithmExceptio
PublicKey next_key = this.keys.get(i);
byte[] signature = this.signatures.get(i);
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
- algo_buf.flip();
- Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
- sgr.initVerify(current_key.key);
- sgr.update(block);
- sgr.update(algo_buf);
- sgr.update(next_key.toBytes());
-
- if (sgr.verify(signature)) {
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(next_key, block);
+ if (KeyPair.verify(current_key, payload, signature)) {
current_key = next_key;
} else {
return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"));
}
}
- if(this.next.publicKey() == current_key.key) {
+ if (this.next.public_key().equals(current_key)) {
return Right(null);
} else {
return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"));
diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java
index c1440980..1f71c01e 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java
@@ -1,6 +1,5 @@
package org.biscuitsec.biscuit.token;
-import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import org.biscuitsec.biscuit.crypto.KeyDelegate;
import org.biscuitsec.biscuit.crypto.KeyPair;
@@ -27,7 +26,7 @@ public class Biscuit extends UnverifiedBiscuit {
* @param root root private key
* @return
*/
- public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final KeyPair root) {
+ public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final org.biscuitsec.biscuit.crypto.Signer root) {
return new org.biscuitsec.biscuit.token.builder.Biscuit(new SecureRandom(), root);
}
@@ -47,11 +46,11 @@ public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureR
/**
* Creates a token builder
*
- * @param rng random number generator
- * @param root root private key
+ * @param rng random number generator
+ * @param root root private key
* @return
*/
- public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureRandom rng, final KeyPair root, final Option root_key_id) {
+ public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Option root_key_id) {
return new org.biscuitsec.biscuit.token.builder.Biscuit(rng, root, root_key_id);
}
@@ -63,7 +62,7 @@ public static org.biscuitsec.biscuit.token.builder.Biscuit builder(final SecureR
* @param authority authority block
* @return Biscuit
*/
- public static Biscuit make(final SecureRandom rng, final KeyPair root, final Block authority) throws Error.FormatError {
+ public static Biscuit make(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Block authority) throws Error.FormatError {
return Biscuit.make(rng, root, Option.none(), authority);
}
@@ -75,7 +74,7 @@ public static Biscuit make(final SecureRandom rng, final KeyPair root, final Blo
* @param authority authority block
* @return Biscuit
*/
- public static Biscuit make(final SecureRandom rng, final KeyPair root, final Integer root_key_id, final Block authority) throws Error.FormatError {
+ 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 {
return Biscuit.make(rng, root, Option.of(root_key_id), authority);
}
@@ -87,7 +86,7 @@ public static Biscuit make(final SecureRandom rng, final KeyPair root, final Int
* @param authority authority block
* @return Biscuit
*/
- static private Biscuit make(final SecureRandom rng, final KeyPair root, final Option root_key_id, final Block authority) throws Error.FormatError {
+ static private Biscuit make(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, final Option root_key_id, final Block authority) throws Error.FormatError {
ArrayList blocks = new ArrayList<>();
KeyPair next = KeyPair.generate(root.public_key().algorithm, rng);
diff --git a/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java
index 7a3215dc..3ab63607 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java
@@ -4,21 +4,16 @@
import com.google.protobuf.InvalidProtocolBufferException;
import io.vavr.control.Either;
import io.vavr.control.Option;
-import net.i2p.crypto.eddsa.EdDSAEngine;
-import org.biscuitsec.biscuit.crypto.KeyPair;
+import org.biscuitsec.biscuit.crypto.BlockSignatureBuffer;
import org.biscuitsec.biscuit.crypto.PublicKey;
+import org.biscuitsec.biscuit.crypto.Signer;
import org.biscuitsec.biscuit.datalog.SymbolTable;
import org.biscuitsec.biscuit.error.Error;
import org.biscuitsec.biscuit.token.builder.Block;
-import org.biscuitsec.biscuit.token.format.SerializedBiscuit;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
-import java.util.ArrayList;
-import java.util.List;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.util.Objects;
public class ThirdPartyBlockRequest {
@@ -28,29 +23,20 @@ public class ThirdPartyBlockRequest {
this.previousKey = previousKey;
}
- public Either createBlock(KeyPair keyPair, Block blockBuilder) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ public Either createBlock(final Signer externalSigner, Block blockBuilder) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
SymbolTable symbols = new SymbolTable();
- org.biscuitsec.biscuit.token.Block block = blockBuilder.build(symbols, Option.some(keyPair.public_key()));
+ org.biscuitsec.biscuit.token.Block block = blockBuilder.build(symbols, Option.some(externalSigner.public_key()));
Either res = block.to_bytes();
- if(res.isLeft()) {
+ if (res.isLeft()) {
return Either.left(res.getLeft());
}
byte[] serializedBlock = res.get();
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(this.previousKey, serializedBlock);
+ byte[] signature = externalSigner.sign(payload);
- Signature sgr = KeyPair.generateSignature(keyPair.public_key().algorithm);
- sgr.initSign(keyPair.private_key());
- sgr.update(serializedBlock);
-
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(Schema.PublicKey.Algorithm.Ed25519.getNumber()));
- algo_buf.flip();
- sgr.update(algo_buf);
- sgr.update(previousKey.toBytes());
- byte[] signature = sgr.sign();
-
- PublicKey publicKey = keyPair.public_key();
+ PublicKey publicKey = externalSigner.public_key();
return Either.right(new ThirdPartyBlockContents(serializedBlock, signature, publicKey));
}
diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java
index 39d2dede..bff22e35 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java
@@ -1,7 +1,7 @@
package org.biscuitsec.biscuit.token;
-import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
+import org.biscuitsec.biscuit.crypto.BlockSignatureBuffer;
import org.biscuitsec.biscuit.crypto.KeyDelegate;
import org.biscuitsec.biscuit.crypto.KeyPair;
import org.biscuitsec.biscuit.crypto.PublicKey;
@@ -14,8 +14,6 @@
import org.biscuitsec.biscuit.datalog.Check;
import org.biscuitsec.biscuit.datalog.SymbolTable;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.security.*;
import java.util.*;
import java.util.stream.Collectors;
@@ -234,25 +232,15 @@ public ThirdPartyBlockRequest thirdPartyRequest() {
*/
public UnverifiedBiscuit appendThirdPartyBlock(PublicKey externalKey, ThirdPartyBlockContents blockResponse)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
- KeyPair nextKeyPair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519);
-
- Signature sgr = KeyPair.generateSignature(externalKey.algorithm);
- sgr.initVerify(externalKey.key);
-
- sgr.update(blockResponse.payload);
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(Schema.PublicKey.Algorithm.Ed25519.getNumber()));
- algo_buf.flip();
- sgr.update(algo_buf);
-
PublicKey previousKey;
if(this.serializedBiscuit.blocks.isEmpty()) {
previousKey = this.serializedBiscuit.authority.key;
} else {
previousKey = this.serializedBiscuit.blocks.get(this.serializedBiscuit.blocks.size() - 1).key;
}
- sgr.update(previousKey.toBytes());
- if (!sgr.verify(blockResponse.signature)) {
+ KeyPair nextKeyPair = KeyPair.generate(previousKey.algorithm);
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(previousKey, blockResponse.payload);
+ if (!KeyPair.verify(externalKey, payload, blockResponse.signature)) {
throw new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied");
}
diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/builder/Biscuit.java
index 66c202d2..1bcdc99e 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/builder/Biscuit.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/builder/Biscuit.java
@@ -1,6 +1,5 @@
package org.biscuitsec.biscuit.token.builder;
-import org.biscuitsec.biscuit.crypto.KeyPair;
import org.biscuitsec.biscuit.crypto.PublicKey;
import org.biscuitsec.biscuit.datalog.SchemaVersion;
import org.biscuitsec.biscuit.datalog.SymbolTable;
@@ -20,7 +19,7 @@
public class Biscuit {
SecureRandom rng;
- KeyPair root;
+ org.biscuitsec.biscuit.crypto.Signer root;
String context;
List facts;
List rules;
@@ -28,7 +27,7 @@ public class Biscuit {
List scopes;
Option root_key_id;
- public Biscuit(final SecureRandom rng, final KeyPair root) {
+ public Biscuit(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root) {
this.rng = rng;
this.root = root;
this.context = "";
@@ -39,7 +38,7 @@ public Biscuit(final SecureRandom rng, final KeyPair root) {
this.root_key_id = Option.none();
}
- public Biscuit(final SecureRandom rng, final KeyPair root, Option root_key_id) {
+ public Biscuit(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, Option root_key_id) {
this.rng = rng;
this.root = root;
this.context = "";
@@ -50,7 +49,7 @@ public Biscuit(final SecureRandom rng, final KeyPair root, Option root_
this.root_key_id = root_key_id;
}
- public Biscuit(final SecureRandom rng, final KeyPair root, Option root_key_id, org.biscuitsec.biscuit.token.builder.Block block) {
+ public Biscuit(final SecureRandom rng, final org.biscuitsec.biscuit.crypto.Signer root, Option root_key_id, org.biscuitsec.biscuit.token.builder.Block block) {
this.rng = rng;
this.root = root;
this.root_key_id = root_key_id;
diff --git a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java
index f0b1b71a..e0974260 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java
@@ -2,6 +2,7 @@
import biscuit.format.schema.Schema;
import io.vavr.Tuple2;
+import org.biscuitsec.biscuit.crypto.BlockSignatureBuffer;
import org.biscuitsec.biscuit.crypto.KeyDelegate;
import org.biscuitsec.biscuit.crypto.KeyPair;
import org.biscuitsec.biscuit.crypto.PublicKey;
@@ -230,7 +231,7 @@ static public Either make(final org.biscui
return make(root, Option.none(), authority, next);
}
- static public Either make(final org.biscuitsec.biscuit.crypto.KeyPair root, final Option root_key_id,
+ static public Either make(final org.biscuitsec.biscuit.crypto.Signer rootSigner, final Option root_key_id,
final Block authority, final org.biscuitsec.biscuit.crypto.KeyPair next) {
Schema.Block b = authority.serialize();
try {
@@ -238,17 +239,8 @@ static public Either make(final org.biscui
b.writeTo(stream);
byte[] block = stream.toByteArray();
org.biscuitsec.biscuit.crypto.PublicKey next_key = next.public_key();
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber()));
- algo_buf.flip();
-
- Signature sgr = KeyPair.generateSignature(root.public_key().algorithm);
- sgr.initSign(root.private_key());
- sgr.update(block);
- sgr.update(algo_buf);
- sgr.update(next_key.toBytes());
- byte[] signature = sgr.sign();
-
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(next_key, block);
+ byte[] signature = rootSigner.sign(payload);
SignedBlock signedBlock = new SignedBlock(block, next_key, signature, Option.none());
Proof proof = new Proof(next);
@@ -270,20 +262,11 @@ public Either append(final org.biscuitsec.
b.writeTo(stream);
byte[] block = stream.toByteArray();
+ KeyPair secretKey = this.proof.secretKey.get();
org.biscuitsec.biscuit.crypto.PublicKey next_key = next.public_key();
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber()));
- algo_buf.flip();
-
- Signature sgr = KeyPair.generateSignature(next_key.algorithm);
- sgr.initSign(this.proof.secretKey.get().private_key());
- sgr.update(block);
- if(externalSignature.isDefined()) {
- sgr.update(externalSignature.get().signature);
- }
- sgr.update(algo_buf);
- sgr.update(next_key.toBytes());
- byte[] signature = sgr.sign();
+
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(next_key, block, externalSignature.toJavaOptional());
+ byte[] signature = this.proof.secretKey.get().sign(payload);
SignedBlock signedBlock = new SignedBlock(block, next_key, signature, externalSignature);
@@ -303,7 +286,6 @@ public Either append(final org.biscuitsec.
public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
org.biscuitsec.biscuit.crypto.PublicKey current_key = root;
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
{
Either res = verifyBlockSignature(this.authority, current_key);
if(res.isRight()) {
@@ -352,18 +334,10 @@ public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root)
byte[] block = b.block;
org.biscuitsec.biscuit.crypto.PublicKey next_key = b.key;
byte[] signature = b.signature;
- algo_buf.clear();
- algo_buf.putInt(next_key.algorithm.getNumber());
- algo_buf.flip();
-
- Signature sgr = KeyPair.generateSignature(next_key.algorithm);
- sgr.initVerify(current_key.key);
- sgr.update(block);
- sgr.update(algo_buf);
- sgr.update(next_key.toBytes());
- sgr.update(signature);
-
- if (sgr.verify(finalSignature)) {
+
+ byte[] payload = BlockSignatureBuffer.getBufferSealedSignature(next_key, block, signature);
+
+ if (KeyPair.verify(current_key, payload, finalSignature)) {
return Right(null);
} else {
return Left(new Error.FormatError.Signature.SealedSignature());
@@ -396,22 +370,16 @@ static Either verifyBlockSignatu
}
sgr.update(algo_buf);
sgr.update(next_key.toBytes());
- if (!sgr.verify(signature)) {
+ byte[] payload = BlockSignatureBuffer.getBufferSignature(next_key, block, signedBlock.externalSignature.toJavaOptional());
+ if (!KeyPair.verify(publicKey, payload, signature)) {
return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"));
}
- if(signedBlock.externalSignature.isDefined()) {
- ByteBuffer algo_buf2 = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf2.putInt(Integer.valueOf(publicKey.algorithm.getNumber()));
- algo_buf2.flip();
+ if (signedBlock.externalSignature.isDefined()) {
+ byte[] externalPayload = BlockSignatureBuffer.getBufferSignature(publicKey, block);
+ ExternalSignature externalSignature = signedBlock.externalSignature.get();
- Signature sgr2 = KeyPair.generateSignature(publicKey.algorithm);
- sgr2.initVerify(signedBlock.externalSignature.get().key.key);
- sgr2.update(block);
- sgr2.update(algo_buf2);
- sgr2.update(publicKey.toBytes());
-
- if (!sgr2.verify(signedBlock.externalSignature.get().signature)) {
+ if (!KeyPair.verify(externalSignature.key, externalPayload, externalSignature.signature)) {
return Left(new Error.FormatError.Signature.InvalidSignature("external signature error: Verification equation was not satisfied"));
}
}
@@ -479,19 +447,9 @@ public Either seal() throws InvalidKeyException, NoSuchAlgorithmExc
block = this.blocks.get(this.blocks.size() - 1);
}
- Signature sgr = KeyPair.generateSignature(block.key.algorithm);
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(block.key.algorithm.getNumber()));
- algo_buf.flip();
-
-
- sgr.initSign(this.proof.secretKey.get().private_key());
- sgr.update(block.block);
- sgr.update(algo_buf);
- sgr.update(block.key.toBytes());
- sgr.update(block.signature);
-
- byte[] signature = sgr.sign();
+ KeyPair secretKey = this.proof.secretKey.get();
+ byte[] payload = BlockSignatureBuffer.getBufferSealedSignature(block.key, block.block, block.signature);
+ byte[] signature = secretKey.sign(payload);
this.proof.secretKey = Option.none();
this.proof.signature = Option.some(signature);
diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java
index dfd0a8dd..b7f3cb67 100644
--- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java
+++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java
@@ -75,20 +75,20 @@ private static void testChangeMessages(Schema.PublicKey.Algorithm algorithm) thr
KeyPair root = KeyPair.generate(algorithm, rng);
KeyPair keypair2 = KeyPair.generate(algorithm, rng);
Token token1 = new Token(root, message1.getBytes(), keypair2);
- assertEquals(Right(null), token1.verify(new PublicKey(algorithm, root.publicKey())));
+ assertEquals(Right(null), token1.verify(new PublicKey(algorithm, root.public_key().key)));
String message2 = "world";
KeyPair keypair3 = KeyPair.generate(algorithm, rng);
Token token2 = token1.append(keypair3, message2.getBytes());
token2.blocks.set(1, "you".getBytes());
assertEquals(Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")),
- token2.verify(new PublicKey(algorithm, root.publicKey())));
+ token2.verify(new PublicKey(algorithm, root.public_key().key)));
String message3 = "!!";
KeyPair keypair4 = KeyPair.generate(algorithm, rng);
Token token3 = token2.append(keypair4, message3.getBytes());
assertEquals(Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")),
- token3.verify(new PublicKey(algorithm, root.publicKey())));
+ token3.verify(new PublicKey(algorithm, root.public_key().key)));
}
private static void testThreeMessages(Schema.PublicKey.Algorithm algorithm) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
@@ -121,9 +121,9 @@ private static void testThreeMessages(Schema.PublicKey.Algorithm algorithm) thro
public void testSerializeBiscuit() throws Error {
var root = KeyPair.generate(SECP256R1);
var biscuit = Biscuit.builder(root)
- .add_authority_fact("user(\"1234\")")
- .add_authority_check("check if operation(\"read\")")
- .build();
+ .add_authority_fact("user(\"1234\")")
+ .add_authority_check("check if operation(\"read\")")
+ .build();
var serialized = biscuit.serialize();
var unverified = Biscuit.from_bytes(serialized);
assertDoesNotThrow(() -> unverified.verify(root.public_key()));
diff --git a/src/test/java/org/biscuitsec/biscuit/token/KmsSignerExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/KmsSignerExampleTest.java
new file mode 100644
index 00000000..a369e344
--- /dev/null
+++ b/src/test/java/org/biscuitsec/biscuit/token/KmsSignerExampleTest.java
@@ -0,0 +1,111 @@
+package org.biscuitsec.biscuit.token;
+
+import biscuit.format.schema.Schema.PublicKey.Algorithm;
+import org.biscuitsec.biscuit.crypto.PublicKey;
+import org.biscuitsec.biscuit.crypto.Signer;
+import org.biscuitsec.biscuit.error.Error;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.localstack.LocalStackContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.SdkBytes;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.kms.KmsClient;
+import software.amazon.awssdk.services.kms.model.KeySpec;
+import software.amazon.awssdk.services.kms.model.KeyUsageType;
+import software.amazon.awssdk.services.kms.model.SigningAlgorithmSpec;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+@Testcontainers
+public class KmsSignerExampleTest {
+
+ private static final DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:4.0.3");
+
+ @Container
+ public static LocalStackContainer LOCALSTACK = new LocalStackContainer(LOCALSTACK_IMAGE)
+ .withServices(LocalStackContainer.Service.KMS);
+
+ private KmsClient kmsClient;
+ private String kmsKeyId;
+
+ @BeforeEach
+ public void setup() {
+ var credentials = AwsBasicCredentials.create(LOCALSTACK.getAccessKey(), LOCALSTACK.getSecretKey());
+ kmsClient = KmsClient.builder()
+ .endpointOverride(LOCALSTACK.getEndpointOverride(LocalStackContainer.Service.KMS))
+ .credentialsProvider(StaticCredentialsProvider.create(credentials))
+ .region(Region.of(LOCALSTACK.getRegion()))
+ .build();
+
+ // ECC_NIST_P256 == SECP256R1
+ kmsKeyId = kmsClient.createKey(b -> b
+ .keySpec(KeySpec.ECC_NIST_P256)
+ .keyUsage(KeyUsageType.SIGN_VERIFY)
+ .build()
+ ).keyMetadata().keyId();
+ }
+
+ @Test
+ public void testCreateBiscuitWithRemoteSigner() throws Error {
+ var getPublicKeyResponse = kmsClient.getPublicKey(b -> b.keyId(kmsKeyId).build());
+ var x509EncodedPublicKey = getPublicKeyResponse.publicKey().asByteArray();
+ var sec1CompressedEncodedPublicKey = convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey(x509EncodedPublicKey);
+ var publicKey = new PublicKey(Algorithm.SECP256R1, sec1CompressedEncodedPublicKey);
+ var signer = new Signer() {
+ @Override
+ public byte[] sign(byte[] bytes) {
+ var signResponse = kmsClient.sign(b -> b
+ .keyId(kmsKeyId)
+ .signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
+ .message(SdkBytes.fromByteArray(bytes))
+ );
+ return signResponse.signature().asByteArray();
+ }
+
+ @Override
+ public PublicKey public_key() {
+ return publicKey;
+ }
+ };
+ var biscuit = Biscuit.builder(signer)
+ .add_authority_fact("user(\"1234\")")
+ .add_authority_check("check if operation(\"read\")")
+ .build();
+ var serializedBiscuit = biscuit.serialize();
+ var deserializedUnverifiedBiscuit = Biscuit.from_bytes(serializedBiscuit);
+ var verifiedBiscuit = assertDoesNotThrow(() -> deserializedUnverifiedBiscuit.verify(publicKey));
+
+ System.out.println(verifiedBiscuit.print());
+ }
+
+ private static byte[] convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey(byte[] publicKeyBytes) {
+ try (ASN1InputStream asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(publicKeyBytes))) {
+
+ // Parse the ASN.1 encoded public key bytes
+ var asn1Primitive = asn1InputStream.readObject();
+ var subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(asn1Primitive);
+
+ // Extract the public key data
+ var publicKeyDataBitString = subjectPublicKeyInfo.getPublicKeyData();
+ byte[] publicKeyData = publicKeyDataBitString.getBytes();
+
+ // Parse the public key data to get the elliptic curve point
+ var ecParameters = ECNamedCurveTable.getByName("secp256r1");
+ var ecPoint = ecParameters.getCurve().decodePoint(publicKeyData);
+ return ecPoint.getEncoded(true);
+ } catch (IOException e) {
+ throw new RuntimeException("Error converting DER-encoded X.509 to SEC1 compressed format", e);
+ }
+ }
+}