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