Skip to content

Commit d53cc2d

Browse files
authored
Merge pull request #126 from preuss-adam/apreuss/new-third-party-blocks
Add support for V1 third party blocks
2 parents 3792172 + 6ce2cc5 commit d53cc2d

17 files changed

+785
-413
lines changed

src/main/java/org/eclipse/biscuit/crypto/BlockSignatureBuffer.java

Lines changed: 178 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,192 @@
55

66
package org.eclipse.biscuit.crypto;
77

8+
import static io.vavr.API.Left;
9+
import static io.vavr.API.Right;
10+
11+
import biscuit.format.schema.Schema;
12+
import io.vavr.control.Either;
813
import java.nio.ByteBuffer;
914
import java.nio.ByteOrder;
15+
import java.nio.charset.StandardCharsets;
1016
import java.util.Optional;
17+
import java.util.stream.Stream;
18+
import org.eclipse.biscuit.error.Error;
1119
import org.eclipse.biscuit.token.format.ExternalSignature;
20+
import org.eclipse.biscuit.token.format.SerializedBiscuit;
21+
import org.eclipse.biscuit.token.format.SignedBlock;
1222

1323
public final class BlockSignatureBuffer {
14-
public static final int HEADER_SIZE = 4;
24+
public static final int THIRD_PARTY_SIGNATURE_VERSION = 1;
25+
public static final int DATALOG_3_3_SIGNATURE_VERSION = 1;
26+
public static final int NON_ED25519_SIGNATURE_VERSION = 1;
27+
28+
private static final byte[] BLOCK_VERSION =
29+
"\0BLOCK\0\0VERSION\0".getBytes(StandardCharsets.US_ASCII);
30+
private static final byte[] EXTERNAL_VERSION =
31+
"\0EXTERNAL\0\0VERSION\0".getBytes(StandardCharsets.US_ASCII);
32+
private static final byte[] PAYLOAD = "\0PAYLOAD\0".getBytes(StandardCharsets.US_ASCII);
33+
private static final byte[] ALGORITHM = "\0ALGORITHM\0".getBytes(StandardCharsets.US_ASCII);
34+
private static final byte[] NEXTKEY = "\0NEXTKEY\0".getBytes(StandardCharsets.US_ASCII);
35+
private static final byte[] PREVSIG = "\0PREVSIG\0".getBytes(StandardCharsets.US_ASCII);
36+
private static final byte[] EXTERNALSIG = "\0EXTERNALSIG\0".getBytes(StandardCharsets.US_ASCII);
1537

1638
private BlockSignatureBuffer() {}
1739

18-
public static byte[] getBufferSignature(PublicKey nextPubKey, byte[] data) {
19-
return getBufferSignature(nextPubKey, data, Optional.empty());
20-
}
21-
22-
public static byte[] getBufferSignature(
23-
PublicKey nextPubKey, byte[] data, Optional<ExternalSignature> externalSignature) {
24-
var buffer =
25-
ByteBuffer.allocate(
26-
HEADER_SIZE
27-
+ data.length
28-
+ nextPubKey.toBytes().length
29-
+ externalSignature.map((a) -> a.getSignature().length).orElse(0))
30-
.order(ByteOrder.LITTLE_ENDIAN);
31-
buffer.put(data);
32-
externalSignature.ifPresent(signature -> buffer.put(signature.getSignature()));
33-
buffer.putInt(nextPubKey.getAlgorithm().getNumber());
34-
buffer.put(nextPubKey.toBytes());
35-
buffer.flip();
36-
return buffer.array();
37-
}
38-
39-
public static byte[] getBufferSealedSignature(
40-
PublicKey nextPubKey, byte[] data, byte[] blockSignature) {
41-
var buffer =
42-
ByteBuffer.allocate(
43-
HEADER_SIZE + data.length + nextPubKey.toBytes().length + blockSignature.length)
44-
.order(ByteOrder.LITTLE_ENDIAN);
45-
buffer.put(data);
46-
buffer.putInt(nextPubKey.getAlgorithm().getNumber());
47-
buffer.put(nextPubKey.toBytes());
48-
buffer.put(blockSignature);
49-
buffer.flip();
50-
return buffer.array();
40+
public static int blockSignatureVersion(
41+
PublicKey blockKey,
42+
PublicKey nextKey,
43+
Optional<ExternalSignature> externalSignature,
44+
Optional<Long> blockVersion,
45+
Stream<Integer> previousSigVersions) {
46+
if (externalSignature.isPresent()) {
47+
return THIRD_PARTY_SIGNATURE_VERSION;
48+
}
49+
50+
if (blockVersion.isPresent() && blockVersion.get() >= SerializedBiscuit.DATALOG_3_3) {
51+
return DATALOG_3_3_SIGNATURE_VERSION;
52+
}
53+
54+
if (blockKey.getAlgorithm() != Schema.PublicKey.Algorithm.Ed25519
55+
|| nextKey.getAlgorithm() != Schema.PublicKey.Algorithm.Ed25519) {
56+
return NON_ED25519_SIGNATURE_VERSION;
57+
}
58+
59+
return previousSigVersions.mapToInt(Integer::intValue).max().orElse(0);
60+
}
61+
62+
public static Either<Error.FormatError, byte[]> generateBlockSignaturePayload(
63+
byte[] payload,
64+
PublicKey nextKey,
65+
Optional<ExternalSignature> externalSignature,
66+
Optional<byte[]> previousSignature,
67+
int version) {
68+
switch (version) {
69+
case 0:
70+
return Right(generateBlockSignaturePayloadV0(payload, nextKey, externalSignature));
71+
case 1:
72+
return Right(
73+
generateBlockSignaturePayloadV1(
74+
payload, nextKey, externalSignature, previousSignature, version));
75+
default:
76+
return Left(
77+
new Error.FormatError.DeserializationError("unsupported block version " + version));
78+
}
79+
}
80+
81+
public static byte[] generateBlockSignaturePayloadV0(
82+
byte[] payload, PublicKey nextKey, Optional<ExternalSignature> externalSignature) {
83+
var nextKeyBytes = nextKey.toBytes();
84+
var capacity = payload.length + Integer.BYTES + nextKeyBytes.length;
85+
if (externalSignature.isPresent()) {
86+
capacity += externalSignature.get().getSignature().length;
87+
}
88+
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
89+
toVerify.put(payload);
90+
if (externalSignature.isPresent()) {
91+
toVerify.put(externalSignature.get().getSignature());
92+
}
93+
toVerify.putInt(nextKey.getAlgorithm().getNumber());
94+
toVerify.put(nextKeyBytes);
95+
toVerify.flip();
96+
return toVerify.array();
97+
}
98+
99+
public static byte[] generateBlockSignaturePayloadV1(
100+
byte[] payload,
101+
PublicKey nextKey,
102+
Optional<ExternalSignature> externalSignature,
103+
Optional<byte[]> previousSignature,
104+
int version) {
105+
var nextKeyBytes = nextKey.toBytes();
106+
var capacity =
107+
BLOCK_VERSION.length
108+
+ Integer.BYTES
109+
+ PAYLOAD.length
110+
+ payload.length
111+
+ ALGORITHM.length
112+
+ Integer.BYTES
113+
+ NEXTKEY.length
114+
+ nextKeyBytes.length;
115+
if (previousSignature.isPresent()) {
116+
capacity += PREVSIG.length + previousSignature.get().length;
117+
}
118+
if (externalSignature.isPresent()) {
119+
capacity += EXTERNALSIG.length + externalSignature.get().getSignature().length;
120+
}
121+
122+
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
123+
toVerify.put(BLOCK_VERSION);
124+
toVerify.putInt(version);
125+
toVerify.put(PAYLOAD);
126+
toVerify.put(payload);
127+
toVerify.put(ALGORITHM);
128+
toVerify.putInt(nextKey.getAlgorithm().getNumber());
129+
toVerify.put(NEXTKEY);
130+
toVerify.put(nextKeyBytes);
131+
if (previousSignature.isPresent()) {
132+
toVerify.put(PREVSIG);
133+
toVerify.put(previousSignature.get());
134+
}
135+
if (externalSignature.isPresent()) {
136+
toVerify.put(EXTERNALSIG);
137+
toVerify.put(externalSignature.get().getSignature());
138+
}
139+
toVerify.flip();
140+
return toVerify.array();
141+
}
142+
143+
public static byte[] generateExternalBlockSignaturePayload(
144+
byte[] payload, PublicKey previousKey, byte[] previousSignature, int version) {
145+
if (version == 0) {
146+
return generateExternalBlockSignaturePayloadV0(payload, previousKey);
147+
} else {
148+
return generateExternalBlockSignaturePayloadV1(payload, previousSignature, version);
149+
}
150+
}
151+
152+
public static byte[] generateExternalBlockSignaturePayloadV0(
153+
byte[] payload, PublicKey previousKey) {
154+
var previousKeyBytes = previousKey.toBytes();
155+
var capacity = payload.length + Integer.BYTES + previousKeyBytes.length;
156+
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
157+
toVerify.put(payload);
158+
toVerify.putInt(previousKey.getAlgorithm().getNumber());
159+
toVerify.put(previousKey.toBytes());
160+
toVerify.flip();
161+
return toVerify.array();
162+
}
163+
164+
public static byte[] generateExternalBlockSignaturePayloadV1(
165+
byte[] payload, byte[] previousSignature, int version) {
166+
var capacity =
167+
EXTERNAL_VERSION.length
168+
+ Integer.BYTES
169+
+ PAYLOAD.length
170+
+ payload.length
171+
+ PREVSIG.length
172+
+ previousSignature.length;
173+
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
174+
toVerify.put(EXTERNAL_VERSION);
175+
toVerify.putInt(version);
176+
toVerify.put(PAYLOAD);
177+
toVerify.put(payload);
178+
toVerify.put(PREVSIG);
179+
toVerify.put(previousSignature);
180+
toVerify.flip();
181+
return toVerify.array();
182+
}
183+
184+
public static byte[] generateSealBlockSignaturePayloadV0(SignedBlock block) {
185+
var keyBytes = block.getKey().toBytes();
186+
var capacity =
187+
block.getBlock().length + Integer.BYTES + keyBytes.length + block.getSignature().length;
188+
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
189+
toVerify.put(block.getBlock());
190+
toVerify.putInt(block.getKey().getAlgorithm().getNumber());
191+
toVerify.put(keyBytes);
192+
toVerify.put(block.getSignature());
193+
toVerify.flip();
194+
return toVerify.array();
51195
}
52196
}

src/main/java/org/eclipse/biscuit/crypto/Token.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.security.NoSuchAlgorithmException;
1414
import java.security.SignatureException;
1515
import java.util.ArrayList;
16+
import java.util.Optional;
1617
import org.eclipse.biscuit.error.Error;
1718

1819
class Token {
@@ -29,7 +30,9 @@ class Token {
2930
this.keys = new ArrayList<>();
3031
this.keys.add(next.getPublicKey());
3132
this.signatures = new ArrayList<>();
32-
byte[] payload = BlockSignatureBuffer.getBufferSignature(next.getPublicKey(), message);
33+
byte[] payload =
34+
BlockSignatureBuffer.generateBlockSignaturePayloadV0(
35+
message, next.getPublicKey(), Optional.empty());
3336
byte[] signature = rootSigner.sign(payload);
3437
this.signatures.add(signature);
3538
this.next = next;
@@ -48,7 +51,9 @@ class Token {
4851

4952
Token append(KeyPair keyPair, byte[] message)
5053
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
51-
byte[] payload = BlockSignatureBuffer.getBufferSignature(keyPair.getPublicKey(), message);
54+
byte[] payload =
55+
BlockSignatureBuffer.generateBlockSignaturePayloadV0(
56+
message, keyPair.getPublicKey(), Optional.empty());
5257
byte[] signature = this.next.sign(payload);
5358

5459
Token token = new Token(this.blocks, this.keys, this.signatures, keyPair);
@@ -68,7 +73,8 @@ public Either<Error, Void> verify(PublicKey root)
6873
PublicKey nextKey = this.keys.get(i);
6974
byte[] signature = this.signatures.get(i);
7075

71-
byte[] payload = BlockSignatureBuffer.getBufferSignature(nextKey, block);
76+
byte[] payload =
77+
BlockSignatureBuffer.generateBlockSignaturePayloadV0(block, nextKey, Optional.empty());
7278
if (currentKey.verify(payload, signature)) {
7379
currentKey = nextKey;
7480
} else {

src/main/java/org/eclipse/biscuit/token/Block.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public Block(
8383
this.scopes = scopes;
8484
this.publicKeys = publicKeys;
8585
this.externalKey = externalKey;
86+
this.version = version;
8687
}
8788

8889
public SymbolTable getSymbolTable() {
@@ -435,7 +436,10 @@ public boolean equals(Object o) {
435436
if (!Objects.equals(publicKeys, block.publicKeys)) {
436437
return false;
437438
}
438-
return Objects.equals(externalKey, block.externalKey);
439+
if (!Objects.equals(externalKey, block.externalKey)) {
440+
return false;
441+
}
442+
return Objects.equals(version, block.version);
439443
}
440444

441445
@Override
@@ -448,6 +452,7 @@ public int hashCode() {
448452
result = 31 * result + (scopes != null ? scopes.hashCode() : 0);
449453
result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
450454
result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
455+
result = 31 * result + Long.hashCode(version);
451456
return result;
452457
}
453458

@@ -471,6 +476,8 @@ public String toString() {
471476
+ publicKeys
472477
+ ", externalKey="
473478
+ externalKey
479+
+ ", version="
480+
+ version
474481
+ '}';
475482
}
476483

0 commit comments

Comments
 (0)