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); + } + } +}