From 1c5789b3843a1deec2365c1982a7d57a5c03964c Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Mon, 20 May 2024 13:44:55 +1200 Subject: [PATCH 01/18] p256 in progress, commit before updating tests just want to get plumbed in and existing tests passing --- .../biscuit/crypto/Ed25519KeyPair.java | 100 ++++++++++++++ .../biscuitsec/biscuit/crypto/KeyPair.java | 122 +++++++++--------- .../biscuit/crypto/P256KeyPair.java | 74 +++++++++++ .../biscuitsec/biscuit/crypto/PublicKey.java | 61 ++++++--- .../org/biscuitsec/biscuit/crypto/Token.java | 13 +- .../org/biscuitsec/biscuit/error/Error.java | 34 +++++ .../org/biscuitsec/biscuit/token/Biscuit.java | 17 ++- .../biscuit/token/UnverifiedBiscuit.java | 6 +- .../biscuit/token/builder/parser/Parser.java | 22 +++- .../token/format/SerializedBiscuit.java | 29 +++-- src/main/proto/schema.proto | 1 + 11 files changed, 369 insertions(+), 110 deletions(-) create mode 100644 src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java create mode 100644 src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java new file mode 100644 index 00000000..9bab1b52 --- /dev/null +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java @@ -0,0 +1,100 @@ +package org.biscuitsec.biscuit.crypto; + +import biscuit.format.schema.Schema; +import net.i2p.crypto.eddsa.EdDSAEngine; +import net.i2p.crypto.eddsa.EdDSAPrivateKey; +import net.i2p.crypto.eddsa.EdDSAPublicKey; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; +import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; +import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; +import org.biscuitsec.biscuit.token.builder.Utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Signature; + +class Ed25519KeyPair extends KeyPair { + + private final EdDSAPrivateKey privateKey; + private final EdDSAPublicKey publicKey; + + private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); + + public Ed25519KeyPair() { + this(new SecureRandom()); + } + + public Ed25519KeyPair(byte[] bytes) { + EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, ed25519); + EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); + + EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); + EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); + + this.privateKey = privKey; + this.publicKey = pubKey; + } + + public Ed25519KeyPair(SecureRandom rng) { + byte[] b = new byte[32]; + rng.nextBytes(b); + + EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519); + EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); + + EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); + EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); + + this.privateKey = privKey; + this.publicKey = pubKey; + } + + public Ed25519KeyPair(String hex) { + byte[] b = Utils.hexStringToByteArray(hex); + + EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519); + EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); + + EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); + EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); + + this.privateKey = privKey; + this.publicKey = pubKey; + } + + public static java.security.PublicKey generatePublicKey(byte[] data) { + return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, ed25519)); + } + + public static Signature getSignature() throws NoSuchAlgorithmException { + return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm())); + } + + @Override + public byte[] toBytes() { + return privateKey.getSeed(); + } + + @Override + 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 a12c3de0..7f227987 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -1,77 +1,75 @@ package org.biscuitsec.biscuit.crypto; -import biscuit.format.schema.Schema; -import org.biscuitsec.biscuit.token.builder.Utils; -import net.i2p.crypto.eddsa.EdDSAPrivateKey; -import net.i2p.crypto.eddsa.EdDSAPublicKey; -import net.i2p.crypto.eddsa.spec.*; +import biscuit.format.schema.Schema.PublicKey.Algorithm; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.Signature; /** - * Private and public key + * Private and public key. */ -public final class KeyPair { - public final EdDSAPrivateKey private_key; - public final EdDSAPublicKey public_key; - - private static final int ED25519_PUBLIC_KEYSIZE = 32; - private static final int ED25519_PRIVATE_KEYSIZE = 64; - private static final int ED25519_SEED_SIZE = 32; - public static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); - - public KeyPair() { - this(new SecureRandom()); +public abstract class KeyPair { + + public static KeyPair generateForAlgorithm(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + if (algorithm == Algorithm.Ed25519) { + return new Ed25519KeyPair(bytes); + } else if (algorithm == Algorithm.P256) { + return new P256KeyPair(bytes); + } else { + throw new NoSuchAlgorithmException("Unsupported algorithm"); + } } - public KeyPair(final SecureRandom rng) { - byte[] b = new byte[32]; - rng.nextBytes(b); - - EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519); - EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); - - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); - EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); - - this.private_key = privKey; - this.public_key = pubKey; + public static KeyPair generateForAlgorithm(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + if (algorithm == Algorithm.Ed25519) { + return new Ed25519KeyPair(rng); + } else if (algorithm == Algorithm.P256) { + return new P256KeyPair(rng); + } else { + throw new NoSuchAlgorithmException("Unsupported algorithm"); + } } - public byte[] toBytes() { - return this.private_key.getSeed(); - } - - public KeyPair(byte[] b) { - EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519); - EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); - - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); - EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); - - this.private_key = privKey; - this.public_key = pubKey; + public static Signature getSignatureForAlgorithm(Algorithm algorithm) throws NoSuchAlgorithmException { + if (algorithm == Algorithm.Ed25519) { + return Ed25519KeyPair.getSignature(); + } else if (algorithm == Algorithm.P256) { + return P256KeyPair.getSignature(); + } else { + throw new NoSuchAlgorithmException("Unsupported algorithm"); + } } - public String toHex() { - return Utils.byteArrayToHexString(this.toBytes()); - } - - public KeyPair(String hex) { - byte[] b = Utils.hexStringToByteArray(hex); - - EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519); - EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); - - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); - EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); - - this.private_key = privKey; - this.public_key = pubKey; - } - - public PublicKey public_key() { - return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.public_key); - } + /** + * Returns the seed bytes of the private key. Returns null if the key was not created from a seed. + * @return seed bytes. + */ + public abstract byte[] toBytes(); // todo: rename to getSeedBytes() + + /** + * Returns the hex representation of the seed bytes of the private key. Null if the key was not created from a seed. + * @return hex representation of the seed bytes. + */ + public abstract String toHex(); // todo: rename to getHex() + + /** + * Returns the java.security.PublicKey. + * @return + */ + public abstract java.security.PublicKey publicKey(); + + /** + * Returns the java.security.PrivateKey. + * @return + */ + public abstract java.security.PrivateKey private_key(); + + /** + * Returns the biscuit.crypto.PublicKey. + * @return + */ + public abstract PublicKey public_key(); } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java new file mode 100644 index 00000000..ed6faf5b --- /dev/null +++ b/src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java @@ -0,0 +1,74 @@ +package org.biscuitsec.biscuit.crypto; + +import org.biscuitsec.biscuit.token.builder.Utils; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +class P256KeyPair extends KeyPair { + + private final java.security.KeyPair keyPair; + + public P256KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + this(new SecureRandom()); + } + + public P256KeyPair(byte[] seed) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + var kpg = KeyPairGenerator.getInstance("EC"); + var spec = new ECGenParameterSpec("secp256r1"); + kpg.initialize(spec, new SecureRandom(seed)); + keyPair = kpg.generateKeyPair(); + } + + public P256KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + var kpg = KeyPairGenerator.getInstance("EC"); + var spec = new ECGenParameterSpec("secp256r1"); + kpg.initialize(spec, rng); + keyPair = kpg.generateKeyPair(); + } + + public P256KeyPair(String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + this(Utils.hexStringToByteArray(hex)); + } + + public static java.security.PublicKey generatePublicKey(byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException { + return KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(data, "SHA256withECDSA")); + } + + public static Signature getSignature() { + throw new RuntimeException("not yet implemented"); + } + + @Override + public byte[] toBytes() { + throw new RuntimeException("not yet implemented"); + } + + @Override + public String toHex() { + return Utils.byteArrayToHexString(this.toBytes()); + } + + @Override + public java.security.PublicKey publicKey() { + throw new RuntimeException("not yet implemented"); + } + + @Override + public PrivateKey private_key() { + throw new RuntimeException("not yet implemented"); + } + + @Override + public PublicKey public_key() { + throw new RuntimeException("not yet implemented"); + } +} diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java index a1cda9fd..1aa1ca3c 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java @@ -5,41 +5,52 @@ import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.token.builder.Utils; import com.google.protobuf.ByteString; -import net.i2p.crypto.eddsa.EdDSAPublicKey; -import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; -import static org.biscuitsec.biscuit.crypto.KeyPair.ed25519; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + public class PublicKey { - public final EdDSAPublicKey key; + public final java.security.PublicKey key; public final Algorithm algorithm; - public PublicKey(Algorithm algorithm, EdDSAPublicKey public_key) { + public PublicKey(Algorithm algorithm, java.security.PublicKey public_key) { this.key = public_key; this.algorithm = algorithm; } - public PublicKey(Algorithm algorithm, byte[] data) { - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(data, ed25519); - EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); - this.key = pubKey; + public PublicKey(Algorithm algorithm, byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException { + if (algorithm == Algorithm.Ed25519) { + this.key = Ed25519KeyPair.generatePublicKey(data); + } else if (algorithm == Algorithm.P256) { + this.key = P256KeyPair.generatePublicKey(data); + } else { + throw new IllegalArgumentException("Invalid algorithm"); + } this.algorithm = algorithm; } public byte[] toBytes() { - return this.key.getAbyte(); + // wtf is this? + // return this.key.getAbyte(); + + return key.getEncoded(); } public String toHex() { return Utils.byteArrayToHexString(this.toBytes()); } - public PublicKey(Algorithm algorithm, String hex) { + public PublicKey(Algorithm algorithm, String hex) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] data = Utils.hexStringToByteArray(hex); - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(data, ed25519); - EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); - this.key = pubKey; + if (algorithm == Algorithm.Ed25519) { + this.key = Ed25519KeyPair.generatePublicKey(data); + } else if (algorithm == Algorithm.P256) { + this.key = P256KeyPair.generatePublicKey(data); + } else { + throw new IllegalArgumentException("Invalid algorithm"); + } this.algorithm = algorithm; } @@ -51,11 +62,19 @@ public Schema.PublicKey serialize() { } static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError.DeserializationError { - if(!pk.hasAlgorithm() || !pk.hasKey() || pk.getAlgorithm() != Algorithm.Ed25519) { + if(!pk.hasAlgorithm() || !pk.hasKey()) { throw new Error.FormatError.DeserializationError("Invalid public key"); } - - return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray()); + if (pk.getAlgorithm() != Algorithm.Ed25519 && pk.getAlgorithm() != Algorithm.P256) { + throw new Error.FormatError.DeserializationError("Invalid public key algorithm"); + } + try { + return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray()); + } catch (NoSuchAlgorithmException e) { + throw new Error.FormatError.DeserializationError("No such algorithm"); + } catch (InvalidKeySpecException e) { + throw new Error.FormatError.DeserializationError("Invalid key spec"); + } } @Override @@ -75,6 +94,12 @@ public int hashCode() { @Override public String toString() { - return "ed25519/" + toHex().toLowerCase(); + if (algorithm == Algorithm.Ed25519) { + return "ed25519/" + toHex().toLowerCase(); + } else if (algorithm == Algorithm.P256) { + return "p256/" + toHex().toLowerCase(); + } else { + return null; + } } } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java index 8e43894f..ce858558 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java @@ -2,7 +2,6 @@ import org.biscuitsec.biscuit.error.Error; import io.vavr.control.Either; -import net.i2p.crypto.eddsa.EdDSAEngine; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -19,11 +18,11 @@ class Token { public final KeyPair next; public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(KeyPair.ed25519.getHashAlgorithm())); + Signature sgr = KeyPair.getSignatureForAlgorithm(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.initSign(rootKeyPair.private_key()); sgr.update(message); sgr.update(algo_buf); sgr.update(next.public_key().toBytes()); @@ -48,8 +47,8 @@ public Token(final ArrayList blocks, final ArrayList keys, fi } public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(KeyPair.ed25519.getHashAlgorithm())); - sgr.initSign(this.next.private_key); + Signature sgr = KeyPair.getSignatureForAlgorithm(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(); @@ -75,10 +74,10 @@ public Either verify(PublicKey root) throws NoSuchAlgorithmExceptio PublicKey next_key = this.keys.get(i); byte[] signature = this.signatures.get(i); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(KeyPair.ed25519.getHashAlgorithm())); 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.getSignatureForAlgorithm(next.public_key().algorithm); sgr.initVerify(current_key.key); sgr.update(block); sgr.update(algo_buf); @@ -91,7 +90,7 @@ public Either verify(PublicKey root) throws NoSuchAlgorithmExceptio } } - if(this.next.public_key == current_key.key) { + if(this.next.publicKey() == current_key.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/error/Error.java b/src/main/java/org/biscuitsec/biscuit/error/Error.java index f1abdda1..06b03b27 100644 --- a/src/main/java/org/biscuitsec/biscuit/error/Error.java +++ b/src/main/java/org/biscuitsec/biscuit/error/Error.java @@ -6,6 +6,7 @@ import com.google.gson.JsonPrimitive; import io.vavr.control.Option; +import java.security.GeneralSecurityException; import java.util.List; import java.util.Objects; @@ -332,6 +333,39 @@ public JsonElement toJson() { return FormatError.jsonWrapper(jo); } } + + public static class AlgorithmError extends FormatError { + final public String e; + + public AlgorithmError(String e) { + this.e = e; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AlgorithmError other = (AlgorithmError) o; + return e.equals(other.e); + } + + @Override + public int hashCode() { + return Objects.hash(e); + } + + @Override + public String toString() { + return "Err(FormatError.AlgorithmError{ error: "+ e + " }"; + } + + @Override + public JsonElement toJson() { + JsonObject jo = new JsonObject(); + jo.addProperty("AlgorithmError", this.e); + return FormatError.jsonWrapper(jo); + } + } } public static class InvalidAuthorityIndex extends Error { final public long index; diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index 372e0b4e..b23fa5be 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyDelegate; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.crypto.PublicKey; @@ -10,9 +11,11 @@ import io.vavr.control.Either; import io.vavr.control.Option; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.Signature; import java.security.SignatureException; import java.util.*; @@ -110,7 +113,12 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op symbols.symbols.addAll(authority.symbols.symbols); ArrayList blocks = new ArrayList<>(); - KeyPair next = new KeyPair(rng); + KeyPair next; + try { + next = KeyPair.generateForAlgorithm(root.public_key().algorithm, rng); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new Error.FormatError.AlgorithmError(e.getMessage()); + } Either container = SerializedBiscuit.make(root, root_key_id, authority, next); if (container.isLeft()) { @@ -324,7 +332,12 @@ public String serialize_b64url() throws Error.FormatError.SerializationError { */ public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws Error { SecureRandom rng = new SecureRandom(); - KeyPair keypair = new KeyPair(rng); + KeyPair keypair; + try { + keypair = KeyPair.generateForAlgorithm(Schema.PublicKey.Algorithm.Ed25519, rng); // todo figure out how to get the algorithm + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new Error.FormatError.AlgorithmError(e.getMessage()); + } return attenuate(rng, keypair, block.build()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 302fd6d4..4c059622 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyDelegate; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.crypto.PublicKey; @@ -11,6 +12,7 @@ import org.biscuitsec.biscuit.datalog.Check; import org.biscuitsec.biscuit.datalog.SymbolTable; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -138,9 +140,9 @@ public org.biscuitsec.biscuit.token.builder.Block create_block() { * @param block new block (should be generated from a Block builder) * @return */ - public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { SecureRandom rng = new SecureRandom(); - KeyPair keypair = new KeyPair(rng); + KeyPair keypair = KeyPair.generateForAlgorithm(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm return attenuate(rng, keypair, block.build()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java index e3bac7cb..1d0db5b0 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java @@ -10,6 +10,8 @@ import io.vavr.control.Either; import org.biscuitsec.biscuit.token.builder.*; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; import java.time.OffsetDateTime; import java.time.format.DateTimeParseException; import java.util.*; @@ -319,13 +321,21 @@ public static Either> scope(String s) { } public static Either> publicKey(String s) { - if (!s.startsWith("ed25519/")) { - return Either.left(new Error(s, "unrecognized public key prefix")); + try { + if (s.startsWith("ed25519/")) { + s = s.substring("ed25519/".length()); + Tuple2 t = hex(s); + return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.Ed25519, t._2))); + } else if (s.startsWith("p256/")) { + s = s.substring("p256/".length()); + Tuple2 t = hex(s); + return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.P256, t._2))); + } else { + return Either.left(new Error(s, "unrecognized public key prefix")); + } + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + return Either.left(new Error(s, "error parsing public key")); } - - s = s.substring("ed25519/".length()); - Tuple2 t = hex(s); - return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.Ed25519, t._2))); } public static Either> fact_predicate(String s) { 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 93b94144..b0952f96 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -12,7 +12,6 @@ import io.vavr.Tuple3; import io.vavr.control.Either; import io.vavr.control.Option; -import net.i2p.crypto.eddsa.EdDSAEngine; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -102,7 +101,7 @@ static SerializedBiscuit from_bytes_inner(Schema.Biscuit data, org.biscuitsec.bi * @return SerializedBiscuit * @throws Error.FormatError.DeserializationError */ - static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.FormatError.DeserializationError { + static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.FormatError.DeserializationError, Error.FormatError.AlgorithmError { try { Schema.Biscuit data = Schema.Biscuit.parseFrom(slice); return SerializedBiscuit.deserialize(data); @@ -118,7 +117,7 @@ static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.Fo * @return SerializedBiscuit * @throws Error.FormatError.DeserializationError */ - static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.FormatError.DeserializationError { + static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.FormatError.DeserializationError, Error.FormatError.AlgorithmError { if(data.getAuthority().hasExternalSignature()) { throw new Error.FormatError.DeserializationError("the authority block must not contain an external signature"); } @@ -151,7 +150,11 @@ static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.F Option secretKey = Option.none(); if (data.getProof().hasNextSecret()) { - secretKey = Option.some(new org.biscuitsec.biscuit.crypto.KeyPair(data.getProof().getNextSecret().toByteArray())); + try { + secretKey = Option.some(KeyPair.generateForAlgorithm(Schema.PublicKey.Algorithm.Ed25519, data.getProof().getNextSecret().toByteArray())); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new Error.FormatError.AlgorithmError(e.getMessage()); + } } Option signature = Option.none(); @@ -245,8 +248,8 @@ static public Either make(final org.biscui algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); - sgr.initSign(root.private_key); + Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); + sgr.initSign(root.private_key()); sgr.update(block); sgr.update(algo_buf); sgr.update(next_key.toBytes()); @@ -278,8 +281,8 @@ public Either append(final org.biscuitsec. algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); - sgr.initSign(this.proof.secretKey.get().private_key); + Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); + sgr.initSign(this.proof.secretKey.get().private_key()); sgr.update(block); sgr.update(algo_buf); sgr.update(next_key.toBytes()); @@ -397,7 +400,7 @@ public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) algo_buf.putInt(next_key.algorithm.getNumber()); algo_buf.flip(); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); + Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); sgr.initVerify(current_key.key); sgr.update(block); @@ -427,7 +430,7 @@ static Either verifyBlockSignatu algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); + Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); sgr.initVerify(publicKey.key); sgr.update(block); @@ -445,7 +448,7 @@ static Either verifyBlockSignatu algo_buf2.putInt(Integer.valueOf(publicKey.algorithm.getNumber())); algo_buf2.flip(); - Signature sgr2 = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); + Signature sgr2 = KeyPair.getSignatureForAlgorithm(publicKey.algorithm); sgr2.initVerify(signedBlock.externalSignature.get().key.key); sgr2.update(block); sgr2.update(algo_buf2); @@ -530,13 +533,13 @@ public Either seal() throws InvalidKeyException, NoSuchAlgorithmExc block = this.blocks.get(this.blocks.size() - 1); } - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(KeyPair.ed25519.getHashAlgorithm())); + Signature sgr = KeyPair.getSignatureForAlgorithm(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.initSign(this.proof.secretKey.get().private_key()); sgr.update(block.block); sgr.update(algo_buf); sgr.update(block.key.toBytes()); diff --git a/src/main/proto/schema.proto b/src/main/proto/schema.proto index bc6cb295..329c04ba 100644 --- a/src/main/proto/schema.proto +++ b/src/main/proto/schema.proto @@ -26,6 +26,7 @@ message PublicKey { enum Algorithm { Ed25519 = 0; + P256 = 1; } required bytes key = 2; From 15a93d7339b2b6d227f9f43f1e3e8b706d560b10 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Tue, 21 May 2024 07:47:48 +1200 Subject: [PATCH 02/18] p256 update existing tests --- .../biscuit/crypto/KeyDelegate.java | 5 +- .../biscuitsec/biscuit/crypto/KeyPair.java | 11 +++- .../biscuitsec/biscuit/crypto/PublicKey.java | 7 +- .../org/biscuitsec/biscuit/crypto/Token.java | 6 +- .../org/biscuitsec/biscuit/token/Biscuit.java | 5 +- .../biscuit/token/UnverifiedBiscuit.java | 4 +- .../token/format/SerializedBiscuit.java | 16 ++--- .../biscuit/builder/BuilderTest.java | 7 +- .../biscuit/builder/parser/ParserTest.java | 4 +- .../biscuit/crypto/SignatureTest.java | 34 +++++----- .../biscuit/token/AuthorizerTest.java | 4 +- .../biscuitsec/biscuit/token/BiscuitTest.java | 64 ++++++++++--------- .../biscuitsec/biscuit/token/ExampleTest.java | 7 +- .../biscuitsec/biscuit/token/SamplesTest.java | 7 +- .../biscuit/token/UnverifiedBiscuitTest.java | 10 +-- 15 files changed, 109 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java index 234eebdf..a5b5f6a4 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java @@ -2,6 +2,9 @@ import io.vavr.control.Option; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; + /** * Used to find the key associated with a key id @@ -10,5 +13,5 @@ * Tokens can carry a root key id, that can be used to indicate which key will verify it. */ public interface KeyDelegate { - public Option root_key(Option key_id); + public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException; } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index 7f227987..ad09b7c2 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -2,6 +2,7 @@ import biscuit.format.schema.Schema.PublicKey.Algorithm; +import net.i2p.crypto.eddsa.Utils; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -13,7 +14,11 @@ */ public abstract class KeyPair { - public static KeyPair generateForAlgorithm(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public static KeyPair generate(Algorithm algorithm, String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + return generate(algorithm, Utils.hexToBytes(hex)); + } + + public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(bytes); } else if (algorithm == Algorithm.P256) { @@ -23,7 +28,7 @@ public static KeyPair generateForAlgorithm(Algorithm algorithm, byte[] bytes) th } } - public static KeyPair generateForAlgorithm(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public static KeyPair generate(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(rng); } else if (algorithm == Algorithm.P256) { @@ -33,7 +38,7 @@ public static KeyPair generateForAlgorithm(Algorithm algorithm, SecureRandom rng } } - public static Signature getSignatureForAlgorithm(Algorithm algorithm) throws NoSuchAlgorithmException { + public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException { if (algorithm == Algorithm.Ed25519) { return Ed25519KeyPair.getSignature(); } else if (algorithm == Algorithm.P256) { diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java index 1aa1ca3c..4f9789e8 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java @@ -2,6 +2,7 @@ import biscuit.format.schema.Schema; import biscuit.format.schema.Schema.PublicKey.Algorithm; +import net.i2p.crypto.eddsa.EdDSAPublicKey; import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.token.builder.Utils; import com.google.protobuf.ByteString; @@ -32,9 +33,9 @@ public PublicKey(Algorithm algorithm, byte[] data) throws NoSuchAlgorithmExcepti } public byte[] toBytes() { - // wtf is this? - // return this.key.getAbyte(); - + if (key instanceof EdDSAPublicKey) { + return ((EdDSAPublicKey) key).getAbyte(); + } return key.getEncoded(); } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java index ce858558..18ed8caf 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java @@ -18,7 +18,7 @@ class Token { public final KeyPair next; public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - Signature sgr = KeyPair.getSignatureForAlgorithm(next.public_key().algorithm); + 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(); @@ -47,7 +47,7 @@ public Token(final ArrayList blocks, final ArrayList keys, fi } public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - Signature sgr = KeyPair.getSignatureForAlgorithm(next.public_key().algorithm); + 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())); @@ -77,7 +77,7 @@ public Either verify(PublicKey root) throws NoSuchAlgorithmExceptio 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.getSignatureForAlgorithm(next.public_key().algorithm); + Signature sgr = KeyPair.generateSignature(next.public_key().algorithm); sgr.initVerify(current_key.key); sgr.update(block); sgr.update(algo_buf); diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index b23fa5be..0714358e 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -15,7 +15,6 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.security.Signature; import java.security.SignatureException; import java.util.*; @@ -115,7 +114,7 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op KeyPair next; try { - next = KeyPair.generateForAlgorithm(root.public_key().algorithm, rng); + next = KeyPair.generate(root.public_key().algorithm, rng); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } @@ -334,7 +333,7 @@ public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throw SecureRandom rng = new SecureRandom(); KeyPair keypair; try { - keypair = KeyPair.generateForAlgorithm(Schema.PublicKey.Algorithm.Ed25519, rng); // todo figure out how to get the algorithm + keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo figure out how to get the algorithm } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 4c059622..d7028524 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -142,7 +142,7 @@ public org.biscuitsec.biscuit.token.builder.Block create_block() { */ public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { SecureRandom rng = new SecureRandom(); - KeyPair keypair = KeyPair.generateForAlgorithm(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm + KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm return attenuate(rng, keypair, block.build()); } @@ -272,7 +272,7 @@ public Biscuit verify(PublicKey publicKey) throws Error, NoSuchAlgorithmExceptio return Biscuit.from_serialized_biscuit(serializedBiscuit, this.symbols); } - public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { SerializedBiscuit serializedBiscuit = this.serializedBiscuit; 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 b0952f96..39e8583a 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -72,7 +72,7 @@ static public SerializedBiscuit from_bytes(byte[] slice, KeyDelegate delegate) t } return from_bytes_inner(data, root.get()); - } catch (InvalidProtocolBufferException e) { + } catch (InvalidProtocolBufferException | InvalidAlgorithmParameterException e) { throw new Error.FormatError.DeserializationError(e.toString()); } } @@ -151,7 +151,7 @@ static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.F Option secretKey = Option.none(); if (data.getProof().hasNextSecret()) { try { - secretKey = Option.some(KeyPair.generateForAlgorithm(Schema.PublicKey.Algorithm.Ed25519, data.getProof().getNextSecret().toByteArray())); + secretKey = Option.some(KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, data.getProof().getNextSecret().toByteArray())); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } @@ -248,7 +248,7 @@ static public Either make(final org.biscui algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); + Signature sgr = KeyPair.generateSignature(next_key.algorithm); sgr.initSign(root.private_key()); sgr.update(block); sgr.update(algo_buf); @@ -281,7 +281,7 @@ public Either append(final org.biscuitsec. algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); + Signature sgr = KeyPair.generateSignature(next_key.algorithm); sgr.initSign(this.proof.secretKey.get().private_key()); sgr.update(block); sgr.update(algo_buf); @@ -400,7 +400,7 @@ public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) algo_buf.putInt(next_key.algorithm.getNumber()); algo_buf.flip(); - Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); + Signature sgr = KeyPair.generateSignature(next_key.algorithm); sgr.initVerify(current_key.key); sgr.update(block); @@ -430,7 +430,7 @@ static Either verifyBlockSignatu algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = KeyPair.getSignatureForAlgorithm(next_key.algorithm); + Signature sgr = KeyPair.generateSignature(next_key.algorithm); sgr.initVerify(publicKey.key); sgr.update(block); @@ -448,7 +448,7 @@ static Either verifyBlockSignatu algo_buf2.putInt(Integer.valueOf(publicKey.algorithm.getNumber())); algo_buf2.flip(); - Signature sgr2 = KeyPair.getSignatureForAlgorithm(publicKey.algorithm); + Signature sgr2 = KeyPair.generateSignature(publicKey.algorithm); sgr2.initVerify(signedBlock.externalSignature.get().key.key); sgr2.update(block); sgr2.update(algo_buf2); @@ -533,7 +533,7 @@ public Either seal() throws InvalidKeyException, NoSuchAlgorithmExc block = this.blocks.get(this.blocks.size() - 1); } - Signature sgr = KeyPair.getSignatureForAlgorithm(block.key.algorithm); + 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(); diff --git a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java index ed0b590e..2f263aba 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.builder; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.datalog.SymbolTable; import org.biscuitsec.biscuit.error.Error; @@ -11,6 +12,8 @@ import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.time.Instant; import java.util.Arrays; @@ -23,9 +26,9 @@ public class BuilderTest { @Test - public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.FormatError { + public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.FormatError, InvalidAlgorithmParameterException, NoSuchAlgorithmException { SecureRandom rng = new SecureRandom(); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); Block authority_builder = new Block(0, symbols); diff --git a/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java b/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java index 0230b235..5ba7aba2 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java @@ -18,6 +18,8 @@ import static org.biscuitsec.biscuit.datalog.Check.Kind.One; import static org.junit.jupiter.api.Assertions.*; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; import java.util.*; class ParserTest { @@ -183,7 +185,7 @@ void ruleWithFreeExpressionVariables() { } @Test - void testRuleWithScope() { + void testRuleWithScope() throws NoSuchAlgorithmException, InvalidKeySpecException { Either> res = Parser.rule("valid_date(\"file1\") <- resource(\"file1\") trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db, authority "); assertEquals(Either.right(new Tuple2<>("", diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index 26a1d69d..6352a2c5 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -2,10 +2,12 @@ import biscuit.format.schema.Schema; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; import static io.vavr.API.Left; import static io.vavr.API.Right; @@ -20,17 +22,17 @@ public class SignatureTest { @Test - public void testSerialize() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public void testSerialize() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException { byte[] seed = {1, 2, 3, 4}; SecureRandom rng = new SecureRandom(seed); - KeyPair keypair = new KeyPair(rng); + KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); PublicKey pubkey = keypair.public_key(); byte[] serializedSecretKey = keypair.toBytes(); byte[] serializedPublicKey = pubkey.toBytes(); - KeyPair deserializedSecretKey = new KeyPair(serializedSecretKey); + KeyPair deserializedSecretKey = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, serializedSecretKey); PublicKey deserializedPublicKey = new PublicKey(Schema.PublicKey.Algorithm.Ed25519, serializedPublicKey); assertEquals(32, serializedSecretKey.length); @@ -45,13 +47,13 @@ public void testSerialize() throws NoSuchAlgorithmException, SignatureException, } @Test - public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); String message1 = "hello"; - KeyPair root = new KeyPair(rng); - KeyPair keypair2 = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); System.out.println("root key: " + root.toHex()); System.out.println("keypair2: " + keypair2.toHex()); System.out.println("root key public: " + root.public_key().toHex()); @@ -61,38 +63,38 @@ public void testThreeMessages() throws NoSuchAlgorithmException, SignatureExcept assertEquals(Right(null), token1.verify(root.public_key())); String message2 = "world"; - KeyPair keypair3 = new KeyPair(rng); + KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Token token2 = token1.append(keypair3, message2.getBytes()); assertEquals(Right(null), token2.verify(root.public_key())); String message3 = "!!"; - KeyPair keypair4 = new KeyPair(rng); + KeyPair keypair4 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Token token3 = token2.append(keypair4, message3.getBytes()); assertEquals(Right(null), token3.verify(root.public_key())); } @Test - public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); String message1 = "hello"; - KeyPair root = new KeyPair(rng); - KeyPair keypair2 = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Token token1 = new Token(root, message1.getBytes(), keypair2); - assertEquals(Right(null), token1.verify(new PublicKey(Schema.PublicKey.Algorithm.Ed25519, root.public_key))); + assertEquals(Right(null), token1.verify(new PublicKey(Schema.PublicKey.Algorithm.Ed25519, root.publicKey()))); String message2 = "world"; - KeyPair keypair3 = new KeyPair(rng); + KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, 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(Schema.PublicKey.Algorithm.Ed25519, root.public_key))); + token2.verify(new PublicKey(Schema.PublicKey.Algorithm.Ed25519, root.publicKey()))); String message3 = "!!"; - KeyPair keypair4 = new KeyPair(rng); + KeyPair keypair4 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, 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(Schema.PublicKey.Algorithm.Ed25519, root.public_key))); + token3.verify(new PublicKey(Schema.PublicKey.Algorithm.Ed25519, root.publicKey()))); } } diff --git a/src/test/java/org/biscuitsec/biscuit/token/AuthorizerTest.java b/src/test/java/org/biscuitsec/biscuit/token/AuthorizerTest.java index 9c5f4b50..02053d84 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/AuthorizerTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/AuthorizerTest.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.error.Error.Parser; @@ -7,6 +8,7 @@ import org.biscuitsec.biscuit.token.builder.Term; import org.junit.jupiter.api.Test; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -43,7 +45,7 @@ public void testAuthorizerPolicy() throws Parser { @Test public void testPuttingSomeFactsInABiscuitAndGettingThemBackOutAgain() throws Exception { - KeyPair keypair = new KeyPair(); + KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, new SecureRandom()); Biscuit token = Biscuit.builder(keypair) .add_authority_fact("email(\"bob@example.com\")") diff --git a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java index 60575130..5d6f335e 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyDelegate; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.crypto.PublicKey; @@ -16,6 +17,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -30,13 +32,13 @@ public class BiscuitTest { @Test - public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error { + public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); Block authority_builder = new Block(0, symbols); @@ -65,7 +67,7 @@ public void testBasic() throws NoSuchAlgorithmException, SignatureException, Inv // SECOND BLOCK System.out.println("preparing the second block"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block builder = deser.create_block(); builder.add_check(check(rule( @@ -98,7 +100,7 @@ public void testBasic() throws NoSuchAlgorithmException, SignatureException, Inv // THIRD BLOCK System.out.println("preparing the third block"); - KeyPair keypair3 = new KeyPair(rng); + KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block builder3 = deser2.create_block(); builder3.add_check(check(rule( @@ -156,13 +158,13 @@ public void testBasic() throws NoSuchAlgorithmException, SignatureException, Inv } @Test - public void testFolders() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public void testFolders() throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); org.biscuitsec.biscuit.token.builder.Biscuit builder = Biscuit.builder(rng, root); @@ -181,7 +183,7 @@ public void testFolders() throws NoSuchAlgorithmException, SignatureException, I block2.resource_prefix("/folder1/"); block2.check_right("read"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Biscuit b2 = b.attenuate(rng, keypair2, block2.build()); Authorizer v1 = b2.authorizer(); @@ -223,9 +225,9 @@ public void testFolders() throws NoSuchAlgorithmException, SignatureException, I } @Test - public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { SecureRandom rng = new SecureRandom(); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); Block authority_builder = new Block(0, symbols); @@ -240,25 +242,25 @@ public void testMultipleAttenuation() throws NoSuchAlgorithmException, Signature Arrays.asList(s("topic"), s("tenant"), s("namespace"), s("topic"), s("produce")) )); - String attenuatedB64 = biscuit.attenuate(rng, new KeyPair(rng), builder.build()).serialize_b64url(); + String attenuatedB64 = biscuit.attenuate(rng, KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng), builder.build()).serialize_b64url(); System.out.println("attenuated: " + attenuatedB64); Biscuit.from_b64url(attenuatedB64, root.public_key()); - String attenuated2B64 = biscuit.attenuate(rng, new KeyPair(rng), builder.build()).serialize_b64url(); + String attenuated2B64 = biscuit.attenuate(rng, KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng), builder.build()).serialize_b64url(); System.out.println("attenuated2: " + attenuated2B64); Biscuit.from_b64url(attenuated2B64, root.public_key()); } @Test - public void testReset() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public void testReset() throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); org.biscuitsec.biscuit.token.builder.Biscuit builder = Biscuit.builder(rng, root); @@ -277,7 +279,7 @@ public void testReset() throws NoSuchAlgorithmException, SignatureException, Inv block2.resource_prefix("/folder1/"); block2.check_right("read"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Biscuit b2 = b.attenuate(rng, keypair2, block2.build()); Authorizer v1 = b2.authorizer(); @@ -321,13 +323,13 @@ public void testReset() throws NoSuchAlgorithmException, SignatureException, Inv } @Test - public void testEmptyAuthorizer() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); org.biscuitsec.biscuit.token.builder.Biscuit builder = Biscuit.builder(rng, root); @@ -346,7 +348,7 @@ public void testEmptyAuthorizer() throws NoSuchAlgorithmException, SignatureExce block2.resource_prefix("/folder1/"); block2.check_right("read"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Biscuit b2 = b.attenuate(rng, keypair2, block2.build()); Authorizer v1 = new Authorizer(); @@ -363,13 +365,13 @@ public void testEmptyAuthorizer() throws NoSuchAlgorithmException, SignatureExce } @Test - public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error { + public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); Block authority_builder = new Block(0, symbols); @@ -397,7 +399,7 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature // SECOND BLOCK System.out.println("preparing the second block"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block builder = deser.create_block(); builder.add_check(check(rule( @@ -430,7 +432,7 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature // THIRD BLOCK System.out.println("preparing the third block"); - KeyPair keypair3 = new KeyPair(rng); + KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block builder3 = deser2.create_block(); builder3.add_check(check(rule( @@ -488,13 +490,13 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature } @Test - public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error { + public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); org.biscuitsec.biscuit.token.builder.Biscuit o = new org.biscuitsec.biscuit.token.builder.Biscuit(rng, root, symbols); @@ -521,7 +523,7 @@ public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithm // SECOND BLOCK System.out.println("preparing the second block"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block builder = deser.create_block(); builder.add_check(check(rule( @@ -554,7 +556,7 @@ public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithm // THIRD BLOCK System.out.println("preparing the third block"); - KeyPair keypair3 = new KeyPair(rng); + KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block builder3 = deser2.create_block(); builder3.add_check(check(rule( @@ -611,13 +613,13 @@ public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithm } @Test - public void testRootKeyId() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error { + public void testRootKeyId() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); Block authority_builder = new Block(0, symbols); @@ -653,9 +655,9 @@ public Option root_key(Option key_id) { assertThrows(Error.FormatError.Signature.InvalidSignature.class, () -> { Biscuit deser = Biscuit.from_bytes(data, new KeyDelegate() { @Override - public Option root_key(Option key_id) { + public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); return Option.some(root.public_key()); } }); @@ -675,13 +677,13 @@ public Option root_key(Option key_id) { } @Test - public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block"); - KeyPair root = new KeyPair(rng); + KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Biscuit biscuit = Biscuit.builder(root) .add_authority_check("check all operation($op), allowed_operations($allowed), $allowed.contains($op)") diff --git a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java index eb6fdc9b..43bf9422 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java @@ -1,19 +1,22 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.token.builder.Block; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.security.SignatureException; /* example code for the documentation at https://www.biscuitsec.org * if these functions change, please send a PR to update them at https://github.com/biscuit-auth/website */ public class ExampleTest { - public KeyPair root() { - return new KeyPair(); + public KeyPair root() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + return KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, new SecureRandom()); } public Biscuit createToken(KeyPair root) throws Error { diff --git a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java index bb5907c4..af90134e 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java @@ -24,6 +24,9 @@ import java.io.BufferedInputStream; import java.io.InputStream; import java.io.InputStreamReader; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.util.*; import java.util.stream.Collectors; @@ -35,13 +38,13 @@ class SamplesTest { final RunLimits runLimits = new RunLimits(500,100, Duration.ofMillis(500)); @TestFactory - Stream jsonTest() { + Stream jsonTest() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException { InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("samples/samples.json"); Gson gson = new Gson(); Sample sample = gson.fromJson(new InputStreamReader(new BufferedInputStream(inputStream)), Sample.class); PublicKey publicKey = new PublicKey(Schema.PublicKey.Algorithm.Ed25519, sample.root_public_key); - KeyPair keyPair = new KeyPair(sample.root_private_key); + KeyPair keyPair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, sample.root_private_key); return sample.testcases.stream().map(t -> process_testcase(t, publicKey, keyPair)); } diff --git a/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java index 555b25df..bada2774 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.datalog.RunLimits; import org.biscuitsec.biscuit.datalog.SymbolTable; @@ -10,6 +11,7 @@ import org.biscuitsec.biscuit.token.builder.Utils; import org.junit.jupiter.api.Test; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -24,13 +26,13 @@ public class UnverifiedBiscuitTest { @Test - public void testBasic() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public void testBasic() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); System.out.println("preparing the authority block, block0"); - KeyPair keypair0 = new KeyPair(rng); + KeyPair keypair0 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); org.biscuitsec.biscuit.token.builder.Block block0 = new org.biscuitsec.biscuit.token.builder.Block(0, symbols); @@ -56,7 +58,7 @@ public void testBasic() throws Error, NoSuchAlgorithmException, SignatureExcepti // SECOND BLOCK System.out.println("preparing the second block"); - KeyPair keypair1 = new KeyPair(rng); + KeyPair keypair1 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); org.biscuitsec.biscuit.token.builder.Block block1 = deser0.create_block(); block1.add_check(Utils.check(Utils.rule( "caveat1", @@ -87,7 +89,7 @@ public void testBasic() throws Error, NoSuchAlgorithmException, SignatureExcepti // THIRD BLOCK System.out.println("preparing the third block"); - KeyPair keypair2 = new KeyPair(rng); + KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); Block block2 = unverifiedBiscuit1.create_block(); block2.add_check(Utils.check(Utils.rule( From 31bdef786dda28db28424dc83a4fcc0f840696d9 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Tue, 21 May 2024 08:18:47 +1200 Subject: [PATCH 03/18] p256 keypair impl --- .../biscuit/crypto/Ed25519KeyPair.java | 15 +------- .../biscuit/crypto/KeyDelegate.java | 3 +- .../biscuitsec/biscuit/crypto/KeyPair.java | 29 ++++++++------- .../biscuitsec/biscuit/crypto/PublicKey.java | 17 +++++---- ...P256KeyPair.java => SECP256R1KeyPair.java} | 37 ++++++++++--------- .../org/biscuitsec/biscuit/crypto/Token.java | 7 ++-- .../org/biscuitsec/biscuit/token/Biscuit.java | 3 +- .../biscuit/token/UnverifiedBiscuit.java | 3 +- .../biscuit/token/builder/parser/Parser.java | 6 +-- .../token/format/SerializedBiscuit.java | 25 ++++++++----- src/main/proto/schema.proto | 2 +- .../biscuit/crypto/SignatureTest.java | 4 +- .../biscuitsec/biscuit/token/BiscuitTest.java | 15 ++++---- .../biscuitsec/biscuit/token/ExampleTest.java | 5 ++- .../biscuit/token/UnverifiedBiscuitTest.java | 3 +- 15 files changed, 90 insertions(+), 84 deletions(-) rename src/main/java/org/biscuitsec/biscuit/crypto/{P256KeyPair.java => SECP256R1KeyPair.java} (54%) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java index 9bab1b52..3fae9a75 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java @@ -23,10 +23,6 @@ class Ed25519KeyPair extends KeyPair { private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); - public Ed25519KeyPair() { - this(new SecureRandom()); - } - public Ed25519KeyPair(byte[] bytes) { EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, ed25519); EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); @@ -53,16 +49,7 @@ public Ed25519KeyPair(SecureRandom rng) { } public Ed25519KeyPair(String hex) { - byte[] b = Utils.hexStringToByteArray(hex); - - EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519); - EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec); - - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519); - EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); - - this.privateKey = privKey; - this.publicKey = pubKey; + this(Utils.hexStringToByteArray(hex)); } public static java.security.PublicKey generatePublicKey(byte[] data) { diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java index a5b5f6a4..591278cc 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java @@ -4,6 +4,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; /** @@ -13,5 +14,5 @@ * Tokens can carry a root key id, that can be used to indicate which key will verify it. */ public interface KeyDelegate { - public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException; + public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException; } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index ad09b7c2..99603a49 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -8,6 +8,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Signature; +import java.security.spec.InvalidKeySpecException; /** * Private and public key. @@ -21,8 +22,8 @@ public static KeyPair generate(Algorithm algorithm, String hex) throws NoSuchAlg public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(bytes); - } else if (algorithm == Algorithm.P256) { - return new P256KeyPair(bytes); + } else if (algorithm == Algorithm.SECP256R1) { + return new SECP256R1KeyPair(bytes); } else { throw new NoSuchAlgorithmException("Unsupported algorithm"); } @@ -31,8 +32,8 @@ public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchA public static KeyPair generate(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(rng); - } else if (algorithm == Algorithm.P256) { - return new P256KeyPair(rng); + } else if (algorithm == Algorithm.SECP256R1) { + return new SECP256R1KeyPair(rng); } else { throw new NoSuchAlgorithmException("Unsupported algorithm"); } @@ -41,8 +42,8 @@ public static KeyPair generate(Algorithm algorithm, SecureRandom rng) throws NoS public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException { if (algorithm == Algorithm.Ed25519) { return Ed25519KeyPair.getSignature(); - } else if (algorithm == Algorithm.P256) { - return P256KeyPair.getSignature(); + } else if (algorithm == Algorithm.SECP256R1) { + return SECP256R1KeyPair.getSignature(); } else { throw new NoSuchAlgorithmException("Unsupported algorithm"); } @@ -52,29 +53,29 @@ public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgo * Returns the seed bytes of the private key. Returns null if the key was not created from a seed. * @return seed bytes. */ - public abstract byte[] toBytes(); // todo: rename to getSeedBytes() + public abstract byte[] toBytes(); /** - * Returns the hex representation of the seed bytes of the private key. Null if the key was not created from a seed. + * Returns the hex representation of the seed bytes of the private key. Returns null if the key was not created from a seed. * @return hex representation of the seed bytes. */ - public abstract String toHex(); // todo: rename to getHex() + public abstract String toHex(); /** * Returns the java.security.PublicKey. - * @return + * @return java public key. */ public abstract java.security.PublicKey publicKey(); /** * Returns the java.security.PrivateKey. - * @return + * @return private key. */ public abstract java.security.PrivateKey private_key(); /** - * Returns the biscuit.crypto.PublicKey. - * @return + * Returns the biscuit.crypto.PublicKey wrapper. + * @return biscuit public key. */ - public abstract PublicKey public_key(); + public abstract PublicKey public_key() throws NoSuchAlgorithmException, InvalidKeySpecException; } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java index 4f9789e8..020fbf74 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java @@ -9,6 +9,7 @@ import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; +import java.util.List; public class PublicKey { @@ -16,6 +17,8 @@ public class PublicKey { public final java.security.PublicKey key; public final Algorithm algorithm; + private static final List SUPPORTED_ALGORITHMS = List.of(Algorithm.Ed25519, Algorithm.SECP256R1); + public PublicKey(Algorithm algorithm, java.security.PublicKey public_key) { this.key = public_key; this.algorithm = algorithm; @@ -24,8 +27,8 @@ public PublicKey(Algorithm algorithm, java.security.PublicKey public_key) { public PublicKey(Algorithm algorithm, byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException { if (algorithm == Algorithm.Ed25519) { this.key = Ed25519KeyPair.generatePublicKey(data); - } else if (algorithm == Algorithm.P256) { - this.key = P256KeyPair.generatePublicKey(data); + } else if (algorithm == Algorithm.SECP256R1) { + this.key = SECP256R1KeyPair.generatePublicKey(data); } else { throw new IllegalArgumentException("Invalid algorithm"); } @@ -47,8 +50,8 @@ public PublicKey(Algorithm algorithm, String hex) throws NoSuchAlgorithmExceptio byte[] data = Utils.hexStringToByteArray(hex); if (algorithm == Algorithm.Ed25519) { this.key = Ed25519KeyPair.generatePublicKey(data); - } else if (algorithm == Algorithm.P256) { - this.key = P256KeyPair.generatePublicKey(data); + } else if (algorithm == Algorithm.SECP256R1) { + this.key = SECP256R1KeyPair.generatePublicKey(data); } else { throw new IllegalArgumentException("Invalid algorithm"); } @@ -66,7 +69,7 @@ static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatErro if(!pk.hasAlgorithm() || !pk.hasKey()) { throw new Error.FormatError.DeserializationError("Invalid public key"); } - if (pk.getAlgorithm() != Algorithm.Ed25519 && pk.getAlgorithm() != Algorithm.P256) { + if (!SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) { throw new Error.FormatError.DeserializationError("Invalid public key algorithm"); } try { @@ -97,8 +100,8 @@ public int hashCode() { public String toString() { if (algorithm == Algorithm.Ed25519) { return "ed25519/" + toHex().toLowerCase(); - } else if (algorithm == Algorithm.P256) { - return "p256/" + toHex().toLowerCase(); + } else if (algorithm == Algorithm.SECP256R1) { + return "secp256r1/" + toHex().toLowerCase(); } else { return null; } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java similarity index 54% rename from src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java rename to src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java index ed6faf5b..8cda39be 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/P256KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.crypto; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.token.builder.Utils; import java.security.InvalidAlgorithmParameterException; @@ -13,62 +14,62 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; -class P256KeyPair extends KeyPair { +class SECP256R1KeyPair extends KeyPair { private final java.security.KeyPair keyPair; - public P256KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { - this(new SecureRandom()); - } - - public P256KeyPair(byte[] seed) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public SECP256R1KeyPair(byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { var kpg = KeyPairGenerator.getInstance("EC"); var spec = new ECGenParameterSpec("secp256r1"); - kpg.initialize(spec, new SecureRandom(seed)); + kpg.initialize(spec, new SecureRandom(bytes)); keyPair = kpg.generateKeyPair(); } - public P256KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public SECP256R1KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + byte[] bytes = new byte[32]; + rng.nextBytes(bytes); var kpg = KeyPairGenerator.getInstance("EC"); var spec = new ECGenParameterSpec("secp256r1"); - kpg.initialize(spec, rng); + kpg.initialize(spec, new SecureRandom(bytes)); keyPair = kpg.generateKeyPair(); } - public P256KeyPair(String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public SECP256R1KeyPair(String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { this(Utils.hexStringToByteArray(hex)); } public static java.security.PublicKey generatePublicKey(byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException { - return KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(data, "SHA256withECDSA")); + var kf = KeyFactory.getInstance("EC"); + var spec = new X509EncodedKeySpec(data, "SHA256withECDSA"); + return kf.generatePublic(spec); } - public static Signature getSignature() { - throw new RuntimeException("not yet implemented"); + public static Signature getSignature() throws NoSuchAlgorithmException { + return Signature.getInstance("SHA256withECDSA"); } @Override public byte[] toBytes() { - throw new RuntimeException("not yet implemented"); + return keyPair.getPublic().getEncoded(); } @Override public String toHex() { - return Utils.byteArrayToHexString(this.toBytes()); + return Utils.byteArrayToHexString(toBytes()); } @Override public java.security.PublicKey publicKey() { - throw new RuntimeException("not yet implemented"); + return keyPair.getPublic(); } @Override public PrivateKey private_key() { - throw new RuntimeException("not yet implemented"); + return keyPair.getPrivate(); } @Override public PublicKey public_key() { - throw new RuntimeException("not yet implemented"); + return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, keyPair.getPublic()); } } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java index 18ed8caf..a8d8c4b2 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java @@ -6,6 +6,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.*; +import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import static io.vavr.API.Left; @@ -17,7 +18,7 @@ class Token { public final ArrayList signatures; public final KeyPair next; - public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException { 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())); @@ -46,7 +47,7 @@ public Token(final ArrayList blocks, final ArrayList keys, fi this.next = next; } - public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { + public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException { Signature sgr = KeyPair.generateSignature(next.public_key().algorithm); sgr.initSign(this.next.private_key()); ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); @@ -67,7 +68,7 @@ public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmExcep } // FIXME: rust version returns a Result<(), error::Signature> - public Either verify(PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + public Either verify(PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException { PublicKey current_key = root; for(int i = 0; i < this.blocks.size(); i++) { byte[] block = this.blocks.get(i); diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index 0714358e..65a9917d 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -16,6 +16,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; import java.util.*; /** @@ -115,7 +116,7 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op KeyPair next; try { next = KeyPair.generate(root.public_key().algorithm, rng); - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeySpecException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index d7028524..5f55ef55 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -17,6 +17,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; import java.util.*; import java.util.stream.Collectors; @@ -272,7 +273,7 @@ public Biscuit verify(PublicKey publicKey) throws Error, NoSuchAlgorithmExceptio return Biscuit.from_serialized_biscuit(serializedBiscuit, this.symbols); } - public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { + public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { SerializedBiscuit serializedBiscuit = this.serializedBiscuit; diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java index 1d0db5b0..61a08f99 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java @@ -326,10 +326,10 @@ public static Either> publicKey(String s) { s = s.substring("ed25519/".length()); Tuple2 t = hex(s); return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.Ed25519, t._2))); - } else if (s.startsWith("p256/")) { - s = s.substring("p256/".length()); + } else if (s.startsWith("secp256r1/")) { + s = s.substring("secp256r1/".length()); Tuple2 t = hex(s); - return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.P256, t._2))); + return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, t._2))); } else { return Either.left(new Error(s, "unrecognized public key prefix")); } 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 39e8583a..c0ffe931 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -18,6 +18,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.*; +import java.security.spec.InvalidKeySpecException; import java.util.*; import static io.vavr.API.Left; @@ -72,7 +73,7 @@ static public SerializedBiscuit from_bytes(byte[] slice, KeyDelegate delegate) t } return from_bytes_inner(data, root.get()); - } catch (InvalidProtocolBufferException | InvalidAlgorithmParameterException e) { + } catch (InvalidProtocolBufferException | InvalidAlgorithmParameterException | InvalidKeySpecException e) { throw new Error.FormatError.DeserializationError(e.toString()); } } @@ -259,7 +260,8 @@ static public Either make(final org.biscui Proof proof = new Proof(next); return Right(new SerializedBiscuit(signedBlock, new ArrayList<>(), proof, root_key_id)); - } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) { + } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException | + InvalidKeySpecException e) { return Left(new Error.FormatError.SerializationError(e.toString())); } } @@ -299,7 +301,8 @@ public Either append(final org.biscuitsec. Proof proof = new Proof(next); return Right(new SerializedBiscuit(this.authority, blocks, proof, root_key_id)); - } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) { + } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException | + InvalidKeySpecException e) { return Left(new Error.FormatError.SerializationError(e.toString())); } } @@ -372,14 +375,18 @@ public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) //System.out.println("checking secret key"); //System.out.println("current key: "+current_key.toHex()); //System.out.println("key from proof: "+this.proof.secretKey.get().public_key().toHex()); - if (this.proof.secretKey.get().public_key().equals(current_key)) { - //System.out.println("public keys are equal"); + try { + if (this.proof.secretKey.get().public_key().equals(current_key)) { + //System.out.println("public keys are equal"); - return Right(null); - } else { - //System.out.println("public keys are not equal"); + return Right(null); + } else { + //System.out.println("public keys are not equal"); - return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")); + return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")); + } + } catch (InvalidKeySpecException e) { + return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Invalid key spec")); } } else { //System.out.println("checking final signature"); diff --git a/src/main/proto/schema.proto b/src/main/proto/schema.proto index 329c04ba..641b4cf7 100644 --- a/src/main/proto/schema.proto +++ b/src/main/proto/schema.proto @@ -26,7 +26,7 @@ message PublicKey { enum Algorithm { Ed25519 = 0; - P256 = 1; + SECP256R1 = 1; } required bytes key = 2; diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index 6352a2c5..623688cb 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -47,7 +47,7 @@ public void testSerialize() throws NoSuchAlgorithmException, SignatureException, } @Test - public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { + public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -74,7 +74,7 @@ public void testThreeMessages() throws NoSuchAlgorithmException, SignatureExcept } @Test - public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { + public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); diff --git a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java index 5d6f335e..d5d500c2 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java @@ -22,6 +22,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.time.Instant; import java.util.*; @@ -32,7 +33,7 @@ public class BiscuitTest { @Test - public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -225,7 +226,7 @@ public void testFolders() throws NoSuchAlgorithmException, Error, InvalidAlgorit } @Test - public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { SecureRandom rng = new SecureRandom(); KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); @@ -365,7 +366,7 @@ public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error, Invali } @Test - public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -490,7 +491,7 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature } @Test - public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -655,7 +656,7 @@ public Option root_key(Option key_id) { assertThrows(Error.FormatError.Signature.InvalidSignature.class, () -> { Biscuit deser = Biscuit.from_bytes(data, new KeyDelegate() { @Override - public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException { KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); return Option.some(root.public_key()); @@ -665,7 +666,7 @@ public Option root_key(Option key_id) throws InvalidAlgorith Biscuit deser = Biscuit.from_bytes(data, new KeyDelegate() { @Override - public Option root_key(Option key_id) { + public Option root_key(Option key_id) throws NoSuchAlgorithmException, InvalidKeySpecException { if (key_id.get() == 1) { return Option.some(root.public_key()); } else { @@ -677,7 +678,7 @@ public Option root_key(Option key_id) { } @Test - public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { + public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); diff --git a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java index 43bf9422..39785812 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java @@ -10,6 +10,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; /* example code for the documentation at https://www.biscuitsec.org * if these functions change, please send a PR to update them at https://github.com/biscuit-auth/website @@ -26,7 +27,7 @@ public Biscuit createToken(KeyPair root) throws Error { .build(); } - public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidKeySpecException { return Biscuit.from_bytes(serializedToken, root.public_key()).authorizer() .add_fact("resource(\"/folder1/file1\")") .add_fact("operation(\"read\")") @@ -34,7 +35,7 @@ public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorit .authorize(); } - public Biscuit attenuate(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + public Biscuit attenuate(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidKeySpecException { Biscuit token = Biscuit.from_bytes(serializedToken, root.public_key()); Block block = token.create_block().add_check("check if operation(\"read\")"); return token.attenuate(block); diff --git a/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java index bada2774..c9ab57e1 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java @@ -16,6 +16,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.util.Arrays; import java.util.List; @@ -26,7 +27,7 @@ public class UnverifiedBiscuitTest { @Test - public void testBasic() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { + public void testBasic() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); From 46eda04dbe95f11d2b50041b26a3f83f5acb3562 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 23 May 2024 11:35:46 +1200 Subject: [PATCH 04/18] p256 struggles --- pom.xml | 5 ++ .../biscuit/crypto/Ed25519KeyPair.java | 2 +- .../biscuitsec/biscuit/crypto/KeyPair.java | 23 +----- .../biscuitsec/biscuit/crypto/PublicKey.java | 30 +++---- .../biscuit/crypto/SECP256R1KeyPair.java | 69 ++++++++++++---- .../org/biscuitsec/biscuit/token/Biscuit.java | 2 +- .../biscuit/token/builder/parser/Parser.java | 24 +++--- .../token/format/SerializedBiscuit.java | 22 ++--- .../biscuit/crypto/SignatureTest.java | 81 +++++++++++-------- 9 files changed, 140 insertions(+), 118 deletions(-) diff --git a/pom.xml b/pom.xml index 355fb830..1ac40a53 100644 --- a/pom.xml +++ b/pom.xml @@ -256,6 +256,11 @@ gson ${gson.version} + + org.bouncycastle + bcprov-jdk18on + 1.78.1 + org.junit.jupiter junit-jupiter diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java index 3fae9a75..22857f70 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java @@ -52,7 +52,7 @@ public Ed25519KeyPair(String hex) { this(Utils.hexStringToByteArray(hex)); } - public static java.security.PublicKey generatePublicKey(byte[] data) { + public static java.security.PublicKey decode(byte[] data) { return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, ed25519)); } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index 99603a49..71506d78 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -8,7 +8,6 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Signature; -import java.security.spec.InvalidKeySpecException; /** * Private and public key. @@ -49,33 +48,13 @@ public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgo } } - /** - * Returns the seed bytes of the private key. Returns null if the key was not created from a seed. - * @return seed bytes. - */ public abstract byte[] toBytes(); - /** - * Returns the hex representation of the seed bytes of the private key. Returns null if the key was not created from a seed. - * @return hex representation of the seed bytes. - */ public abstract String toHex(); - /** - * Returns the java.security.PublicKey. - * @return java public key. - */ public abstract java.security.PublicKey publicKey(); - /** - * Returns the java.security.PrivateKey. - * @return private key. - */ public abstract java.security.PrivateKey private_key(); - /** - * Returns the biscuit.crypto.PublicKey wrapper. - * @return biscuit public key. - */ - public abstract PublicKey public_key() throws NoSuchAlgorithmException, InvalidKeySpecException; + public abstract PublicKey public_key(); } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java index 020fbf74..8b6c7d39 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java @@ -6,9 +6,8 @@ import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.token.builder.Utils; import com.google.protobuf.ByteString; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.util.List; @@ -24,11 +23,11 @@ public PublicKey(Algorithm algorithm, java.security.PublicKey public_key) { this.algorithm = algorithm; } - public PublicKey(Algorithm algorithm, byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException { + public PublicKey(Algorithm algorithm, byte[] data) { if (algorithm == Algorithm.Ed25519) { - this.key = Ed25519KeyPair.generatePublicKey(data); + this.key = Ed25519KeyPair.decode(data); } else if (algorithm == Algorithm.SECP256R1) { - this.key = SECP256R1KeyPair.generatePublicKey(data); + this.key = SECP256R1KeyPair.decode(data); } else { throw new IllegalArgumentException("Invalid algorithm"); } @@ -36,22 +35,25 @@ public PublicKey(Algorithm algorithm, byte[] data) throws NoSuchAlgorithmExcepti } public byte[] toBytes() { - if (key instanceof EdDSAPublicKey) { + if (algorithm == Algorithm.Ed25519) { return ((EdDSAPublicKey) key).getAbyte(); + } else if (algorithm == Algorithm.SECP256R1) { + return ((BCECPublicKey) key).getQ().getEncoded(true); + } else { + throw new IllegalArgumentException("Invalid algorithm"); } - return key.getEncoded(); } public String toHex() { return Utils.byteArrayToHexString(this.toBytes()); } - public PublicKey(Algorithm algorithm, String hex) throws NoSuchAlgorithmException, InvalidKeySpecException { + public PublicKey(Algorithm algorithm, String hex) { byte[] data = Utils.hexStringToByteArray(hex); if (algorithm == Algorithm.Ed25519) { - this.key = Ed25519KeyPair.generatePublicKey(data); + this.key = Ed25519KeyPair.decode(data); } else if (algorithm == Algorithm.SECP256R1) { - this.key = SECP256R1KeyPair.generatePublicKey(data); + this.key = SECP256R1KeyPair.decode(data); } else { throw new IllegalArgumentException("Invalid algorithm"); } @@ -72,13 +74,7 @@ static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatErro if (!SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) { throw new Error.FormatError.DeserializationError("Invalid public key algorithm"); } - try { - return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray()); - } catch (NoSuchAlgorithmException e) { - throw new Error.FormatError.DeserializationError("No such algorithm"); - } catch (InvalidKeySpecException e) { - throw new Error.FormatError.DeserializationError("Invalid key spec"); - } + return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray()); } @Override diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java index 8cda39be..a04e1c57 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java @@ -2,55 +2,82 @@ import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.token.builder.Utils; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECPublicKeySpec; import java.security.InvalidAlgorithmParameterException; -import java.security.KeyFactory; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.SecureRandom; +import java.security.Security; import java.security.Signature; +import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; class SECP256R1KeyPair extends KeyPair { - private final java.security.KeyPair keyPair; + private final BCECPrivateKey privateKey; + private final BCECPublicKey publicKey; + + static { + Security.addProvider(new BouncyCastleProvider()); + } public SECP256R1KeyPair(byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { - var kpg = KeyPairGenerator.getInstance("EC"); + var kpg = getBcKeyPairGenerator(); var spec = new ECGenParameterSpec("secp256r1"); kpg.initialize(spec, new SecureRandom(bytes)); - keyPair = kpg.generateKeyPair(); + var keyPair = kpg.generateKeyPair(); + privateKey = (BCECPrivateKey) keyPair.getPrivate(); + publicKey = (BCECPublicKey) keyPair.getPublic(); } public SECP256R1KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { byte[] bytes = new byte[32]; rng.nextBytes(bytes); - var kpg = KeyPairGenerator.getInstance("EC"); + var kpg = getBcKeyPairGenerator(); var spec = new ECGenParameterSpec("secp256r1"); kpg.initialize(spec, new SecureRandom(bytes)); - keyPair = kpg.generateKeyPair(); + var keyPair = kpg.generateKeyPair(); + privateKey = (BCECPrivateKey) keyPair.getPrivate(); + publicKey = (BCECPublicKey) keyPair.getPublic(); } public SECP256R1KeyPair(String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { this(Utils.hexStringToByteArray(hex)); } - public static java.security.PublicKey generatePublicKey(byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException { - var kf = KeyFactory.getInstance("EC"); - var spec = new X509EncodedKeySpec(data, "SHA256withECDSA"); - return kf.generatePublic(spec); + public static java.security.PublicKey decode(byte[] data) { + var params = ECNamedCurveTable.getParameterSpec("secp256r1"); + var spec = new ECPublicKeySpec(params.getCurve().decodePoint(data), params); + return new BCECPublicKey("ECDSA", spec, BouncyCastleProvider.CONFIGURATION); + } + + public static byte[] encode(ECPublicKey ecPublicKey) { + var params = ecPublicKey.getParams(); + var w = ecPublicKey.getW(); + var bcSpec = EC5Util.convertSpec(params); + var bcPoint = bcSpec.getCurve().createPoint(w.getAffineX(), w.getAffineY()); + return bcPoint.getEncoded(true); // true for compressed } public static Signature getSignature() throws NoSuchAlgorithmException { - return Signature.getInstance("SHA256withECDSA"); + try { + return Signature.getInstance("SHA256withECDSA", "BC"); + } catch (NoSuchProviderException e) { + throw new RuntimeException(e); + } } @Override public byte[] toBytes() { - return keyPair.getPublic().getEncoded(); + return encode(publicKey); } @Override @@ -60,16 +87,24 @@ public String toHex() { @Override public java.security.PublicKey publicKey() { - return keyPair.getPublic(); + return publicKey; } @Override public PrivateKey private_key() { - return keyPair.getPrivate(); + return privateKey; } @Override public PublicKey public_key() { - return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, keyPair.getPublic()); + return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, publicKey); + } + + private static KeyPairGenerator getBcKeyPairGenerator() throws NoSuchAlgorithmException { + try { + return KeyPairGenerator.getInstance("EC", "BC"); + } catch (NoSuchProviderException e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index 65a9917d..b86dfbde 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -116,7 +116,7 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op KeyPair next; try { next = KeyPair.generate(root.public_key().algorithm, rng); - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeySpecException e) { + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java index 61a08f99..b4bc4dd5 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java @@ -321,20 +321,16 @@ public static Either> scope(String s) { } public static Either> publicKey(String s) { - try { - if (s.startsWith("ed25519/")) { - s = s.substring("ed25519/".length()); - Tuple2 t = hex(s); - return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.Ed25519, t._2))); - } else if (s.startsWith("secp256r1/")) { - s = s.substring("secp256r1/".length()); - Tuple2 t = hex(s); - return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, t._2))); - } else { - return Either.left(new Error(s, "unrecognized public key prefix")); - } - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - return Either.left(new Error(s, "error parsing public key")); + if (s.startsWith("ed25519/")) { + s = s.substring("ed25519/".length()); + Tuple2 t = hex(s); + return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.Ed25519, t._2))); + } else if (s.startsWith("secp256r1/")) { + s = s.substring("secp256r1/".length()); + Tuple2 t = hex(s); + return Either.right(new Tuple2(t._1, new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, t._2))); + } else { + return Either.left(new Error(s, "unrecognized public key prefix")); } } 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 c0ffe931..eaa03440 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -260,8 +260,7 @@ static public Either make(final org.biscui Proof proof = new Proof(next); return Right(new SerializedBiscuit(signedBlock, new ArrayList<>(), proof, root_key_id)); - } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException | - InvalidKeySpecException e) { + } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) { return Left(new Error.FormatError.SerializationError(e.toString())); } } @@ -301,8 +300,7 @@ public Either append(final org.biscuitsec. Proof proof = new Proof(next); return Right(new SerializedBiscuit(this.authority, blocks, proof, root_key_id)); - } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException | - InvalidKeySpecException e) { + } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) { return Left(new Error.FormatError.SerializationError(e.toString())); } } @@ -375,18 +373,14 @@ public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) //System.out.println("checking secret key"); //System.out.println("current key: "+current_key.toHex()); //System.out.println("key from proof: "+this.proof.secretKey.get().public_key().toHex()); - try { - if (this.proof.secretKey.get().public_key().equals(current_key)) { - //System.out.println("public keys are equal"); + if (this.proof.secretKey.get().public_key().equals(current_key)) { + //System.out.println("public keys are equal"); - return Right(null); - } else { - //System.out.println("public keys are not equal"); + return Right(null); + } else { + //System.out.println("public keys are not equal"); - return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")); - } - } catch (InvalidKeySpecException e) { - return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Invalid key spec")); + return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")); } } else { //System.out.println("checking final signature"); diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index 623688cb..ab244101 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -9,6 +9,7 @@ import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; +import static biscuit.format.schema.Schema.PublicKey.Algorithm.*; import static io.vavr.API.Left; import static io.vavr.API.Right; @@ -22,21 +23,39 @@ public class SignatureTest { @Test - public void testSerialize() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException { + public void testSerialize() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + testSerialize(Ed25519, 32); + testSerialize(SECP256R1, 33); + } + + @Test + public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + testThreeMessages(Ed25519); + testThreeMessages(SECP256R1); + } + + @Test + public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + testChangeMessages(Ed25519); + testChangeMessages(SECP256R1); + + } + + private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedKeyLength) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { byte[] seed = {1, 2, 3, 4}; SecureRandom rng = new SecureRandom(seed); - KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + KeyPair keypair = KeyPair.generate(algorithm, rng); PublicKey pubkey = keypair.public_key(); byte[] serializedSecretKey = keypair.toBytes(); byte[] serializedPublicKey = pubkey.toBytes(); - KeyPair deserializedSecretKey = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, serializedSecretKey); - PublicKey deserializedPublicKey = new PublicKey(Schema.PublicKey.Algorithm.Ed25519, serializedPublicKey); + KeyPair deserializedSecretKey = KeyPair.generate(algorithm, serializedSecretKey); + PublicKey deserializedPublicKey = new PublicKey(algorithm, serializedPublicKey); - assertEquals(32, serializedSecretKey.length); - assertEquals(32, serializedPublicKey.length); + assertEquals(expectedKeyLength, serializedSecretKey.length); + assertEquals(expectedKeyLength, serializedPublicKey.length); System.out.println(keypair.toHex()); System.out.println(deserializedSecretKey.toHex()); @@ -46,55 +65,53 @@ public void testSerialize() throws NoSuchAlgorithmException, SignatureException, assertEquals(pubkey.toHex(), deserializedPublicKey.toHex()); } - @Test - public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + private static void testChangeMessages(Schema.PublicKey.Algorithm algorithm) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); String message1 = "hello"; - KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); - KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); - System.out.println("root key: " + root.toHex()); - System.out.println("keypair2: " + keypair2.toHex()); - System.out.println("root key public: " + root.public_key().toHex()); - System.out.println("keypair2 public: " + keypair2.public_key().toHex()); - + 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(root.public_key())); + assertEquals(Right(null), token1.verify(new PublicKey(algorithm, root.publicKey()))); String message2 = "world"; - KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + KeyPair keypair3 = KeyPair.generate(algorithm, rng); Token token2 = token1.append(keypair3, message2.getBytes()); - assertEquals(Right(null), token2.verify(root.public_key())); + 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()))); String message3 = "!!"; - KeyPair keypair4 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + KeyPair keypair4 = KeyPair.generate(algorithm, rng); Token token3 = token2.append(keypair4, message3.getBytes()); - assertEquals(Right(null), token3.verify(root.public_key())); + assertEquals(Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied")), + token3.verify(new PublicKey(algorithm, root.publicKey()))); } - @Test - public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + private static void testThreeMessages(Schema.PublicKey.Algorithm algorithm) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); String message1 = "hello"; - KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); - KeyPair keypair2 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + KeyPair root = KeyPair.generate(algorithm, rng); + KeyPair keypair2 = KeyPair.generate(algorithm, rng); + System.out.println("root key: " + root.toHex()); + System.out.println("keypair2: " + keypair2.toHex()); + System.out.println("root key public: " + root.public_key().toHex()); + System.out.println("keypair2 public: " + keypair2.public_key().toHex()); + Token token1 = new Token(root, message1.getBytes(), keypair2); - assertEquals(Right(null), token1.verify(new PublicKey(Schema.PublicKey.Algorithm.Ed25519, root.publicKey()))); + assertEquals(Right(null), token1.verify(root.public_key())); String message2 = "world"; - KeyPair keypair3 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + 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(Schema.PublicKey.Algorithm.Ed25519, root.publicKey()))); + assertEquals(Right(null), token2.verify(root.public_key())); String message3 = "!!"; - KeyPair keypair4 = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + 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(Schema.PublicKey.Algorithm.Ed25519, root.publicKey()))); + assertEquals(Right(null), token3.verify(root.public_key())); } } From fac093940876ba129e8ca466820a1a06581f03f6 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 23 May 2024 12:44:01 +1200 Subject: [PATCH 05/18] return seed bytes like Ed.. feels weird though --- .../biscuitsec/biscuit/crypto/SECP256R1KeyPair.java | 13 ++++--------- .../biscuitsec/biscuit/crypto/SignatureTest.java | 7 ++++--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java index a04e1c57..0621126b 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java @@ -24,6 +24,7 @@ class SECP256R1KeyPair extends KeyPair { private final BCECPrivateKey privateKey; private final BCECPublicKey publicKey; + private final byte[] seed; static { Security.addProvider(new BouncyCastleProvider()); @@ -36,6 +37,7 @@ public SECP256R1KeyPair(byte[] bytes) throws NoSuchAlgorithmException, InvalidAl var keyPair = kpg.generateKeyPair(); privateKey = (BCECPrivateKey) keyPair.getPrivate(); publicKey = (BCECPublicKey) keyPair.getPublic(); + seed = bytes; } public SECP256R1KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { @@ -47,6 +49,7 @@ public SECP256R1KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, Inval var keyPair = kpg.generateKeyPair(); privateKey = (BCECPrivateKey) keyPair.getPrivate(); publicKey = (BCECPublicKey) keyPair.getPublic(); + seed = bytes; } public SECP256R1KeyPair(String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { @@ -59,14 +62,6 @@ public static java.security.PublicKey decode(byte[] data) { return new BCECPublicKey("ECDSA", spec, BouncyCastleProvider.CONFIGURATION); } - public static byte[] encode(ECPublicKey ecPublicKey) { - var params = ecPublicKey.getParams(); - var w = ecPublicKey.getW(); - var bcSpec = EC5Util.convertSpec(params); - var bcPoint = bcSpec.getCurve().createPoint(w.getAffineX(), w.getAffineY()); - return bcPoint.getEncoded(true); // true for compressed - } - public static Signature getSignature() throws NoSuchAlgorithmException { try { return Signature.getInstance("SHA256withECDSA", "BC"); @@ -77,7 +72,7 @@ public static Signature getSignature() throws NoSuchAlgorithmException { @Override public byte[] toBytes() { - return encode(publicKey); + return seed; } @Override diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index ab244101..d8188332 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -8,6 +8,7 @@ import java.security.SecureRandom; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; import static biscuit.format.schema.Schema.PublicKey.Algorithm.*; import static io.vavr.API.Left; @@ -41,7 +42,7 @@ public void testChangeMessages() throws NoSuchAlgorithmException, SignatureExcep } - private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedKeyLength) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { byte[] seed = {1, 2, 3, 4}; SecureRandom rng = new SecureRandom(seed); @@ -54,8 +55,8 @@ private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expe KeyPair deserializedSecretKey = KeyPair.generate(algorithm, serializedSecretKey); PublicKey deserializedPublicKey = new PublicKey(algorithm, serializedPublicKey); - assertEquals(expectedKeyLength, serializedSecretKey.length); - assertEquals(expectedKeyLength, serializedPublicKey.length); + assertEquals(32, serializedSecretKey.length); + assertEquals(expectedPublicKeyLength, serializedPublicKey.length); System.out.println(keypair.toHex()); System.out.println(deserializedSecretKey.toHex()); From dab7d6ac5fdcfd1a4a9799e7a5cb9e2c5a90085c Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 23 May 2024 13:34:50 +1200 Subject: [PATCH 06/18] fix for padding --- .../java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java index 0621126b..46af57c8 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java @@ -4,10 +4,10 @@ import org.biscuitsec.biscuit.token.builder.Utils; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; -import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.util.BigIntegers; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPairGenerator; @@ -17,7 +17,6 @@ import java.security.SecureRandom; import java.security.Security; import java.security.Signature; -import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; class SECP256R1KeyPair extends KeyPair { @@ -72,7 +71,7 @@ public static Signature getSignature() throws NoSuchAlgorithmException { @Override public byte[] toBytes() { - return seed; + return BigIntegers.asUnsignedByteArray(privateKey.getD()); } @Override From 6455586e75674f2d07298fcd9eb6a460c00c4c73 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 23 May 2024 13:59:23 +1200 Subject: [PATCH 07/18] fix it --- .../biscuit/crypto/SECP256R1KeyPair.java | 59 +++++++++---------- .../biscuit/crypto/SignatureTest.java | 8 ++- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java index 46af57c8..b6187a13 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java @@ -6,59 +6,64 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; import org.bouncycastle.jce.spec.ECPublicKeySpec; import org.bouncycastle.util.BigIntegers; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; -import java.security.spec.ECGenParameterSpec; class SECP256R1KeyPair extends KeyPair { private final BCECPrivateKey privateKey; private final BCECPublicKey publicKey; - private final byte[] seed; + + private static final String ALGORITHM = "ECDSA"; + private static final String CURVE = "secp256r1"; + private static final ECNamedCurveParameterSpec SECP256R1 = ECNamedCurveTable.getParameterSpec(CURVE); static { Security.addProvider(new BouncyCastleProvider()); } - public SECP256R1KeyPair(byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { - var kpg = getBcKeyPairGenerator(); - var spec = new ECGenParameterSpec("secp256r1"); - kpg.initialize(spec, new SecureRandom(bytes)); - var keyPair = kpg.generateKeyPair(); - privateKey = (BCECPrivateKey) keyPair.getPrivate(); - publicKey = (BCECPublicKey) keyPair.getPublic(); - seed = bytes; + public SECP256R1KeyPair(byte[] bytes) { + var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1); + var privateKey = new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION); + + var publicKeySpec = new ECPublicKeySpec(SECP256R1.getG().multiply(privateKeySpec.getD()), SECP256R1); + var publicKey = new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION); + + this.privateKey = privateKey; + this.publicKey = publicKey; } - public SECP256R1KeyPair(SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public SECP256R1KeyPair(SecureRandom rng) { byte[] bytes = new byte[32]; rng.nextBytes(bytes); - var kpg = getBcKeyPairGenerator(); - var spec = new ECGenParameterSpec("secp256r1"); - kpg.initialize(spec, new SecureRandom(bytes)); - var keyPair = kpg.generateKeyPair(); - privateKey = (BCECPrivateKey) keyPair.getPrivate(); - publicKey = (BCECPublicKey) keyPair.getPublic(); - seed = bytes; + + var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1); + var privateKey = new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION); + + var publicKeySpec = new ECPublicKeySpec(SECP256R1.getG().multiply(privateKeySpec.getD()), SECP256R1); + var publicKey = new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION); + + this.privateKey = privateKey; + this.publicKey = publicKey; } - public SECP256R1KeyPair(String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public SECP256R1KeyPair(String hex) { this(Utils.hexStringToByteArray(hex)); } public static java.security.PublicKey decode(byte[] data) { - var params = ECNamedCurveTable.getParameterSpec("secp256r1"); + var params = ECNamedCurveTable.getParameterSpec(CURVE); var spec = new ECPublicKeySpec(params.getCurve().decodePoint(data), params); - return new BCECPublicKey("ECDSA", spec, BouncyCastleProvider.CONFIGURATION); + return new BCECPublicKey(ALGORITHM, spec, BouncyCastleProvider.CONFIGURATION); } public static Signature getSignature() throws NoSuchAlgorithmException { @@ -93,12 +98,4 @@ public PrivateKey private_key() { public PublicKey public_key() { return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, publicKey); } - - private static KeyPairGenerator getBcKeyPairGenerator() throws NoSuchAlgorithmException { - try { - return KeyPairGenerator.getInstance("EC", "BC"); - } catch (NoSuchProviderException e) { - throw new RuntimeException(e); - } - } } diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index d8188332..e4ab0db6 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -16,6 +16,8 @@ import org.biscuitsec.biscuit.error.Error; import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -37,9 +39,8 @@ public void testThreeMessages() throws NoSuchAlgorithmException, SignatureExcept @Test public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { - testChangeMessages(Ed25519); + testChangeMessages(Ed25519); testChangeMessages(SECP256R1); - } private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { @@ -60,7 +61,8 @@ private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expe System.out.println(keypair.toHex()); System.out.println(deserializedSecretKey.toHex()); - assertEquals(keypair.toBytes(), deserializedSecretKey.toBytes()); + assertArrayEquals(keypair.toBytes(), deserializedSecretKey.toBytes()); + System.out.println(pubkey.toHex()); System.out.println(deserializedPublicKey.toHex()); assertEquals(pubkey.toHex(), deserializedPublicKey.toHex()); From 658d1207b4fca91327dacc3950f706772fc184aa Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 23 May 2024 14:19:39 +1200 Subject: [PATCH 08/18] exception cleanup since adding BC --- .../biscuitsec/biscuit/crypto/KeyDelegate.java | 4 +--- .../org/biscuitsec/biscuit/crypto/KeyPair.java | 7 +++---- .../java/org/biscuitsec/biscuit/crypto/Token.java | 7 +++---- .../org/biscuitsec/biscuit/token/Biscuit.java | 5 ++--- .../biscuit/token/UnverifiedBiscuit.java | 5 ++--- .../biscuit/token/builder/parser/Parser.java | 4 ---- .../biscuit/token/format/SerializedBiscuit.java | 5 ++--- .../biscuitsec/biscuit/builder/BuilderTest.java | 3 +-- .../biscuit/builder/parser/ParserTest.java | 6 +----- .../biscuitsec/biscuit/crypto/SignatureTest.java | 15 ++++++--------- .../org/biscuitsec/biscuit/token/BiscuitTest.java | 15 +++++++-------- .../org/biscuitsec/biscuit/token/ExampleTest.java | 8 +++----- .../org/biscuitsec/biscuit/token/SamplesTest.java | 10 +--------- .../biscuit/token/UnverifiedBiscuitTest.java | 5 +---- 14 files changed, 33 insertions(+), 66 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java index 591278cc..3a65c1e9 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java @@ -2,9 +2,7 @@ import io.vavr.control.Option; -import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; /** @@ -14,5 +12,5 @@ * Tokens can carry a root key id, that can be used to indicate which key will verify it. */ public interface KeyDelegate { - public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException; + public Option root_key(Option key_id) throws NoSuchAlgorithmException; } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index 71506d78..18dae39e 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -4,7 +4,6 @@ import biscuit.format.schema.Schema.PublicKey.Algorithm; import net.i2p.crypto.eddsa.Utils; -import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Signature; @@ -14,11 +13,11 @@ */ public abstract class KeyPair { - public static KeyPair generate(Algorithm algorithm, String hex) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public static KeyPair generate(Algorithm algorithm, String hex) throws NoSuchAlgorithmException { return generate(algorithm, Utils.hexToBytes(hex)); } - public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(bytes); } else if (algorithm == Algorithm.SECP256R1) { @@ -28,7 +27,7 @@ public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchA } } - public static KeyPair generate(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public static KeyPair generate(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(rng); } else if (algorithm == Algorithm.SECP256R1) { diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java index a8d8c4b2..18ed8caf 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java @@ -6,7 +6,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.*; -import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import static io.vavr.API.Left; @@ -18,7 +17,7 @@ class Token { public final ArrayList signatures; public final KeyPair next; - public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException { + 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())); @@ -47,7 +46,7 @@ public Token(final ArrayList blocks, final ArrayList keys, fi this.next = next; } - public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException { + 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); @@ -68,7 +67,7 @@ public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmExcep } // FIXME: rust version returns a Result<(), error::Signature> - public Either verify(PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException { + public Either verify(PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { PublicKey current_key = root; for(int i = 0; i < this.blocks.size(); i++) { byte[] block = this.blocks.get(i); diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index b86dfbde..b896adcd 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -16,7 +16,6 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; import java.util.*; /** @@ -116,7 +115,7 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op KeyPair next; try { next = KeyPair.generate(root.public_key().algorithm, rng); - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + } catch (NoSuchAlgorithmException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } @@ -335,7 +334,7 @@ public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throw KeyPair keypair; try { keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo figure out how to get the algorithm - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + } catch (NoSuchAlgorithmException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } return attenuate(rng, keypair, block.build()); diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 5f55ef55..73077f79 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -17,7 +17,6 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; import java.util.*; import java.util.stream.Collectors; @@ -141,7 +140,7 @@ public org.biscuitsec.biscuit.token.builder.Block create_block() { * @param block new block (should be generated from a Block builder) * @return */ - public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { + public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, Error { SecureRandom rng = new SecureRandom(); KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm return attenuate(rng, keypair, block.build()); @@ -273,7 +272,7 @@ public Biscuit verify(PublicKey publicKey) throws Error, NoSuchAlgorithmExceptio return Biscuit.from_serialized_biscuit(serializedBiscuit, this.symbols); } - public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { SerializedBiscuit serializedBiscuit = this.serializedBiscuit; diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java index b4bc4dd5..3c7ae0dd 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java @@ -1,17 +1,13 @@ package org.biscuitsec.biscuit.token.builder.parser; import biscuit.format.schema.Schema; -import io.vavr.collection.Stream; import org.biscuitsec.biscuit.crypto.PublicKey; -import org.biscuitsec.biscuit.datalog.SymbolTable; import org.biscuitsec.biscuit.token.Policy; import io.vavr.Tuple2; import io.vavr.Tuple4; import io.vavr.control.Either; import org.biscuitsec.biscuit.token.builder.*; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.time.OffsetDateTime; import java.time.format.DateTimeParseException; import java.util.*; 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 eaa03440..de93a5aa 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -18,7 +18,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.*; -import java.security.spec.InvalidKeySpecException; import java.util.*; import static io.vavr.API.Left; @@ -73,7 +72,7 @@ static public SerializedBiscuit from_bytes(byte[] slice, KeyDelegate delegate) t } return from_bytes_inner(data, root.get()); - } catch (InvalidProtocolBufferException | InvalidAlgorithmParameterException | InvalidKeySpecException e) { + } catch (InvalidProtocolBufferException e) { throw new Error.FormatError.DeserializationError(e.toString()); } } @@ -153,7 +152,7 @@ static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.F if (data.getProof().hasNextSecret()) { try { secretKey = Option.some(KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, data.getProof().getNextSecret().toByteArray())); - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + } catch (NoSuchAlgorithmException e) { throw new Error.FormatError.AlgorithmError(e.getMessage()); } } diff --git a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java index 2f263aba..65a815d8 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.time.Instant; @@ -26,7 +25,7 @@ public class BuilderTest { @Test - public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.FormatError, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.FormatError, NoSuchAlgorithmException { SecureRandom rng = new SecureRandom(); KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); diff --git a/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java b/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java index 5ba7aba2..b8205721 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java @@ -5,12 +5,10 @@ import org.biscuitsec.biscuit.datalog.SymbolTable; import org.biscuitsec.biscuit.datalog.TemporarySymbolTable; import org.biscuitsec.biscuit.datalog.expressions.Op; -import org.biscuitsec.biscuit.token.Biscuit; import org.biscuitsec.biscuit.token.builder.parser.Error; import org.biscuitsec.biscuit.token.builder.parser.Parser; import io.vavr.Tuple2; import io.vavr.control.Either; -import io.vavr.control.Option; import org.biscuitsec.biscuit.token.builder.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -18,8 +16,6 @@ import static org.biscuitsec.biscuit.datalog.Check.Kind.One; import static org.junit.jupiter.api.Assertions.*; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.util.*; class ParserTest { @@ -185,7 +181,7 @@ void ruleWithFreeExpressionVariables() { } @Test - void testRuleWithScope() throws NoSuchAlgorithmException, InvalidKeySpecException { + void testRuleWithScope() { Either> res = Parser.rule("valid_date(\"file1\") <- resource(\"file1\") trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db, authority "); assertEquals(Either.right(new Tuple2<>("", diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index e4ab0db6..6ec765d5 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -2,13 +2,10 @@ import biscuit.format.schema.Schema; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; import static biscuit.format.schema.Schema.PublicKey.Algorithm.*; import static io.vavr.API.Left; @@ -26,24 +23,24 @@ public class SignatureTest { @Test - public void testSerialize() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + public void testSerialize() throws NoSuchAlgorithmException { testSerialize(Ed25519, 32); testSerialize(SECP256R1, 33); } @Test - public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testThreeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { testThreeMessages(Ed25519); testThreeMessages(SECP256R1); } @Test - public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testChangeMessages() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { testChangeMessages(Ed25519); testChangeMessages(SECP256R1); } - private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) throws NoSuchAlgorithmException { byte[] seed = {1, 2, 3, 4}; SecureRandom rng = new SecureRandom(seed); @@ -68,7 +65,7 @@ private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expe assertEquals(pubkey.toHex(), deserializedPublicKey.toHex()); } - private static void testChangeMessages(Schema.PublicKey.Algorithm algorithm) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException { + private static void testChangeMessages(Schema.PublicKey.Algorithm algorithm) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -92,7 +89,7 @@ private static void testChangeMessages(Schema.PublicKey.Algorithm algorithm) thr token3.verify(new PublicKey(algorithm, root.publicKey()))); } - private static void testThreeMessages(Schema.PublicKey.Algorithm algorithm) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException { + private static void testThreeMessages(Schema.PublicKey.Algorithm algorithm) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); diff --git a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java index d5d500c2..eb5ec515 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java @@ -22,7 +22,6 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.time.Instant; import java.util.*; @@ -33,7 +32,7 @@ public class BiscuitTest { @Test - public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -226,7 +225,7 @@ public void testFolders() throws NoSuchAlgorithmException, Error, InvalidAlgorit } @Test - public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { SecureRandom rng = new SecureRandom(); KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); @@ -366,7 +365,7 @@ public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error, Invali } @Test - public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -491,7 +490,7 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature } @Test - public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -656,7 +655,7 @@ public Option root_key(Option key_id) { assertThrows(Error.FormatError.Signature.InvalidSignature.class, () -> { Biscuit deser = Biscuit.from_bytes(data, new KeyDelegate() { @Override - public Option root_key(Option key_id) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException { + public Option root_key(Option key_id) throws NoSuchAlgorithmException { KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); return Option.some(root.public_key()); @@ -666,7 +665,7 @@ public Option root_key(Option key_id) throws InvalidAlgorith Biscuit deser = Biscuit.from_bytes(data, new KeyDelegate() { @Override - public Option root_key(Option key_id) throws NoSuchAlgorithmException, InvalidKeySpecException { + public Option root_key(Option key_id) { if (key_id.get() == 1) { return Option.some(root.public_key()); } else { @@ -678,7 +677,7 @@ public Option root_key(Option key_id) throws NoSuchAlgorithm } @Test - public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); diff --git a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java index 39785812..ff00ad85 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java @@ -5,18 +5,16 @@ import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.token.builder.Block; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; /* example code for the documentation at https://www.biscuitsec.org * if these functions change, please send a PR to update them at https://github.com/biscuit-auth/website */ public class ExampleTest { - public KeyPair root() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + public KeyPair root() throws NoSuchAlgorithmException { return KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, new SecureRandom()); } @@ -27,7 +25,7 @@ public Biscuit createToken(KeyPair root) throws Error { .build(); } - public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidKeySpecException { + public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { return Biscuit.from_bytes(serializedToken, root.public_key()).authorizer() .add_fact("resource(\"/folder1/file1\")") .add_fact("operation(\"read\")") @@ -35,7 +33,7 @@ public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorit .authorize(); } - public Biscuit attenuate(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidKeySpecException { + public Biscuit attenuate(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { Biscuit token = Biscuit.from_bytes(serializedToken, root.public_key()); Block block = token.create_block().add_check("check if operation(\"read\")"); return token.attenuate(block); diff --git a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java index af90134e..aad0f7e3 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java @@ -2,21 +2,16 @@ import biscuit.format.schema.Schema; import com.google.gson.*; -import com.google.protobuf.MapEntry; import io.vavr.Tuple2; import io.vavr.control.Option; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.crypto.PublicKey; import org.biscuitsec.biscuit.datalog.Rule; import org.biscuitsec.biscuit.datalog.RunLimits; -import org.biscuitsec.biscuit.datalog.SymbolTable; -import org.biscuitsec.biscuit.datalog.TrustedOrigins; import org.biscuitsec.biscuit.error.Error; import io.vavr.control.Either; import io.vavr.control.Try; import org.biscuitsec.biscuit.token.builder.Check; -import org.biscuitsec.biscuit.token.builder.Expression; -import org.biscuitsec.biscuit.token.builder.parser.ExpressionParser; import org.biscuitsec.biscuit.token.builder.parser.Parser; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; @@ -24,13 +19,10 @@ import java.io.BufferedInputStream; import java.io.InputStream; import java.io.InputStreamReader; -import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; @@ -38,7 +30,7 @@ class SamplesTest { final RunLimits runLimits = new RunLimits(500,100, Duration.ofMillis(500)); @TestFactory - Stream jsonTest() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeySpecException { + Stream jsonTest() throws NoSuchAlgorithmException { InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("samples/samples.json"); Gson gson = new Gson(); diff --git a/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java index c9ab57e1..42547535 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/UnverifiedBiscuitTest.java @@ -11,23 +11,20 @@ import org.biscuitsec.biscuit.token.builder.Utils; import org.junit.jupiter.api.Test; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; import java.time.Duration; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; public class UnverifiedBiscuitTest { @Test - public void testBasic() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { + public void testBasic() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); From adc8f83a60b38cce0d85f87aa660c7a5b2205386 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 23 May 2024 14:26:38 +1200 Subject: [PATCH 09/18] cleanup more of the mess i made early on --- .../org/biscuitsec/biscuit/error/Error.java | 1 - .../org/biscuitsec/biscuit/token/Biscuit.java | 1 - .../biscuit/token/UnverifiedBiscuit.java | 1 - .../biscuitsec/biscuit/token/BiscuitTest.java | 19 +++++++++---------- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/error/Error.java b/src/main/java/org/biscuitsec/biscuit/error/Error.java index 06b03b27..aa88257c 100644 --- a/src/main/java/org/biscuitsec/biscuit/error/Error.java +++ b/src/main/java/org/biscuitsec/biscuit/error/Error.java @@ -6,7 +6,6 @@ import com.google.gson.JsonPrimitive; import io.vavr.control.Option; -import java.security.GeneralSecurityException; import java.util.List; import java.util.Objects; diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index b896adcd..0a773c0a 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -11,7 +11,6 @@ import io.vavr.control.Either; import io.vavr.control.Option; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 73077f79..a182bd0a 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -12,7 +12,6 @@ import org.biscuitsec.biscuit.datalog.Check; import org.biscuitsec.biscuit.datalog.SymbolTable; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; diff --git a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java index eb5ec515..11a93228 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -32,7 +31,7 @@ public class BiscuitTest { @Test - public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testBasic() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -158,7 +157,7 @@ public void testBasic() throws NoSuchAlgorithmException, SignatureException, Inv } @Test - public void testFolders() throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { + public void testFolders() throws NoSuchAlgorithmException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -225,7 +224,7 @@ public void testFolders() throws NoSuchAlgorithmException, Error, InvalidAlgorit } @Test - public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testMultipleAttenuation() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { SecureRandom rng = new SecureRandom(); KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); @@ -254,7 +253,7 @@ public void testMultipleAttenuation() throws NoSuchAlgorithmException, Signature } @Test - public void testReset() throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { + public void testReset() throws NoSuchAlgorithmException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -323,7 +322,7 @@ public void testReset() throws NoSuchAlgorithmException, Error, InvalidAlgorithm } @Test - public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error, InvalidAlgorithmParameterException { + public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -365,7 +364,7 @@ public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error, Invali } @Test - public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testBasicWithNamespaces() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -490,7 +489,7 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature } @Test - public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -613,7 +612,7 @@ public void testBasicWithNamespacesWithAddAuthorityFact() throws NoSuchAlgorithm } @Test - public void testRootKeyId() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error, InvalidAlgorithmParameterException { + public void testRootKeyId() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -677,7 +676,7 @@ public Option root_key(Option key_id) { } @Test - public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidAlgorithmParameterException { + public void testCheckAll() throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); From 13b8a6890e03fe8ded37a2cf9a3f8cc1b39a99a8 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Fri, 24 May 2024 10:43:52 +1200 Subject: [PATCH 10/18] add provided secp256r1 sample --- .../biscuit/crypto/SECP256R1KeyPair.java | 7 +- .../biscuit/crypto/SignatureTest.java | 2 +- src/test/resources/samples/samples.json | 72 +++++++++++++++++- .../resources/samples/test031_secp256r1.bc | Bin 0 -> 366 bytes 4 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 src/test/resources/samples/test031_secp256r1.bc diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java index b6187a13..8bbeb1c2 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java @@ -12,7 +12,6 @@ import org.bouncycastle.util.BigIntegers; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Security; @@ -67,11 +66,7 @@ public static java.security.PublicKey decode(byte[] data) { } public static Signature getSignature() throws NoSuchAlgorithmException { - try { - return Signature.getInstance("SHA256withECDSA", "BC"); - } catch (NoSuchProviderException e) { - throw new RuntimeException(e); - } + return Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider()); } @Override diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index 6ec765d5..97208c21 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -25,7 +25,7 @@ public class SignatureTest { @Test public void testSerialize() throws NoSuchAlgorithmException { testSerialize(Ed25519, 32); - testSerialize(SECP256R1, 33); + testSerialize(SECP256R1, 33); // compressed - 0x02 or 0x03 prefix byte, 32 bytes for X coordinate } @Test diff --git a/src/test/resources/samples/samples.json b/src/test/resources/samples/samples.json index 432c45d2..0888458a 100644 --- a/src/test/resources/samples/samples.json +++ b/src/test/resources/samples/samples.json @@ -2174,6 +2174,76 @@ ] } } + }, + { + "title": "ECDSA secp256r1 signatures", + "filename": "test031_secp256r1.bc", + "token": [ + { + "symbols": [ + "file1", + "file2" + ], + "public_keys": [], + "external_key": null, + "code": "right(\"file1\", \"read\");\nright(\"file2\", \"read\");\nright(\"file1\", \"write\");\n" + }, + { + "symbols": [ + "0" + ], + "public_keys": [], + "external_key": null, + "code": "check if resource($0), operation(\"read\"), right($0, \"read\");\n" + } + ], + "validations": { + "": { + "world": { + "facts": [ + { + "origin": [ + null + ], + "facts": [ + "operation(\"read\")", + "resource(\"file1\")" + ] + }, + { + "origin": [ + 0 + ], + "facts": [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")" + ] + } + ], + "rules": [], + "checks": [ + { + "origin": 1, + "checks": [ + "check if resource($0), operation(\"read\"), right($0, \"read\")" + ] + } + ], + "policies": [ + "allow if true" + ] + }, + "result": { + "Ok": 0 + }, + "authorizer_code": "resource(\"file1\");\noperation(\"read\");\n\nallow if true;\n", + "revocation_ids": [ + "760785de30d7348e9c847aab8b3bdad6a0d463f4f50ed9667aade563e9112ee6d2f589630dd7553c2eced2a57edf3636d5c874b35df15120c62fddcbdbd2de09", + "30440220039667c7a4d964e4b449289dc8fd206d7aa0e77eb701a9253b3307d32c177fa8022023f7523c143c5fb55ee4cafe49804702ef05a70883ebf42185b54bd36a7e7cd4" + ] + } + } } ] -} +} \ No newline at end of file diff --git a/src/test/resources/samples/test031_secp256r1.bc b/src/test/resources/samples/test031_secp256r1.bc new file mode 100644 index 0000000000000000000000000000000000000000..15a8a429e52823e318d362c4264507f2a92fc49d GIT binary patch literal 366 zcmWey!N_IH#hR9xlWGW|j3k(qc)7SaScI4*8aRZQBpBe_MpQ0XmQhHRgHcG4DQ;r_ z6*mi`b&Hy8|4tWMob5YXxO{K$-KM)=4(us?o4sGkp^Uxtp22mKzBw&btGlglU0ZM^ z`O8RpC-Q))O&X6YiBa=^-vqVbC;IZ-8VD4dZJ`=?8iWbWBPYb-@bH@Q)&() zmo^upfdsRW3YQWW6Nj`AI|q{xGe;AL5E}Joi6Mq$Qs}?-3+s?RB z)!LZ-vW|HD3MK{R??E;qHt}2Io}BvU+2GFfo^?4#^Xo5)ty{e>XVukQQBvYkIOTY2 jL+gaMOPmb1{rR$6kf&8!<%7ZgnOU75bj}>D+xigzPX&iC literal 0 HcmV?d00001 From a42407a06da447376cd4ee7e5d12829a23e8e1e6 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Fri, 24 May 2024 11:47:05 +1200 Subject: [PATCH 11/18] get the new test passing --- .../biscuitsec/biscuit/crypto/KeyPair.java | 10 +++--- .../biscuitsec/biscuit/crypto/PublicKey.java | 2 +- .../org/biscuitsec/biscuit/error/Error.java | 33 ------------------- .../org/biscuitsec/biscuit/token/Biscuit.java | 14 ++------ .../biscuit/token/UnverifiedBiscuit.java | 2 +- .../token/format/SerializedBiscuit.java | 28 +++++++++------- 6 files changed, 25 insertions(+), 64 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index 18dae39e..5ff512a4 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -13,27 +13,27 @@ */ public abstract class KeyPair { - public static KeyPair generate(Algorithm algorithm, String hex) throws NoSuchAlgorithmException { + public static KeyPair generate(Algorithm algorithm, String hex) { return generate(algorithm, Utils.hexToBytes(hex)); } - public static KeyPair generate(Algorithm algorithm, byte[] bytes) throws NoSuchAlgorithmException { + public static KeyPair generate(Algorithm algorithm, byte[] bytes) { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(bytes); } else if (algorithm == Algorithm.SECP256R1) { return new SECP256R1KeyPair(bytes); } else { - throw new NoSuchAlgorithmException("Unsupported algorithm"); + throw new IllegalArgumentException("Unsupported algorithm"); } } - public static KeyPair generate(Algorithm algorithm, SecureRandom rng) throws NoSuchAlgorithmException { + public static KeyPair generate(Algorithm algorithm, SecureRandom rng) { if (algorithm == Algorithm.Ed25519) { return new Ed25519KeyPair(rng); } else if (algorithm == Algorithm.SECP256R1) { return new SECP256R1KeyPair(rng); } else { - throw new NoSuchAlgorithmException("Unsupported algorithm"); + throw new IllegalArgumentException("Unsupported algorithm"); } } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java index 8b6c7d39..8eb850cf 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java @@ -38,7 +38,7 @@ public byte[] toBytes() { if (algorithm == Algorithm.Ed25519) { return ((EdDSAPublicKey) key).getAbyte(); } else if (algorithm == Algorithm.SECP256R1) { - return ((BCECPublicKey) key).getQ().getEncoded(true); + return ((BCECPublicKey) key).getQ().getEncoded(true); // true = compressed } else { throw new IllegalArgumentException("Invalid algorithm"); } diff --git a/src/main/java/org/biscuitsec/biscuit/error/Error.java b/src/main/java/org/biscuitsec/biscuit/error/Error.java index aa88257c..f1abdda1 100644 --- a/src/main/java/org/biscuitsec/biscuit/error/Error.java +++ b/src/main/java/org/biscuitsec/biscuit/error/Error.java @@ -332,39 +332,6 @@ public JsonElement toJson() { return FormatError.jsonWrapper(jo); } } - - public static class AlgorithmError extends FormatError { - final public String e; - - public AlgorithmError(String e) { - this.e = e; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AlgorithmError other = (AlgorithmError) o; - return e.equals(other.e); - } - - @Override - public int hashCode() { - return Objects.hash(e); - } - - @Override - public String toString() { - return "Err(FormatError.AlgorithmError{ error: "+ e + " }"; - } - - @Override - public JsonElement toJson() { - JsonObject jo = new JsonObject(); - jo.addProperty("AlgorithmError", this.e); - return FormatError.jsonWrapper(jo); - } - } } public static class InvalidAuthorityIndex extends Error { final public long index; diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index 0a773c0a..a61c04ff 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -111,12 +111,7 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op symbols.symbols.addAll(authority.symbols.symbols); ArrayList blocks = new ArrayList<>(); - KeyPair next; - try { - next = KeyPair.generate(root.public_key().algorithm, rng); - } catch (NoSuchAlgorithmException e) { - throw new Error.FormatError.AlgorithmError(e.getMessage()); - } + var next = KeyPair.generate(root.public_key().algorithm, rng); Either container = SerializedBiscuit.make(root, root_key_id, authority, next); if (container.isLeft()) { @@ -330,12 +325,7 @@ public String serialize_b64url() throws Error.FormatError.SerializationError { */ public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws Error { SecureRandom rng = new SecureRandom(); - KeyPair keypair; - try { - keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo figure out how to get the algorithm - } catch (NoSuchAlgorithmException e) { - throw new Error.FormatError.AlgorithmError(e.getMessage()); - } + KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm return attenuate(rng, keypair, block.build()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index a182bd0a..355a3e64 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -139,7 +139,7 @@ public org.biscuitsec.biscuit.token.builder.Block create_block() { * @param block new block (should be generated from a Block builder) * @return */ - public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, Error { + public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws Error { SecureRandom rng = new SecureRandom(); KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm return attenuate(rng, keypair, block.build()); 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 de93a5aa..32adb141 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -101,7 +101,7 @@ static SerializedBiscuit from_bytes_inner(Schema.Biscuit data, org.biscuitsec.bi * @return SerializedBiscuit * @throws Error.FormatError.DeserializationError */ - static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.FormatError.DeserializationError, Error.FormatError.AlgorithmError { + static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.FormatError.DeserializationError { try { Schema.Biscuit data = Schema.Biscuit.parseFrom(slice); return SerializedBiscuit.deserialize(data); @@ -117,7 +117,7 @@ static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.Fo * @return SerializedBiscuit * @throws Error.FormatError.DeserializationError */ - static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.FormatError.DeserializationError, Error.FormatError.AlgorithmError { + static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.FormatError.DeserializationError { if(data.getAuthority().hasExternalSignature()) { throw new Error.FormatError.DeserializationError("the authority block must not contain an external signature"); } @@ -150,11 +150,7 @@ static private SerializedBiscuit deserialize(Schema.Biscuit data) throws Error.F Option secretKey = Option.none(); if (data.getProof().hasNextSecret()) { - try { - secretKey = Option.some(KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, data.getProof().getNextSecret().toByteArray())); - } catch (NoSuchAlgorithmException e) { - throw new Error.FormatError.AlgorithmError(e.getMessage()); - } + secretKey = Option.some(KeyPair.generate(authority.key.algorithm, data.getProof().getNextSecret().toByteArray())); } Option signature = Option.none(); @@ -401,7 +397,6 @@ public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) algo_buf.flip(); Signature sgr = KeyPair.generateSignature(next_key.algorithm); - sgr.initVerify(current_key.key); sgr.update(block); sgr.update(algo_buf); @@ -423,15 +418,24 @@ static Either verifyBlockSignatu byte[] block = signedBlock.block; org.biscuitsec.biscuit.crypto.PublicKey next_key = signedBlock.key; byte[] signature = signedBlock.signature; - if (signature.length != 64) { - return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); + + if (publicKey.algorithm == Schema.PublicKey.Algorithm.Ed25519) { + if (signature.length != 64) { + return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); + } + } else if (publicKey.algorithm == Schema.PublicKey.Algorithm.SECP256R1) { + if (signature.length != 70) { + return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); + } + } else { + return Left(new Error.FormatError.Signature.InvalidSignature("unsupported algorithm")); } + 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); - + Signature sgr = KeyPair.generateSignature(publicKey.algorithm); sgr.initVerify(publicKey.key); sgr.update(block); if(signedBlock.externalSignature.isDefined()) { From 5ade42e705c27949099f5ed5a76cb1a3f960f2bf Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Fri, 24 May 2024 14:46:48 +1200 Subject: [PATCH 12/18] update samples readme as per rust pr --- src/test/resources/samples/README | 84 ++++++++++++++++++++++++++++ src/test/resources/samples/README.md | 84 ++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) diff --git a/src/test/resources/samples/README b/src/test/resources/samples/README index 0f915a7b..7ca746e2 100644 --- a/src/test/resources/samples/README +++ b/src/test/resources/samples/README @@ -2341,3 +2341,87 @@ World { result: `Ok(0)` + +------------------------------ + +## ECDSA secp256r1 signatures: test031_secp256r1.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +authorizer code: +``` +resource("file1"); +operation("read"); +allow if true; +``` + +revocation ids: +- `760785de30d7348e9c847aab8b3bdad6a0d463f4f50ed9667aade563e9112ee6d2f589630dd7553c2eced2a57edf3636d5c874b35df15120c62fddcbdbd2de09` +- `30440220039667c7a4d964e4b449289dc8fd206d7aa0e77eb701a9253b3307d32c177fa8022023f7523c143c5fb55ee4cafe49804702ef05a70883ebf42185b54bd36a7e7cd4` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + diff --git a/src/test/resources/samples/README.md b/src/test/resources/samples/README.md index e123c7c6..35d430e3 100644 --- a/src/test/resources/samples/README.md +++ b/src/test/resources/samples/README.md @@ -2357,3 +2357,87 @@ World { result: `Ok(0)` + +------------------------------ + +## ECDSA secp256r1 signatures: test031_secp256r1.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +authorizer code: +``` +resource("file1"); +operation("read"); +allow if true; +``` + +revocation ids: +- `760785de30d7348e9c847aab8b3bdad6a0d463f4f50ed9667aade563e9112ee6d2f589630dd7553c2eced2a57edf3636d5c874b35df15120c62fddcbdbd2de09` +- `30440220039667c7a4d964e4b449289dc8fd206d7aa0e77eb701a9253b3307d32c177fa8022023f7523c143c5fb55ee4cafe49804702ef05a70883ebf42185b54bd36a7e7cd4` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + From 57da835990896bce0e6bb4be43bb5804198ff47e Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Fri, 24 May 2024 15:30:49 +1200 Subject: [PATCH 13/18] cleaning up exceptions, imports --- .../java/org/biscuitsec/biscuit/crypto/KeyDelegate.java | 4 +--- src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java | 4 ++++ src/main/java/org/biscuitsec/biscuit/datalog/Rule.java | 1 - .../java/org/biscuitsec/biscuit/datalog/SymbolTable.java | 1 - src/main/java/org/biscuitsec/biscuit/token/Authorizer.java | 2 -- .../java/org/biscuitsec/biscuit/token/builder/Term.java | 2 -- .../java/org/biscuitsec/biscuit/builder/BuilderTest.java | 3 +-- .../java/org/biscuitsec/biscuit/crypto/SignatureTest.java | 4 ++-- src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java | 6 +++--- src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java | 5 ++--- src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java | 3 +-- 11 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java index 3a65c1e9..234eebdf 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyDelegate.java @@ -2,8 +2,6 @@ import io.vavr.control.Option; -import java.security.NoSuchAlgorithmException; - /** * Used to find the key associated with a key id @@ -12,5 +10,5 @@ * Tokens can carry a root key id, that can be used to indicate which key will verify it. */ public interface KeyDelegate { - public Option root_key(Option key_id) throws NoSuchAlgorithmException; + public Option root_key(Option key_id); } diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index 5ff512a4..9014271c 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -13,6 +13,10 @@ */ public abstract class KeyPair { + public static KeyPair generate(Algorithm algorithm) { + return generate(algorithm, new SecureRandom()); + } + public static KeyPair generate(Algorithm algorithm, String hex) { return generate(algorithm, Utils.hexToBytes(hex)); } diff --git a/src/main/java/org/biscuitsec/biscuit/datalog/Rule.java b/src/main/java/org/biscuitsec/biscuit/datalog/Rule.java index e30bb6dc..31323676 100644 --- a/src/main/java/org/biscuitsec/biscuit/datalog/Rule.java +++ b/src/main/java/org/biscuitsec/biscuit/datalog/Rule.java @@ -6,7 +6,6 @@ import io.vavr.Tuple2; import io.vavr.Tuple3; import io.vavr.control.Either; -import io.vavr.control.Option; import java.io.Serializable; import java.util.*; diff --git a/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java b/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java index 0216b29d..26500807 100644 --- a/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java +++ b/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java @@ -1,7 +1,6 @@ package org.biscuitsec.biscuit.datalog; import org.biscuitsec.biscuit.crypto.PublicKey; -import org.biscuitsec.biscuit.crypto.TokenSignature; import org.biscuitsec.biscuit.datalog.expressions.Expression; import org.biscuitsec.biscuit.token.builder.Utils; import io.vavr.control.Option; diff --git a/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java b/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java index 639d7f74..2e037986 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java @@ -2,7 +2,6 @@ import org.biscuitsec.biscuit.crypto.PublicKey; import org.biscuitsec.biscuit.datalog.*; -import org.biscuitsec.biscuit.datalog.Rule; import org.biscuitsec.biscuit.error.Error; import org.biscuitsec.biscuit.error.FailedCheck; import org.biscuitsec.biscuit.error.LogicError; @@ -12,7 +11,6 @@ import io.vavr.control.Option; import org.biscuitsec.biscuit.datalog.Scope; import org.biscuitsec.biscuit.token.builder.Check; -import org.biscuitsec.biscuit.token.builder.Fact; import org.biscuitsec.biscuit.token.builder.Term; import org.biscuitsec.biscuit.token.builder.parser.Parser; diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/Term.java b/src/main/java/org/biscuitsec/biscuit/token/builder/Term.java index 51631670..47c08c58 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/Term.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/Term.java @@ -2,9 +2,7 @@ import org.biscuitsec.biscuit.datalog.SymbolTable; -import java.text.SimpleDateFormat; import java.time.Instant; -import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Arrays; diff --git a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java index 65a815d8..1b1c3875 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; -import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.time.Instant; import java.util.Arrays; @@ -25,7 +24,7 @@ public class BuilderTest { @Test - public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.FormatError, NoSuchAlgorithmException { + public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.FormatError { SecureRandom rng = new SecureRandom(); KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable symbols = Biscuit.default_symbol_table(); diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index 97208c21..b215e2d0 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -23,7 +23,7 @@ public class SignatureTest { @Test - public void testSerialize() throws NoSuchAlgorithmException { + public void testSerialize() { testSerialize(Ed25519, 32); testSerialize(SECP256R1, 33); // compressed - 0x02 or 0x03 prefix byte, 32 bytes for X coordinate } @@ -40,7 +40,7 @@ public void testChangeMessages() throws NoSuchAlgorithmException, SignatureExcep testChangeMessages(SECP256R1); } - private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) throws NoSuchAlgorithmException { + private static void testSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) { byte[] seed = {1, 2, 3, 4}; SecureRandom rng = new SecureRandom(seed); diff --git a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java index 11a93228..3d8fd58c 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java @@ -253,7 +253,7 @@ public void testMultipleAttenuation() throws NoSuchAlgorithmException, Signature } @Test - public void testReset() throws NoSuchAlgorithmException, Error { + public void testReset() throws Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -322,7 +322,7 @@ public void testReset() throws NoSuchAlgorithmException, Error { } @Test - public void testEmptyAuthorizer() throws NoSuchAlgorithmException, Error { + public void testEmptyAuthorizer() throws Error { byte[] seed = {0, 0, 0, 0}; SecureRandom rng = new SecureRandom(seed); @@ -654,7 +654,7 @@ public Option root_key(Option key_id) { assertThrows(Error.FormatError.Signature.InvalidSignature.class, () -> { Biscuit deser = Biscuit.from_bytes(data, new KeyDelegate() { @Override - public Option root_key(Option key_id) throws NoSuchAlgorithmException { + public Option root_key(Option key_id) { KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); return Option.some(root.public_key()); diff --git a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java index ff00ad85..3a5aa02a 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java @@ -7,15 +7,14 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.security.SignatureException; /* example code for the documentation at https://www.biscuitsec.org * if these functions change, please send a PR to update them at https://github.com/biscuit-auth/website */ public class ExampleTest { - public KeyPair root() throws NoSuchAlgorithmException { - return KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, new SecureRandom()); + public KeyPair root() { + return KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519); } public Biscuit createToken(KeyPair root) throws Error { diff --git a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java index aad0f7e3..09914bd8 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java @@ -19,7 +19,6 @@ import java.io.BufferedInputStream; import java.io.InputStream; import java.io.InputStreamReader; -import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.util.*; import java.util.stream.Collectors; @@ -30,7 +29,7 @@ class SamplesTest { final RunLimits runLimits = new RunLimits(500,100, Duration.ofMillis(500)); @TestFactory - Stream jsonTest() throws NoSuchAlgorithmException { + Stream jsonTest() { InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("samples/samples.json"); Gson gson = new Gson(); From bfada322e931e12d44e6d87505f88ec2fe7c0e13 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Fri, 24 May 2024 15:33:23 +1200 Subject: [PATCH 14/18] squash condition --- src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java index 8eb850cf..3ab5ace9 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java @@ -68,12 +68,9 @@ public Schema.PublicKey serialize() { } static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError.DeserializationError { - if(!pk.hasAlgorithm() || !pk.hasKey()) { + if(!pk.hasAlgorithm() || !pk.hasKey() || !SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) { throw new Error.FormatError.DeserializationError("Invalid public key"); } - if (!SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) { - throw new Error.FormatError.DeserializationError("Invalid public key algorithm"); - } return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray()); } From b500faf004fbdcea88481ada2f78aaa125f86341 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 13 Jun 2024 11:32:39 +1200 Subject: [PATCH 15/18] resolve todos by making attenuation specify the algorithm to use --- src/main/java/org/biscuitsec/biscuit/token/Biscuit.java | 6 ++++-- .../org/biscuitsec/biscuit/token/UnverifiedBiscuit.java | 6 ++++-- src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index a61c04ff..2a28c307 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -1,6 +1,7 @@ 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; import org.biscuitsec.biscuit.crypto.PublicKey; @@ -321,11 +322,12 @@ public String serialize_b64url() throws Error.FormatError.SerializationError { * Generates a new token from an existing one and a new block * * @param block new block (should be generated from a Block builder) + * @param algorithm algorithm to use for the ephemeral key pair * @return */ - public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws Error { + public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block, Algorithm algorithm) throws Error { SecureRandom rng = new SecureRandom(); - KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm + var keypair = KeyPair.generate(algorithm, rng); return attenuate(rng, keypair, block.build()); } diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 355a3e64..2df04643 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -1,6 +1,7 @@ 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; import org.biscuitsec.biscuit.crypto.PublicKey; @@ -137,11 +138,12 @@ public org.biscuitsec.biscuit.token.builder.Block create_block() { * Generates a new token from an existing one and a new block * * @param block new block (should be generated from a Block builder) + * @param algorithm algorithm to use for the ephemeral key pair * @return */ - public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws Error { + public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block, Algorithm algorithm) throws Error { SecureRandom rng = new SecureRandom(); - KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); // todo, figure out how to get the algorithm + var keypair = KeyPair.generate(algorithm, rng); return attenuate(rng, keypair, block.build()); } diff --git a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java index 3a5aa02a..17e83381 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/ExampleTest.java @@ -35,7 +35,7 @@ public Long authorize(KeyPair root, byte[] serializedToken) throws NoSuchAlgorit public Biscuit attenuate(KeyPair root, byte[] serializedToken) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { Biscuit token = Biscuit.from_bytes(serializedToken, root.public_key()); Block block = token.create_block().add_check("check if operation(\"read\")"); - return token.attenuate(block); + return token.attenuate(block, root.public_key().algorithm); } /*public Set query(Authorizer authorizer) throws Error.Timeout, Error.TooManyFacts, Error.TooManyIterations, Error.Parser { From 6b1c00bf887aa1c7b278c03ddacf0b31f5b7a357 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Fri, 14 Jun 2024 09:18:44 +1200 Subject: [PATCH 16/18] accomodate for variable length ecdsa signature, and throw errors during verify --- .../biscuit/token/UnverifiedBiscuit.java | 11 ++++++++--- .../biscuit/token/format/SerializedBiscuit.java | 2 +- .../biscuitsec/biscuit/crypto/SignatureTest.java | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 2df04643..31db6f49 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -269,20 +269,25 @@ public UnverifiedBiscuit copy() throws Error { public Biscuit verify(PublicKey publicKey) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { SerializedBiscuit serializedBiscuit = this.serializedBiscuit; - serializedBiscuit.verify(publicKey); + var result = serializedBiscuit.verify(publicKey); + if (result.isLeft()) { + throw result.getLeft(); + } return Biscuit.from_serialized_biscuit(serializedBiscuit, this.symbols); } public Biscuit verify(KeyDelegate delegate) throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException { SerializedBiscuit serializedBiscuit = this.serializedBiscuit; - Option root = delegate.root_key(root_key_id); if(root.isEmpty()) { throw new InvalidKeyException("unknown root key id"); } - serializedBiscuit.verify(root.get()); + var result = serializedBiscuit.verify(root.get()); + if (result.isLeft()) { + throw result.getLeft(); + } return Biscuit.from_serialized_biscuit(serializedBiscuit, this.symbols); } } 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 32adb141..52c6d4dd 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -424,7 +424,7 @@ static Either verifyBlockSignatu return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); } } else if (publicKey.algorithm == Schema.PublicKey.Algorithm.SECP256R1) { - if (signature.length != 70) { + if (signature.length < 70 || signature.length > 72) { return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); } } else { diff --git a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java index b215e2d0..dfd0a8dd 100644 --- a/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java +++ b/src/test/java/org/biscuitsec/biscuit/crypto/SignatureTest.java @@ -12,9 +12,11 @@ import static io.vavr.API.Right; import org.biscuitsec.biscuit.error.Error; +import org.biscuitsec.biscuit.token.Biscuit; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -114,4 +116,16 @@ private static void testThreeMessages(Schema.PublicKey.Algorithm algorithm) thro Token token3 = token2.append(keypair4, message3.getBytes()); assertEquals(Right(null), token3.verify(root.public_key())); } + + @Test + 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(); + var serialized = biscuit.serialize(); + var unverified = Biscuit.from_bytes(serialized); + assertDoesNotThrow(() -> unverified.verify(root.public_key())); + } } From b5ca28c046293e39ab89d8a9281ce7e138d67c49 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Tue, 18 Jun 2024 12:50:33 +1200 Subject: [PATCH 17/18] amend secp256r1 signature length check --- .../org/biscuitsec/biscuit/token/format/SerializedBiscuit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 52c6d4dd..0dd92732 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -424,7 +424,7 @@ static Either verifyBlockSignatu return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); } } else if (publicKey.algorithm == Schema.PublicKey.Algorithm.SECP256R1) { - if (signature.length < 70 || signature.length > 72) { + if (signature.length < 68 || signature.length > 72) { return Either.left(new Error.FormatError.Signature.InvalidSignatureSize(signature.length)); } } else { From c4df990aa9c4fb48c4acb47df4003edfd3bfb2e4 Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 25 Jun 2024 15:01:35 +1200 Subject: [PATCH 18/18] Implement 3rd party block creation (#104) (#4) This implements 3rd party block generation and appending to existing tokens. It also fixes existing issues with public key interning which made deserialization of tokens with 3rd party blocks incorrect in some cases. Co-authored-by: Geoffroy Couprie --- .../biscuitsec/biscuit/crypto/KeyPair.java | 29 +++ .../biscuit/datalog/SymbolTable.java | 2 +- .../biscuitsec/biscuit/token/Authorizer.java | 7 +- .../org/biscuitsec/biscuit/token/Biscuit.java | 33 ++- .../org/biscuitsec/biscuit/token/Block.java | 34 ++- .../token/ThirdPartyBlockContents.java | 85 +++++++ .../biscuit/token/ThirdPartyBlockRequest.java | 122 ++++++++++ .../biscuit/token/UnverifiedBiscuit.java | 105 ++++++++- .../biscuit/token/builder/Block.java | 9 +- .../biscuit/token/builder/parser/Parser.java | 2 +- .../token/format/SerializedBiscuit.java | 56 +---- .../biscuit/builder/BuilderTest.java | 2 +- .../biscuit/builder/parser/ParserTest.java | 6 +- .../biscuitsec/biscuit/token/BiscuitTest.java | 8 +- .../biscuitsec/biscuit/token/SamplesTest.java | 17 +- .../biscuit/token/ThirdPartyTest.java | 220 ++++++++++++++++++ 16 files changed, 645 insertions(+), 92 deletions(-) create mode 100644 src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockContents.java create mode 100644 src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java create mode 100644 src/test/java/org/biscuitsec/biscuit/token/ThirdPartyTest.java diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java index a12c3de0..37dfd147 100644 --- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java +++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java @@ -2,12 +2,17 @@ import biscuit.format.schema.Schema; +import biscuit.format.schema.Schema.PublicKey.Algorithm; +import net.i2p.crypto.eddsa.EdDSAEngine; import org.biscuitsec.biscuit.token.builder.Utils; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; import net.i2p.crypto.eddsa.spec.*; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.Signature; /** * Private and public key @@ -39,6 +44,26 @@ public KeyPair(final SecureRandom rng) { this.public_key = pubKey; } + public static KeyPair generate(Algorithm algorithm) { + return generate(algorithm, new SecureRandom()); + } + + public static KeyPair generate(Algorithm algorithm, SecureRandom rng) { + if (algorithm == Algorithm.Ed25519) { + return new KeyPair(rng); + } else { + throw new IllegalArgumentException("Unsupported algorithm"); + } + } + + public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException { + if (algorithm == Algorithm.Ed25519) { + return KeyPair.getSignature(); + } else { + throw new NoSuchAlgorithmException("Unsupported algorithm"); + } + } + public byte[] toBytes() { return this.private_key.getSeed(); } @@ -71,6 +96,10 @@ public KeyPair(String hex) { this.public_key = pubKey; } + public static Signature getSignature() throws NoSuchAlgorithmException { + return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm())); + } + public PublicKey public_key() { return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.public_key); } diff --git a/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java b/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java index 3b4a9e5e..e7960d98 100644 --- a/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java +++ b/src/main/java/org/biscuitsec/biscuit/datalog/SymbolTable.java @@ -180,7 +180,7 @@ public String print_scope(final Scope scope) { return pk.get().toString(); } } - return "?"; + return "<"+ scope.publicKey+"?>"; } public String print_predicate(final Predicate p) { diff --git a/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java b/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java index e018e90d..f593a6df 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Authorizer.java @@ -614,8 +614,13 @@ public String print_world() { for (int i = 0; i < this.token.blocks.size(); i++) { Block b = this.token.blocks.get(i); + SymbolTable blockSymbols = token.symbols; + if(b.externalKey.isDefined()) { + blockSymbols = new SymbolTable(b.symbols.symbols, token.symbols.publicKeys()); + } + for (int j = 0; j < b.checks.size(); j++) { - checks.add("Block[" + i + "][" + j + "]: " + this.symbols.print_check(b.checks.get(j))); + checks.add("Block[" + (i+1) + "][" + j + "]: " + blockSymbols.print_check(b.checks.get(j))); } } } diff --git a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java index 7f71296f..662612b7 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Biscuit.java @@ -1,5 +1,6 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; import org.biscuitsec.biscuit.crypto.KeyDelegate; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.crypto.PublicKey; @@ -10,10 +11,7 @@ import io.vavr.control.Either; import io.vavr.control.Option; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.SignatureException; +import java.security.*; import java.util.*; /** @@ -91,7 +89,11 @@ public static Biscuit make(final SecureRandom rng, final KeyPair root, final Int static private Biscuit make(final SecureRandom rng, final KeyPair root, final Option root_key_id, final Block authority) throws Error.FormatError { ArrayList blocks = new ArrayList<>(); - KeyPair next = new KeyPair(rng); + KeyPair next = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); + + for(PublicKey pk: authority.publicKeys) { + authority.symbols.insert(pk); + } Either container = SerializedBiscuit.make(root, root_key_id, authority, next); if (container.isLeft()) { @@ -304,7 +306,7 @@ public String serialize_b64url() throws Error.FormatError.SerializationError { */ public Biscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws Error { SecureRandom rng = new SecureRandom(); - KeyPair keypair = new KeyPair(rng); + KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable builderSymbols = new SymbolTable(this.symbols); return attenuate(rng, keypair, block.build(builderSymbols)); } @@ -329,7 +331,7 @@ public Biscuit attenuate(final SecureRandom rng, final KeyPair keypair, Block bl throw new Error.SymbolTableOverlap(); } - Either containerRes = copiedBiscuit.serializedBiscuit.append(keypair, block); + Either containerRes = copiedBiscuit.serializedBiscuit.append(keypair, block, Option.none()); if (containerRes.isLeft()) { throw containerRes.getLeft(); } @@ -340,6 +342,10 @@ public Biscuit attenuate(final SecureRandom rng, final KeyPair keypair, Block bl symbols.add(s); } + for(PublicKey pk: block.publicKeys) { + symbols.insert(pk); + } + ArrayList blocks = new ArrayList<>(); for (Block b : copiedBiscuit.blocks) { blocks.add(b); @@ -354,6 +360,17 @@ public Biscuit attenuate(final SecureRandom rng, final KeyPair keypair, Block bl return new Biscuit(copiedBiscuit.authority, blocks, symbols, container, publicKeyToBlockId, revocation_ids); } + /** + * Generates a third party block request from a token + */ + public Biscuit appendThirdPartyBlock(PublicKey externalKey, ThirdPartyBlockContents blockResponse) + throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { + UnverifiedBiscuit b = super.appendThirdPartyBlock(externalKey, blockResponse); + + // no need to verify again, we are already working from a verified token + return Biscuit.from_serialized_biscuit(b.serializedBiscuit, b.symbols); + } + /** * Prints a token's content */ @@ -361,6 +378,8 @@ public String print() { StringBuilder s = new StringBuilder(); s.append("Biscuit {\n\tsymbols: "); s.append(this.symbols.getAllSymbols()); + s.append("\n\tpublic keys: "); + s.append(this.symbols.publicKeys()); s.append("\n\tauthority: "); s.append(this.authority.print(this.symbols)); s.append("\n\tblocks: [\n"); diff --git a/src/main/java/org/biscuitsec/biscuit/token/Block.java b/src/main/java/org/biscuitsec/biscuit/token/Block.java index 9e8fed83..975a006a 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/Block.java +++ b/src/main/java/org/biscuitsec/biscuit/token/Block.java @@ -31,8 +31,8 @@ public class Block { final List checks; final List scopes; final List publicKeys; - final Option externalKey; - final long version; + Option externalKey; + long version; /** * creates a new block @@ -48,7 +48,6 @@ public Block(SymbolTable base_symbols) { this.scopes = new ArrayList<>(); this.publicKeys = new ArrayList<>(); this.externalKey = Option.none(); - this.version = SerializedBiscuit.MAX_SCHEMA_VERSION; } /** @@ -66,7 +65,6 @@ public Block(SymbolTable base_symbols, String context, List facts, List publicKeys() { return publicKeys; } + public void setExternalKey(PublicKey externalKey) { + this.externalKey = Option.some(externalKey); + } + /** * pretty printing for a block * @@ -88,9 +90,22 @@ public List publicKeys() { public String print(SymbolTable symbol_table) { StringBuilder s = new StringBuilder(); + SymbolTable local_symbols; + if(this.externalKey.isDefined()) { + local_symbols = new SymbolTable(this.symbols); + for(PublicKey pk: symbol_table.publicKeys()) { + local_symbols.insert(pk); + } + } else { + local_symbols = symbol_table; + } s.append("Block"); s.append(" {\n\t\tsymbols: "); s.append(this.symbols.symbols); + s.append("\n\t\tpublic keys: "); + s.append(this.publicKeys); + s.append("\n\t\tsymbol public keys: "); + s.append(this.symbols.publicKeys()); s.append("\n\t\tcontext: "); s.append(this.context); if(this.externalKey.isDefined()) { @@ -105,17 +120,17 @@ public String print(SymbolTable symbol_table) { s.append("\n\t\t]\n\t\tfacts: ["); for (Fact f : this.facts) { s.append("\n\t\t\t"); - s.append(symbol_table.print_fact(f)); + s.append(local_symbols.print_fact(f)); } s.append("\n\t\t]\n\t\trules: ["); for (Rule r : this.rules) { s.append("\n\t\t\t"); - s.append(symbol_table.print_rule(r)); + s.append(local_symbols.print_rule(r)); } s.append("\n\t\t]\n\t\tchecks: ["); for (Check c : this.checks) { s.append("\n\t\t\t"); - s.append(symbol_table.print_check(c)); + s.append(local_symbols.print_check(c)); } s.append("\n\t\t]\n\t}"); @@ -184,7 +199,7 @@ int getSchemaVersion() { } } - if(containsScopes || containsCheckAll || containsV4) { + if(containsScopes || containsCheckAll || containsV4 || this.externalKey.isDefined()) { return SerializedBiscuit.MAX_SCHEMA_VERSION; } else { return SerializedBiscuit.MIN_SCHEMA_VERSION; @@ -321,7 +336,6 @@ public boolean equals(Object o) { Block block = (Block) o; - if (version != block.version) return false; if (!Objects.equals(symbols, block.symbols)) return false; if (!Objects.equals(context, block.context)) return false; if (!Objects.equals(facts, block.facts)) return false; @@ -342,7 +356,6 @@ public int hashCode() { result = 31 * result + (scopes != null ? scopes.hashCode() : 0); result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0); result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0); - result = 31 * result + (int) (version ^ (version >>> 32)); return result; } @@ -357,7 +370,6 @@ public String toString() { ", scopes=" + scopes + ", publicKeys=" + publicKeys + ", externalKey=" + externalKey + - ", version=" + version + '}'; } } diff --git a/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockContents.java b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockContents.java new file mode 100644 index 00000000..812589cf --- /dev/null +++ b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockContents.java @@ -0,0 +1,85 @@ +package org.biscuitsec.biscuit.token; + +import biscuit.format.schema.Schema; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import org.biscuitsec.biscuit.crypto.PublicKey; +import org.biscuitsec.biscuit.error.Error; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class ThirdPartyBlockContents { + byte[] payload; + byte[] signature; + PublicKey publicKey; + + ThirdPartyBlockContents(byte[] payload, byte[] signature, PublicKey publicKey) { + this.payload = payload; + this.signature = signature; + this.publicKey = publicKey; + } + + public Schema.ThirdPartyBlockContents serialize() throws Error.FormatError.SerializationError { + Schema.ThirdPartyBlockContents.Builder b = Schema.ThirdPartyBlockContents.newBuilder(); + b.setPayload(ByteString.copyFrom(this.payload)); + b.setExternalSignature(b.getExternalSignatureBuilder() + .setSignature(ByteString.copyFrom(this.signature)) + .setPublicKey(this.publicKey.serialize()) + .build()); + + return b.build(); + } + + static public ThirdPartyBlockContents deserialize(Schema.ThirdPartyBlockContents b) throws Error.FormatError.DeserializationError { + byte[] payload = b.getPayload().toByteArray(); + byte[] signature = b.getExternalSignature().getSignature().toByteArray(); + PublicKey publicKey = PublicKey.deserialize(b.getExternalSignature().getPublicKey()); + + return new ThirdPartyBlockContents(payload, signature, publicKey); + } + + static public ThirdPartyBlockContents fromBytes(byte[] slice) throws InvalidProtocolBufferException, Error.FormatError.DeserializationError { + return ThirdPartyBlockContents.deserialize(Schema.ThirdPartyBlockContents.parseFrom(slice)); + } + + public byte[] toBytes() throws IOException, Error.FormatError.SerializationError { + Schema.ThirdPartyBlockContents b = this.serialize(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + b.writeTo(stream); + return stream.toByteArray(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ThirdPartyBlockContents that = (ThirdPartyBlockContents) o; + + if (!Arrays.equals(payload, that.payload)) return false; + if (!Arrays.equals(signature, that.signature)) return false; + return Objects.equals(publicKey, that.publicKey); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(payload); + result = 31 * result + Arrays.hashCode(signature); + result = 31 * result + (publicKey != null ? publicKey.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ThirdPartyBlockContents{" + + "payload=" + Arrays.toString(payload) + + ", signature=" + Arrays.toString(signature) + + ", publicKey=" + publicKey + + '}'; + } +} diff --git a/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java new file mode 100644 index 00000000..50b1298d --- /dev/null +++ b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java @@ -0,0 +1,122 @@ +package org.biscuitsec.biscuit.token; + +import biscuit.format.schema.Schema; +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.PublicKey; +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 { + PublicKey previousKey; + List publicKeys; + + ThirdPartyBlockRequest(PublicKey previousKey, List publicKeys) { + this.previousKey = previousKey; + this.publicKeys = publicKeys; + } + + public Either createBlock(KeyPair keyPair, Block blockBuilder) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + SymbolTable symbols = new SymbolTable(); + for(PublicKey pk: this.publicKeys) { + symbols.insert(pk); + } + + org.biscuitsec.biscuit.token.Block block = blockBuilder.build(symbols, Option.some(keyPair.public_key())); + + Either res = block.to_bytes(); + if(res.isLeft()) { + return Either.left(res.getLeft()); + } + + byte[] serializedBlock = res.get(); + + 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(); + + return Either.right(new ThirdPartyBlockContents(serializedBlock, signature, publicKey)); + } + + public Schema.ThirdPartyBlockRequest serialize() throws Error.FormatError.SerializationError { + Schema.ThirdPartyBlockRequest.Builder b = Schema.ThirdPartyBlockRequest.newBuilder(); + b.setPreviousKey(this.previousKey.serialize()); + + for(PublicKey pk: this.publicKeys) { + b.addPublicKeys(pk.serialize()); + } + + return b.build(); + } + + static public ThirdPartyBlockRequest deserialize(Schema.ThirdPartyBlockRequest b) throws Error.FormatError.DeserializationError { + PublicKey previousKey = PublicKey.deserialize(b.getPreviousKey()); + List publicKeys = new ArrayList<>(); + for(Schema.PublicKey pk: b.getPublicKeysList()) { + publicKeys.add(PublicKey.deserialize(pk)); + } + return new ThirdPartyBlockRequest(previousKey, publicKeys); + } + + static public ThirdPartyBlockRequest fromBytes(byte[] slice) throws InvalidProtocolBufferException, Error.FormatError.DeserializationError { + return ThirdPartyBlockRequest.deserialize(Schema.ThirdPartyBlockRequest.parseFrom(slice)); + } + + public byte[] toBytes() throws IOException, Error.FormatError.SerializationError { + Schema.ThirdPartyBlockRequest b = this.serialize(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + b.writeTo(stream); + return stream.toByteArray(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ThirdPartyBlockRequest that = (ThirdPartyBlockRequest) o; + + if (!Objects.equals(previousKey, that.previousKey)) return false; + return Objects.equals(publicKeys, that.publicKeys); + } + + @Override + public int hashCode() { + int result = previousKey != null ? previousKey.hashCode() : 0; + result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ThirdPartyBlockRequest{" + + "previousKey=" + previousKey + + ", publicKeys=" + publicKeys + + '}'; + } +} + diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java index 06592a91..07ac2e9a 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java @@ -1,9 +1,12 @@ package org.biscuitsec.biscuit.token; +import biscuit.format.schema.Schema; +import net.i2p.crypto.eddsa.EdDSAEngine; import org.biscuitsec.biscuit.crypto.KeyDelegate; import org.biscuitsec.biscuit.crypto.KeyPair; import org.biscuitsec.biscuit.crypto.PublicKey; import org.biscuitsec.biscuit.error.Error; +import org.biscuitsec.biscuit.token.format.ExternalSignature; import org.biscuitsec.biscuit.token.format.SerializedBiscuit; import io.vavr.Tuple3; import io.vavr.control.Either; @@ -11,10 +14,9 @@ import org.biscuitsec.biscuit.datalog.Check; import org.biscuitsec.biscuit.datalog.SymbolTable; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.SignatureException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.*; import java.util.*; import java.util.stream.Collectors; @@ -129,7 +131,7 @@ public String serialize_b64url() throws Error.FormatError.SerializationError { * @return */ public org.biscuitsec.biscuit.token.builder.Block create_block() { - return new org.biscuitsec.biscuit.token.builder.Block(1 + this.blocks.size()); + return new org.biscuitsec.biscuit.token.builder.Block(); } /** @@ -140,7 +142,7 @@ public org.biscuitsec.biscuit.token.builder.Block create_block() { */ public UnverifiedBiscuit attenuate(org.biscuitsec.biscuit.token.builder.Block block) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error { SecureRandom rng = new SecureRandom(); - KeyPair keypair = new KeyPair(rng); + KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng); SymbolTable builderSymbols = new SymbolTable(this.symbols); return attenuate(rng, keypair, block.build(builderSymbols)); } @@ -165,7 +167,7 @@ public UnverifiedBiscuit attenuate(final SecureRandom rng, final KeyPair keypair throw new Error.SymbolTableOverlap(); } - Either containerRes = copiedBiscuit.serializedBiscuit.append(keypair, block); + Either containerRes = copiedBiscuit.serializedBiscuit.append(keypair, block, Option.none()); if (containerRes.isLeft()) { throw containerRes.getLeft(); } @@ -232,6 +234,95 @@ public Option root_key_id() { return this.root_key_id; } + /** + * Generates a third party block request from a token + */ + public ThirdPartyBlockRequest thirdPartyRequest() { + PublicKey previousKey; + if(this.serializedBiscuit.blocks.isEmpty()) { + previousKey = this.serializedBiscuit.authority.key; + } else { + previousKey = this.serializedBiscuit.blocks.get(this.serializedBiscuit.blocks.size() - 1).key; + } + + List publicKeys = new ArrayList<>(this.symbols.publicKeys()); + return new ThirdPartyBlockRequest(previousKey, publicKeys); + } + + + /** + * Generates a third party block request from a token + */ + 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)) { + throw new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"); + } + + Either res = Block.from_bytes(blockResponse.payload, Option.some(externalKey)); + if(res.isLeft()) { + throw res.getLeft(); + } + + Block block = res.get(); + + ExternalSignature externalSignature = new ExternalSignature(externalKey, blockResponse.signature); + + UnverifiedBiscuit copiedBiscuit = this.copy(); + + Either containerRes = copiedBiscuit.serializedBiscuit.append(nextKeyPair, block, Option.some(externalSignature)); + if (containerRes.isLeft()) { + throw containerRes.getLeft(); + } + + SerializedBiscuit container = containerRes.get(); + + SymbolTable symbols = new SymbolTable(copiedBiscuit.symbols); + + ArrayList blocks = new ArrayList<>(); + for (Block b : copiedBiscuit.blocks) { + blocks.add(b); + } + blocks.add(block); + + for(PublicKey pk: block.publicKeys) { + symbols.insert(pk); + } + + long pkIndex = symbols.insert(externalKey); + + HashMap> publicKeyToBlockId = new HashMap<>(); + publicKeyToBlockId.putAll(this.publicKeyToBlockId); + if(publicKeyToBlockId.containsKey(pkIndex)) { + publicKeyToBlockId.get(pkIndex).add((long)this.blocks.size()+1); + } else { + List list = new ArrayList<>(); + list.add((long)this.blocks.size()+1); + publicKeyToBlockId.put(pkIndex, list); + } + + List revocation_ids = container.revocation_identifiers(); + + return new UnverifiedBiscuit(copiedBiscuit.authority, blocks, symbols, container, publicKeyToBlockId, revocation_ids); + } /** * Prints a token's content diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/Block.java b/src/main/java/org/biscuitsec/biscuit/token/builder/Block.java index dde4a247..972a694c 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/Block.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/Block.java @@ -18,15 +18,13 @@ import java.util.*; public class Block { - long index; String context; List facts; List rules; List checks; List scopes; - public Block(long index) { - this.index = index; + public Block() { this.context = ""; this.facts = new ArrayList<>(); this.rules = new ArrayList<>(); @@ -138,7 +136,6 @@ public org.biscuitsec.biscuit.token.Block build(SymbolTable symbols, final Optio block_symbols.add(symbols.symbols.get(i)); } - List publicKeys = new ArrayList<>(); for (int i = publicKeyStart; i < symbols.currentPublicKeyOffset(); i++) { publicKeys.add(symbols.publicKeys().get(i)); @@ -155,7 +152,6 @@ public boolean equals(Object o) { Block block = (Block) o; - if (index != block.index) return false; if (!Objects.equals(context, block.context)) return false; if (!Objects.equals(facts, block.facts)) return false; if (!Objects.equals(rules, block.rules)) return false; @@ -165,8 +161,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = (int) (index ^ (index >>> 32)); - result = 31 * result + (context != null ? context.hashCode() : 0); + int result = context != null ? context.hashCode() : 0; result = 31 * result + (facts != null ? facts.hashCode() : 0); result = 31 * result + (rules != null ? rules.hashCode() : 0); result = 31 * result + (checks != null ? checks.hashCode() : 0); diff --git a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java index 0fe529f8..1e85eec8 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java +++ b/src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java @@ -28,7 +28,7 @@ public class Parser { * @return Either>, Block> */ public static Either>, Block> datalog(long index, String s) { - Block blockBuilder = new Block(index); + Block blockBuilder = new Block(); // empty block code if (s.isEmpty()) { 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 a68419e6..9f9609c1 100644 --- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java +++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java @@ -242,7 +242,7 @@ static public Either make(final org.biscui algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); + Signature sgr = KeyPair.generateSignature(root.public_key().algorithm); sgr.initSign(root.private_key); sgr.update(block); sgr.update(algo_buf); @@ -259,7 +259,7 @@ static public Either make(final org.biscui } public Either append(final org.biscuitsec.biscuit.crypto.KeyPair next, - final Block newBlock) { + final Block newBlock, Option externalSignature) { if (this.proof.secretKey.isEmpty()) { return Left(new Error.FormatError.SerializationError("the token is sealed")); } @@ -278,51 +278,14 @@ public Either append(final org.biscuitsec. Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); sgr.initSign(this.proof.secretKey.get().private_key); sgr.update(block); - sgr.update(algo_buf); - sgr.update(next_key.toBytes()); - byte[] signature = sgr.sign(); - - SignedBlock signedBlock = new SignedBlock(block, next_key, signature, Option.none()); - - ArrayList blocks = new ArrayList<>(); - for (SignedBlock bl : this.blocks) { - blocks.add(bl); + if(externalSignature.isDefined()) { + sgr.update(externalSignature.get().signature); } - blocks.add(signedBlock); - - Proof proof = new Proof(next); - - return Right(new SerializedBiscuit(this.authority, blocks, proof, root_key_id)); - } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) { - return Left(new Error.FormatError.SerializationError(e.toString())); - } - } - - public Either appendThirdParty(final org.biscuitsec.biscuit.crypto.KeyPair next, - final Block newBlock) { - /*if (this.proof.secretKey.isEmpty()) { - return Left(new Error.FormatError.SerializationError("the token is sealed")); - } - - Schema.Block b = newBlock.serialize(); - try { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - b.writeTo(stream); - - byte[] block = stream.toByteArray(); - 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 = new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm())); - sgr.initSign(this.proof.secretKey.get().private_key); - sgr.update(block); sgr.update(algo_buf); sgr.update(next_key.toBytes()); byte[] signature = sgr.sign(); - SignedBlock signedBlock = new SignedBlock(block, next_key, signature); + SignedBlock signedBlock = new SignedBlock(block, next_key, signature, externalSignature); ArrayList blocks = new ArrayList<>(); for (SignedBlock bl : this.blocks) { @@ -335,8 +298,7 @@ public Either appendThirdParty(final org.b return Right(new SerializedBiscuit(this.authority, blocks, proof, root_key_id)); } catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) { return Left(new Error.FormatError.SerializationError(e.toString())); - }*/ - throw new RuntimeException("todo"); + } } public Either verify(org.biscuitsec.biscuit.crypto.PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { @@ -424,7 +386,7 @@ static Either verifyBlockSignatu algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber())); algo_buf.flip(); - Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); + Signature sgr = KeyPair.generateSignature(publicKey.algorithm); sgr.initVerify(publicKey.key); sgr.update(block); @@ -442,11 +404,13 @@ static Either verifyBlockSignatu algo_buf2.putInt(Integer.valueOf(publicKey.algorithm.getNumber())); algo_buf2.flip(); - Signature sgr2 = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm())); + Signature sgr2 = new EdDSAEngine(MessageDigest.getInstance(KeyPair.ed25519.getHashAlgorithm())); sgr2.initVerify(signedBlock.externalSignature.get().key.key); sgr2.update(block); sgr2.update(algo_buf2); sgr2.update(publicKey.toBytes()); + Either authRes = Block.from_bytes(block, Option.none()); + if (!sgr2.verify(signedBlock.externalSignature.get().signature)) { return Left(new Error.FormatError.Signature.InvalidSignature("external signature error: Verification equation was not satisfied")); } diff --git a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java index b981c58f..dbb8ff06 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/BuilderTest.java @@ -28,7 +28,7 @@ public void testBuild() throws Error.Language, Error.SymbolTableOverlap, Error.F KeyPair root = new KeyPair(rng); SymbolTable symbols = Biscuit.default_symbol_table(); - Block authority_builder = new Block(0); + Block authority_builder = new Block(); authority_builder.add_fact(Utils.fact("revocation_id", Arrays.asList(Utils.date(Date.from(Instant.now()))))); authority_builder.add_fact(Utils.fact("right", Arrays.asList(Utils.s("admin")))); authority_builder.add_rule(Utils.constrained_rule("right", diff --git a/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java b/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java index fe7504a6..38ecbe3b 100644 --- a/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java +++ b/src/test/java/org/biscuitsec/biscuit/builder/parser/ParserTest.java @@ -434,7 +434,7 @@ void testDatalogSucceeds() throws org.biscuitsec.biscuit.error.Error.Parser { Either>, Block> output = Parser.datalog(1, toParse); assertTrue(output.isRight()); - Block validBlock = new Block(1); + Block validBlock = new Block(); validBlock.add_fact(l1); validBlock.add_fact(l2); validBlock.add_rule(l3); @@ -455,7 +455,7 @@ void testDatalogSucceedsArrays() throws org.biscuitsec.biscuit.error.Error.Parse Either>, Block> output = Parser.datalog(1, toParse); assertTrue(output.isRight()); - Block validBlock = new Block(1); + Block validBlock = new Block(); validBlock.add_check(l1); output.forEach(block -> @@ -473,7 +473,7 @@ void testDatalogSucceedsArraysContains() throws org.biscuitsec.biscuit.error.Err Either>, Block> output = Parser.datalog(1, toParse); assertTrue(output.isRight()); - Block validBlock = new Block(1); + Block validBlock = new Block(); validBlock.add_check(l1); output.forEach(block -> diff --git a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java index 17ac83db..00a24719 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/BiscuitTest.java @@ -38,7 +38,7 @@ public void testBasic() throws NoSuchAlgorithmException, SignatureException, Inv KeyPair root = new KeyPair(rng); - Block authority_builder = new Block(0); + Block authority_builder = new Block(); authority_builder.add_fact(fact("right", Arrays.asList(s("file1"), s("read")))); authority_builder.add_fact(fact("right", Arrays.asList(s("file2"), s("read")))); @@ -226,7 +226,7 @@ public void testMultipleAttenuation() throws NoSuchAlgorithmException, Signature SecureRandom rng = new SecureRandom(); KeyPair root = new KeyPair(rng); - Block authority_builder = new Block(0); + Block authority_builder = new Block(); Date date = Date.from(Instant.now()); authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(date)))); @@ -369,7 +369,7 @@ public void testBasicWithNamespaces() throws NoSuchAlgorithmException, Signature KeyPair root = new KeyPair(rng); - Block authority_builder = new Block(0); + Block authority_builder = new Block(); authority_builder.add_fact(fact("namespace:right", Arrays.asList(s("file1"), s("read")))); authority_builder.add_fact(fact("namespace:right", Arrays.asList(s("file1"), s("write")))); @@ -616,7 +616,7 @@ public void testRootKeyId() throws NoSuchAlgorithmException, SignatureException, KeyPair root = new KeyPair(rng); - Block authority_builder = new Block(0); + Block authority_builder = new Block(); authority_builder.add_fact(fact("right", Arrays.asList(s("file1"), s("read")))); authority_builder.add_fact(fact("right", Arrays.asList(s("file2"), s("read")))); diff --git a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java index 30547d75..a7dffc71 100644 --- a/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java +++ b/src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java @@ -18,6 +18,7 @@ import org.biscuitsec.biscuit.token.builder.Expression; import org.biscuitsec.biscuit.token.builder.parser.ExpressionParser; import org.biscuitsec.biscuit.token.builder.parser.Parser; +import org.biscuitsec.biscuit.token.format.SerializedBiscuit; import org.biscuitsec.biscuit.token.format.SignedBlock; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; @@ -78,14 +79,24 @@ void compareBlock(SymbolTable baseSymbols, long sampleBlockIndex, Block sampleBl sampleSymbols = new SymbolTable(baseSymbols); } else { sampleSymbols = new SymbolTable(); + + for(PublicKey pk: baseSymbols.publicKeys()) { + sampleSymbols.insert(pk); + } } org.biscuitsec.biscuit.token.Block generatedSampleBlock = outputSample.get().build(sampleSymbols); - System.out.println(generatedSampleBlock.symbols.symbols); - System.out.println(block.symbols.symbols); - System.out.println(sampleSymbols.symbols); + if(!block.externalKey.isDefined()) { generatedSampleBlock.symbols.symbols.forEach(baseSymbols::add); + } else { + generatedSampleBlock.setExternalKey(block.externalKey.get()); + generatedSampleBlock.version = SerializedBiscuit.MAX_SCHEMA_VERSION; + baseSymbols.insert(block.externalKey.get()); + } + + for(PublicKey pk: generatedSampleBlock.publicKeys) { + baseSymbols.insert(pk); } System.out.println(baseSymbols.symbols); diff --git a/src/test/java/org/biscuitsec/biscuit/token/ThirdPartyTest.java b/src/test/java/org/biscuitsec/biscuit/token/ThirdPartyTest.java new file mode 100644 index 00000000..aca09add --- /dev/null +++ b/src/test/java/org/biscuitsec/biscuit/token/ThirdPartyTest.java @@ -0,0 +1,220 @@ +package org.biscuitsec.biscuit.token; + +import org.biscuitsec.biscuit.crypto.KeyPair; +import org.biscuitsec.biscuit.datalog.RunLimits; +import org.biscuitsec.biscuit.error.Error; +import org.biscuitsec.biscuit.error.FailedCheck; +import org.biscuitsec.biscuit.error.LogicError; +import org.biscuitsec.biscuit.token.builder.Block; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.time.Duration; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ThirdPartyTest { + @Test + public void testRoundTrip() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error, IOException { + byte[] seed = {0, 0, 0, 0}; + SecureRandom rng = new SecureRandom(seed); + + System.out.println("preparing the authority block"); + + KeyPair root = new KeyPair(rng); + KeyPair external = new KeyPair(rng); + System.out.println("external: ed25519/"+external.public_key().toHex()); + + Block authority_builder = new Block(); + authority_builder.add_fact("right(\"read\")"); + authority_builder.add_check("check if group(\"admin\") trusting ed25519/"+external.public_key().toHex()); + + Biscuit b1 = Biscuit.make(rng, root, authority_builder.build()); + ThirdPartyBlockRequest request = b1.thirdPartyRequest(); + byte[] reqb = request.toBytes(); + ThirdPartyBlockRequest reqdeser = ThirdPartyBlockRequest.fromBytes(reqb); + assertEquals(request, reqdeser); + + Block builder = new Block(); + builder.add_fact("group(\"admin\")"); + builder.add_check("check if resource(\"file1\")"); + + ThirdPartyBlockContents blockResponse = request.createBlock(external, builder).get(); + byte[] resb = blockResponse.toBytes(); + ThirdPartyBlockContents resdeser = ThirdPartyBlockContents.fromBytes(resb); + assertEquals(blockResponse, resdeser); + + Biscuit b2 = b1.appendThirdPartyBlock(external.public_key(), blockResponse); + + byte[] data = b2.serialize(); + Biscuit deser = Biscuit.from_bytes(data, root.public_key()); + assertEquals(b2.print(), deser.print()); + + System.out.println("will check the token for resource=file1"); + Authorizer authorizer = deser.authorizer(); + authorizer.add_fact("resource(\"file1\")"); + authorizer.add_policy("allow if true"); + authorizer.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + + System.out.println("will check the token for resource=file2"); + Authorizer authorizer2 = deser.authorizer(); + authorizer2.add_fact("resource(\"file2\")"); + authorizer2.add_policy("allow if true"); + + try { + authorizer2.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + } catch (Error e) { + System.out.println(e); + assertEquals( + new Error.FailedLogic(new LogicError.Unauthorized(new LogicError.MatchedPolicy.Allow(0), Arrays.asList( + new FailedCheck.FailedBlock(1, 0, "check if resource(\"file1\")") + ))), + e); + } + } + + @Test + public void testPublicKeyInterning() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error { + byte[] seed = {0, 0, 0, 0}; + SecureRandom rng = new SecureRandom(seed); + + System.out.println("preparing the authority block"); + + KeyPair root = new KeyPair(rng); + KeyPair external1 = new KeyPair(rng); + KeyPair external2 = new KeyPair(rng); + KeyPair external3 = new KeyPair(rng); + //System.out.println("external: ed25519/"+external.public_key().toHex()); + + Block authority_builder = new Block(); + authority_builder.add_fact("right(\"read\")"); + authority_builder.add_check("check if first(\"admin\") trusting ed25519/"+external1.public_key().toHex()); + + org.biscuitsec.biscuit.token.Block authority_block = authority_builder.build(); + System.out.println(authority_block); + Biscuit b1 = Biscuit.make(rng, root, authority_block); + System.out.println("TOKEN: "+b1.print()); + + ThirdPartyBlockRequest request1 = b1.thirdPartyRequest(); + Block builder = new Block(); + builder.add_fact("first(\"admin\")"); + builder.add_fact("second(\"A\")"); + builder.add_check("check if third(3) trusting ed25519/"+external2.public_key().toHex()); + ThirdPartyBlockContents blockResponse = request1.createBlock(external1, builder).get(); + Biscuit b2 = b1.appendThirdPartyBlock(external1.public_key(), blockResponse); + byte[] data = b2.serialize(); + Biscuit deser2 = Biscuit.from_bytes(data, root.public_key()); + assertEquals(b2.print(), deser2.print()); + System.out.println("TOKEN: "+deser2.print()); + + ThirdPartyBlockRequest request2 = deser2.thirdPartyRequest(); + Block builder2 = new Block(); + builder2.add_fact("third(3)"); + builder2.add_check("check if fourth(1) trusting ed25519/"+external3.public_key().toHex()+", ed25519/"+external1.public_key().toHex()); + ThirdPartyBlockContents blockResponse2 = request2.createBlock(external2, builder2).get(); + Biscuit b3 = deser2.appendThirdPartyBlock(external2.public_key(), blockResponse2); + byte[] data2 = b3.serialize(); + Biscuit deser3 = Biscuit.from_bytes(data2, root.public_key()); + assertEquals(b3.print(), deser3.print()); + System.out.println("TOKEN: "+deser3.print()); + + + ThirdPartyBlockRequest request3 = deser3.thirdPartyRequest(); + Block builder3 = new Block(); + builder3.add_fact("fourth(1)"); + builder3.add_check("check if resource(\"file1\")"); + ThirdPartyBlockContents blockResponse3 = request3.createBlock(external1, builder3).get(); + Biscuit b4 = deser3.appendThirdPartyBlock(external1.public_key(), blockResponse3); + byte[] data3 = b4.serialize(); + Biscuit deser4 = Biscuit.from_bytes(data3, root.public_key()); + assertEquals(b4.print(), deser4.print()); + System.out.println("TOKEN: "+deser4.print()); + + + System.out.println("will check the token for resource=file1"); + Authorizer authorizer = deser4.authorizer(); + authorizer.add_fact("resource(\"file1\")"); + authorizer.add_policy("allow if true"); + System.out.println("Authorizer world:\n"+authorizer.print_world()); + authorizer.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + + System.out.println("will check the token for resource=file2"); + Authorizer authorizer2 = deser4.authorizer(); + authorizer2.add_fact("resource(\"file2\")"); + authorizer2.add_policy("allow if true"); + System.out.println("Authorizer world 2:\n"+authorizer2.print_world()); + + try { + authorizer2.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + } catch (Error e) { + System.out.println(e); + assertEquals( + new Error.FailedLogic(new LogicError.Unauthorized(new LogicError.MatchedPolicy.Allow(0), Arrays.asList( + new FailedCheck.FailedBlock(3, 0, "check if resource(\"file1\")") + ))), + e); + } + } + + @Test + public void testReusedSymbols() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, CloneNotSupportedException, Error { + byte[] seed = {0, 0, 0, 0}; + SecureRandom rng = new SecureRandom(seed); + + System.out.println("preparing the authority block"); + + KeyPair root = new KeyPair(rng); + KeyPair external = new KeyPair(rng); + System.out.println("external: ed25519/"+external.public_key().toHex()); + + Block authority_builder = new Block(); + authority_builder.add_fact("right(\"read\")"); + authority_builder.add_check("check if group(\"admin\") trusting ed25519/"+external.public_key().toHex()); + + Biscuit b1 = Biscuit.make(rng, root, authority_builder.build()); + ThirdPartyBlockRequest request = b1.thirdPartyRequest(); + Block builder = new Block(); + builder.add_fact("group(\"admin\")"); + builder.add_fact("resource(\"file2\")"); + builder.add_check("check if resource(\"file1\")"); + builder.add_check("check if right(\"read\")"); + + ThirdPartyBlockContents blockResponse = request.createBlock(external, builder).get(); + Biscuit b2 = b1.appendThirdPartyBlock(external.public_key(), blockResponse); + + byte[] data = b2.serialize(); + Biscuit deser = Biscuit.from_bytes(data, root.public_key()); + assertEquals(b2.print(), deser.print()); + + System.out.println("will check the token for resource=file1"); + Authorizer authorizer = deser.authorizer(); + authorizer.add_fact("resource(\"file1\")"); + authorizer.add_policy("allow if true"); + authorizer.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + System.out.println("Authorizer world:\n"+authorizer.print_world()); + + + System.out.println("will check the token for resource=file2"); + Authorizer authorizer2 = deser.authorizer(); + authorizer2.add_fact("resource(\"file2\")"); + authorizer2.add_policy("allow if true"); + + try { + authorizer2.authorize(new RunLimits(500, 100, Duration.ofMillis(500))); + } catch (Error e) { + System.out.println(e); + assertEquals( + new Error.FailedLogic(new LogicError.Unauthorized(new LogicError.MatchedPolicy.Allow(0), Arrays.asList( + new FailedCheck.FailedBlock(1, 0, "check if resource(\"file1\")") + ))), + e); + } + } +} +