Skip to content

Commit 6878e82

Browse files
committed
verify third party block signatures
1 parent c6d27a1 commit 6878e82

File tree

13 files changed

+341
-182
lines changed

13 files changed

+341
-182
lines changed

src/main/java/com/clevercloud/biscuit/crypto/PublicKey.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22

33
import biscuit.format.schema.Schema;
44
import biscuit.format.schema.Schema.PublicKey.Algorithm;
5+
import com.clevercloud.biscuit.datalog.Scope;
6+
import com.clevercloud.biscuit.error.Error;
57
import com.clevercloud.biscuit.token.builder.Utils;
8+
import com.google.protobuf.ByteString;
9+
import io.vavr.control.Either;
610
import net.i2p.crypto.eddsa.EdDSAPublicKey;
711
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
812

913
import static com.clevercloud.biscuit.crypto.KeyPair.ed25519;
14+
import static io.vavr.API.Left;
15+
import static io.vavr.API.Right;
1016

1117
public class PublicKey {
1218

@@ -41,6 +47,22 @@ public PublicKey(Algorithm algorithm, String hex) {
4147
this.algorithm = algorithm;
4248
}
4349

50+
public Schema.PublicKey serialize() {
51+
Schema.PublicKey.Builder publicKey = Schema.PublicKey.newBuilder();
52+
publicKey.setKey(ByteString.copyFrom(this.toBytes()));
53+
publicKey.setAlgorithm(this.algorithm);
54+
return publicKey.build();
55+
}
56+
57+
static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError.DeserializationError {
58+
if(!pk.hasAlgorithm() || !pk.hasKey() || pk.getAlgorithm() != Algorithm.Ed25519) {
59+
throw new Error.FormatError.DeserializationError("Invalid " +
60+
"public key");
61+
}
62+
63+
return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray());
64+
}
65+
4466
@Override
4567
public boolean equals(Object o) {
4668
if (this == o) return true;

src/main/java/com/clevercloud/biscuit/datalog/FactSet.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public FactSet(Origin o, HashSet<Fact> factSet) {
1717
facts.put(o, factSet);
1818
}
1919

20+
public HashMap<Origin, HashSet<Fact>> facts() {
21+
return this.facts;
22+
}
23+
2024
public void add(Origin origin, Fact fact) {
2125
if(!facts.containsKey(origin)) {
2226
facts.put(origin, new HashSet<>());

src/main/java/com/clevercloud/biscuit/datalog/SymbolTable.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,33 @@ public String print_rule_body(final Rule r) {
173173
}
174174
res += String.join(", ", expressions);
175175
}
176+
177+
if(!r.scopes().isEmpty()) {
178+
res += " trusting ";
179+
final List<String> scopes = r.scopes().stream().map((s) -> this.print_scope(s)).collect(Collectors.toList());
180+
res += String.join(", ", scopes);
181+
}
176182
return res;
177183
}
178184

179185
public String print_expression(final Expression e) {
180186
return e.print(this).get();
181187
}
182188

189+
public String print_scope(final Scope scope) {
190+
switch(scope.kind) {
191+
case Authority:
192+
return "authority";
193+
case Previous:
194+
return "previous";
195+
case PublicKey:
196+
Option<PublicKey> pk = this.get_pk((int) scope.publicKey);
197+
if(pk.isDefined()) {
198+
return pk.toString();
199+
}
200+
}
201+
return "?";
202+
}
183203

184204
public String print_predicate(final Predicate p) {
185205
List<String> ids = p.terms().stream().map((t) -> {

src/main/java/com/clevercloud/biscuit/datalog/World.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,13 @@ public String print(SymbolTable symbol_table) {
138138
StringBuilder s = new StringBuilder();
139139

140140
s.append("World {\n\t\tfacts: [");
141-
for (Iterator<Fact> it = this.facts.stream().iterator(); it.hasNext(); ) {
142-
Fact f = it.next();
143-
s.append("\n\t\t\t");
144-
s.append(symbol_table.print_fact(f));
145-
}
141+
for(Map.Entry<Origin, HashSet<Fact>> entry: this.facts.facts().entrySet()) {
142+
s.append("\n\t\t\t"+entry.getKey()+":");
143+
for(Fact f: entry.getValue()) {
144+
s.append("\n\t\t\t\t");
145+
s.append(symbol_table.print_fact(f));
146+
}
147+
}
146148

147149
s.append("\n\t\t]\n\t\trules: [");
148150
for (Iterator<Rule> it = this.rules.stream().iterator(); it.hasNext(); ) {

src/main/java/com/clevercloud/biscuit/token/Authorizer.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,15 @@ public Long authorize(RunLimits limits) throws Error {
557557

558558
public String print_world() {
559559
//FIXME
560-
final List<String> facts = this.world.facts().stream().map((f) -> this.symbols.print_fact(f)).collect(Collectors.toList());
560+
StringBuilder facts = new StringBuilder();
561+
for(Map.Entry<Origin, HashSet<com.clevercloud.biscuit.datalog.Fact>> entry: this.world.facts().facts().entrySet()) {
562+
facts.append("\n\t\t"+entry.getKey()+":");
563+
for(com.clevercloud.biscuit.datalog.Fact f: entry.getValue()) {
564+
facts.append("\n\t\t\t");
565+
facts.append(this.symbols.print_fact(f));
566+
}
567+
}
568+
//final List<String> facts = this.world.facts().facts().entrySet().stream().map((f) -> this.symbols.print_fact(f)).collect(Collectors.toList());
561569
final List<String> rules = this.world.rules().stream().map((r) -> this.symbols.print_rule(r)).collect(Collectors.toList());
562570

563571
List<String> checks = new ArrayList<>();
@@ -580,8 +588,9 @@ public String print_world() {
580588
}
581589
}
582590

583-
return "World {\n\tfacts: [\n\t\t" +
584-
String.join(",\n\t\t", facts) +
591+
return "World {\n\tfacts: [" +
592+
facts.toString() +
593+
//String.join(",\n\t\t", facts) +
585594
"\n\t],\n\trules: [\n\t\t" +
586595
String.join(",\n\t\t", rules) +
587596
"\n\t],\n\tchecks: [\n\t\t" +

src/main/java/com/clevercloud/biscuit/token/Biscuit.java

Lines changed: 20 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@
77
import com.clevercloud.biscuit.error.Error;
88
import com.clevercloud.biscuit.token.format.SerializedBiscuit;
99
import com.clevercloud.biscuit.token.format.SignedBlock;
10+
import io.vavr.Tuple3;
1011
import io.vavr.control.Either;
1112
import io.vavr.control.Option;
1213

1314
import java.security.InvalidKeyException;
1415
import java.security.NoSuchAlgorithmException;
1516
import java.security.SecureRandom;
1617
import java.security.SignatureException;
17-
import java.util.ArrayList;
18-
import java.util.Base64;
19-
import java.util.Collections;
20-
import java.util.List;
18+
import java.util.*;
2119

2220
/**
2321
* Biscuit auth token
@@ -122,18 +120,21 @@ static private Biscuit make(final SecureRandom rng, final KeyPair root, final Op
122120
} else {
123121
SerializedBiscuit s = container.get();
124122
List<byte[]> revocation_ids = s.revocation_identifiers();
123+
HashMap<Long, List<Long>> publicKeyToBlockId = new HashMap<>();
125124

126125
Option<SerializedBiscuit> c = Option.some(s);
127-
return new Biscuit(authority, blocks, symbols, s, revocation_ids, root_key_id);
126+
return new Biscuit(authority, blocks, symbols, s, publicKeyToBlockId, revocation_ids, root_key_id);
128127
}
129128
}
130129

131-
Biscuit(Block authority, List<Block> blocks, SymbolTable symbols, SerializedBiscuit serializedBiscuit, List<byte[]> revocation_ids) {
132-
super(authority, blocks, symbols, serializedBiscuit, revocation_ids);
130+
Biscuit(Block authority, List<Block> blocks, SymbolTable symbols, SerializedBiscuit serializedBiscuit,
131+
HashMap<Long, List<Long>> publicKeyToBlockId, List<byte[]> revocation_ids) {
132+
super(authority, blocks, symbols, serializedBiscuit, publicKeyToBlockId, revocation_ids);
133133
}
134134

135-
Biscuit(Block authority, List<Block> blocks, SymbolTable symbols, SerializedBiscuit serializedBiscuit, List<byte[]> revocation_ids, Option<Integer> root_key_id) {
136-
super(authority, blocks, symbols, serializedBiscuit, revocation_ids, root_key_id);
135+
Biscuit(Block authority, List<Block> blocks, SymbolTable symbols, SerializedBiscuit serializedBiscuit,
136+
HashMap<Long, List<Long>> publicKeyToBlockId, List<byte[]> revocation_ids, Option<Integer> root_key_id) {
137+
super(authority, blocks, symbols, serializedBiscuit, publicKeyToBlockId, revocation_ids, root_key_id);
137138
}
138139

139140
/**
@@ -260,81 +261,20 @@ static public Biscuit from_bytes_with_symbols(byte[] data, KeyDelegate delegate,
260261
return Biscuit.from_serialized_biscuit(ser, symbols);
261262
}
262263

263-
/**
264-
* Fills a Biscuit structure from a deserialized token
265-
*
266-
* @return
267-
*/
268-
@Deprecated
269-
static Biscuit from_serialize_biscuit(SerializedBiscuit ser, SymbolTable symbols) throws Error {
270-
Either<Error.FormatError, Block> authRes = Block.from_bytes(ser.authority.block);
271-
if (authRes.isLeft()) {
272-
Error e = authRes.getLeft();
273-
throw e;
274-
}
275-
Block authority = authRes.get();
276-
277-
ArrayList<Block> blocks = new ArrayList<>();
278-
for (SignedBlock bdata : ser.blocks) {
279-
Either<Error.FormatError, Block> blockRes = Block.from_bytes(bdata.block);
280-
if (blockRes.isLeft()) {
281-
Error e = blockRes.getLeft();
282-
throw e;
283-
}
284-
blocks.add(blockRes.get());
285-
}
286-
287-
for (String s : authority.symbols.symbols) {
288-
symbols.add(s);
289-
}
290-
291-
for (Block b : blocks) {
292-
for (String s : b.symbols.symbols) {
293-
symbols.add(s);
294-
}
295-
}
296-
297-
List<byte[]> revocation_ids = ser.revocation_identifiers();
298-
299-
return new Biscuit(authority, blocks, symbols, ser, revocation_ids);
300-
}
301-
302264
/**
303265
* Fills a Biscuit structure from a deserialized token
304266
*
305267
* @return
306268
*/
307269
static Biscuit from_serialized_biscuit(SerializedBiscuit ser, SymbolTable symbols) throws Error {
308-
Either<Error.FormatError, Block> authRes = Block.from_bytes(ser.authority.block);
309-
if (authRes.isLeft()) {
310-
Error e = authRes.getLeft();
311-
throw e;
312-
}
313-
Block authority = authRes.get();
314-
315-
ArrayList<Block> blocks = new ArrayList<>();
316-
for (SignedBlock bdata : ser.blocks) {
317-
Either<Error.FormatError, Block> blockRes = Block.from_bytes(bdata.block);
318-
if (blockRes.isLeft()) {
319-
Error e = blockRes.getLeft();
320-
throw e;
321-
}
322-
blocks.add(blockRes.get());
323-
}
324-
325-
for (String s : authority.symbols.symbols) {
326-
symbols.add(s);
327-
}
328-
329-
for (Block b : blocks) {
330-
for (String s : b.symbols.symbols) {
331-
symbols.add(s);
332-
}
333-
}
270+
Tuple3<Block, ArrayList<Block>, HashMap<Long, List<Long>>> t = ser.extractBlocks(symbols);
271+
Block authority = t._1;
272+
ArrayList<Block> blocks = t._2;
273+
HashMap<Long, List<Long>> publicKeyToBlockId = t._3;
334274

335275
List<byte[]> revocation_ids = ser.revocation_identifiers();
336276

337-
return new Biscuit(authority, blocks, symbols, ser, revocation_ids);
277+
return new Biscuit(authority, blocks, symbols, ser, publicKeyToBlockId, revocation_ids);
338278
}
339279

340280
/**
@@ -424,7 +364,10 @@ public Biscuit attenuate(final SecureRandom rng, final KeyPair keypair, Block bl
424364

425365
List<byte[]> revocation_ids = container.revocation_identifiers();
426366

427-
return new Biscuit(copiedBiscuit.authority, blocks, symbols, container, revocation_ids);
367+
HashMap<Long, List<Long>> publicKeyToBlockId = new HashMap<>();
368+
publicKeyToBlockId.putAll(this.publicKeyToBlockId);
369+
370+
return new Biscuit(copiedBiscuit.authority, blocks, symbols, container, publicKeyToBlockId, revocation_ids);
428371
}
429372

430373
/**
@@ -448,6 +391,6 @@ public String print() {
448391
}
449392

450393
public Biscuit copy() throws Error {
451-
return Biscuit.from_serialize_biscuit(this.serializedBiscuit, this.symbols);
394+
return Biscuit.from_serialized_biscuit(this.serializedBiscuit, this.symbols);
452395
}
453396
}

src/main/java/com/clevercloud/biscuit/token/Block.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
package com.clevercloud.biscuit.token;
22

33
import biscuit.format.schema.Schema;
4+
import com.clevercloud.biscuit.crypto.PublicKey;
45
import com.clevercloud.biscuit.error.Error;
56
import com.clevercloud.biscuit.datalog.*;
6-
import com.clevercloud.biscuit.error.FailedCheck;
7-
import com.clevercloud.biscuit.error.LogicError;
87
import com.clevercloud.biscuit.token.format.SerializedBiscuit;
98
import com.google.protobuf.InvalidProtocolBufferException;
109
import io.vavr.control.Either;
10+
import io.vavr.control.Option;
1111

1212
import java.io.ByteArrayOutputStream;
1313
import java.io.IOException;
1414
import java.util.ArrayList;
15-
import java.util.HashMap;
1615
import java.util.List;
17-
import java.util.Set;
1816

1917
import static io.vavr.API.Left;
2018
import static io.vavr.API.Right;
@@ -29,6 +27,7 @@ public class Block {
2927
final List<Rule> rules;
3028
final List<Check> checks;
3129
final List<Scope> scopes;
30+
final List<PublicKey> publicKeys;
3231
final long version;
3332

3433
/**
@@ -43,6 +42,7 @@ public Block(SymbolTable base_symbols) {
4342
this.rules = new ArrayList<>();
4443
this.checks = new ArrayList<>();
4544
this.scopes = new ArrayList<>();
45+
this.publicKeys = new ArrayList<>();
4646
this.version = SerializedBiscuit.MAX_SCHEMA_VERSION;
4747
}
4848

@@ -54,14 +54,23 @@ public Block(SymbolTable base_symbols) {
5454
* @param checks
5555
*/
5656
public Block(SymbolTable base_symbols, String context, List<Fact> facts, List<Rule> rules, List<Check> checks,
57-
List<Scope> scopes, int version) {
57+
List<Scope> scopes, List<PublicKey> publicKeys, int version) {
5858
this.symbols = base_symbols;
5959
this.context = context;
6060
this.facts = facts;
6161
this.rules = rules;
6262
this.checks = checks;
6363
this.scopes = scopes;
6464
this.version = version;
65+
this.publicKeys = publicKeys;
66+
}
67+
68+
public SymbolTable symbols() {
69+
return symbols;
70+
}
71+
72+
public List<PublicKey> publicKeys() {
73+
return publicKeys;
6574
}
6675

6776
/**
@@ -130,6 +139,10 @@ public Schema.Block serialize() {
130139
b.addScope(scope.serialize());
131140
}
132141

142+
for(PublicKey pk: this.publicKeys) {
143+
b.addPublicKeys(pk.serialize());
144+
}
145+
133146
b.setVersion(SerializedBiscuit.MAX_SCHEMA_VERSION);
134147
return b.build();
135148
}
@@ -198,14 +211,23 @@ static public Either<Error.FormatError, Block> deserialize(Schema.Block b) {
198211
}
199212
}
200213

214+
ArrayList<PublicKey> publicKeys = new ArrayList<>();
215+
for (Schema.PublicKey pk: b.getPublicKeysList()) {
216+
try {
217+
publicKeys.add(PublicKey.deserialize(pk));
218+
} catch(Error.FormatError e) {
219+
return Left(e);
220+
}
221+
}
222+
201223
SchemaVersion schemaVersion = new SchemaVersion(facts, rules, checks, scopes);
202224
Either<Error.FormatError, Void> res = schemaVersion.checkCompatibility(version);
203225
if (res.isLeft()) {
204226
Error.FormatError e = res.getLeft();
205227
return Left(e);
206228
}
207229

208-
return Right(new Block(symbols, b.getContext(), facts, rules, checks, scopes, version));
230+
return Right(new Block(symbols, b.getContext(), facts, rules, checks, scopes, publicKeys, version));
209231
}
210232

211233
/**

0 commit comments

Comments
 (0)