Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 178 additions & 34 deletions src/main/java/org/eclipse/biscuit/crypto/BlockSignatureBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,192 @@

package org.eclipse.biscuit.crypto;

import static io.vavr.API.Left;
import static io.vavr.API.Right;

import biscuit.format.schema.Schema;
import io.vavr.control.Either;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.biscuit.error.Error;
import org.eclipse.biscuit.token.format.ExternalSignature;
import org.eclipse.biscuit.token.format.SerializedBiscuit;
import org.eclipse.biscuit.token.format.SignedBlock;

public final class BlockSignatureBuffer {
public static final int HEADER_SIZE = 4;
public static final int THIRD_PARTY_SIGNATURE_VERSION = 1;
public static final int DATALOG_3_3_SIGNATURE_VERSION = 1;
public static final int NON_ED25519_SIGNATURE_VERSION = 1;

private static final byte[] BLOCK_VERSION =
"\0BLOCK\0\0VERSION\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] EXTERNAL_VERSION =
"\0EXTERNAL\0\0VERSION\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] PAYLOAD = "\0PAYLOAD\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] ALGORITHM = "\0ALGORITHM\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] NEXTKEY = "\0NEXTKEY\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] PREVSIG = "\0PREVSIG\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] EXTERNALSIG = "\0EXTERNALSIG\0".getBytes(StandardCharsets.US_ASCII);

private BlockSignatureBuffer() {}

public static byte[] getBufferSignature(PublicKey nextPubKey, byte[] data) {
return getBufferSignature(nextPubKey, data, Optional.empty());
}

public static byte[] getBufferSignature(
PublicKey nextPubKey, byte[] data, Optional<ExternalSignature> externalSignature) {
var buffer =
ByteBuffer.allocate(
HEADER_SIZE
+ data.length
+ nextPubKey.toBytes().length
+ externalSignature.map((a) -> a.getSignature().length).orElse(0))
.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(data);
externalSignature.ifPresent(signature -> buffer.put(signature.getSignature()));
buffer.putInt(nextPubKey.getAlgorithm().getNumber());
buffer.put(nextPubKey.toBytes());
buffer.flip();
return buffer.array();
}

public static byte[] getBufferSealedSignature(
PublicKey nextPubKey, byte[] data, byte[] blockSignature) {
var buffer =
ByteBuffer.allocate(
HEADER_SIZE + data.length + nextPubKey.toBytes().length + blockSignature.length)
.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(data);
buffer.putInt(nextPubKey.getAlgorithm().getNumber());
buffer.put(nextPubKey.toBytes());
buffer.put(blockSignature);
buffer.flip();
return buffer.array();
public static int blockSignatureVersion(
PublicKey blockKey,
PublicKey nextKey,
Optional<ExternalSignature> externalSignature,
Optional<Long> blockVersion,
Stream<Integer> previousSigVersions) {
if (externalSignature.isPresent()) {
return THIRD_PARTY_SIGNATURE_VERSION;
}

if (blockVersion.isPresent() && blockVersion.get() >= SerializedBiscuit.DATALOG_3_3) {
return DATALOG_3_3_SIGNATURE_VERSION;
}

if (blockKey.getAlgorithm() != Schema.PublicKey.Algorithm.Ed25519
|| nextKey.getAlgorithm() != Schema.PublicKey.Algorithm.Ed25519) {
return NON_ED25519_SIGNATURE_VERSION;
}

return previousSigVersions.mapToInt(Integer::intValue).max().orElse(0);
}

public static Either<Error.FormatError, byte[]> generateBlockSignaturePayload(
byte[] payload,
PublicKey nextKey,
Optional<ExternalSignature> externalSignature,
Optional<byte[]> previousSignature,
int version) {
switch (version) {
case 0:
return Right(generateBlockSignaturePayloadV0(payload, nextKey, externalSignature));
case 1:
return Right(
generateBlockSignaturePayloadV1(
payload, nextKey, externalSignature, previousSignature, version));
default:
return Left(
new Error.FormatError.DeserializationError("unsupported block version " + version));
}
}

public static byte[] generateBlockSignaturePayloadV0(
byte[] payload, PublicKey nextKey, Optional<ExternalSignature> externalSignature) {
var nextKeyBytes = nextKey.toBytes();
var capacity = payload.length + Integer.BYTES + nextKeyBytes.length;
if (externalSignature.isPresent()) {
capacity += externalSignature.get().getSignature().length;
}
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
toVerify.put(payload);
if (externalSignature.isPresent()) {
toVerify.put(externalSignature.get().getSignature());
}
toVerify.putInt(nextKey.getAlgorithm().getNumber());
toVerify.put(nextKeyBytes);
toVerify.flip();
return toVerify.array();
}

public static byte[] generateBlockSignaturePayloadV1(
byte[] payload,
PublicKey nextKey,
Optional<ExternalSignature> externalSignature,
Optional<byte[]> previousSignature,
int version) {
var nextKeyBytes = nextKey.toBytes();
var capacity =
BLOCK_VERSION.length
+ Integer.BYTES
+ PAYLOAD.length
+ payload.length
+ ALGORITHM.length
+ Integer.BYTES
+ NEXTKEY.length
+ nextKeyBytes.length;
if (previousSignature.isPresent()) {
capacity += PREVSIG.length + previousSignature.get().length;
}
if (externalSignature.isPresent()) {
capacity += EXTERNALSIG.length + externalSignature.get().getSignature().length;
}

var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
toVerify.put(BLOCK_VERSION);
toVerify.putInt(version);
toVerify.put(PAYLOAD);
toVerify.put(payload);
toVerify.put(ALGORITHM);
toVerify.putInt(nextKey.getAlgorithm().getNumber());
toVerify.put(NEXTKEY);
toVerify.put(nextKeyBytes);
if (previousSignature.isPresent()) {
toVerify.put(PREVSIG);
toVerify.put(previousSignature.get());
}
if (externalSignature.isPresent()) {
toVerify.put(EXTERNALSIG);
toVerify.put(externalSignature.get().getSignature());
}
toVerify.flip();
return toVerify.array();
}

public static byte[] generateExternalBlockSignaturePayload(
byte[] payload, PublicKey previousKey, byte[] previousSignature, int version) {
if (version == 0) {
return generateExternalBlockSignaturePayloadV0(payload, previousKey);
} else {
return generateExternalBlockSignaturePayloadV1(payload, previousSignature, version);
}
}

public static byte[] generateExternalBlockSignaturePayloadV0(
byte[] payload, PublicKey previousKey) {
var previousKeyBytes = previousKey.toBytes();
var capacity = payload.length + Integer.BYTES + previousKeyBytes.length;
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
toVerify.put(payload);
toVerify.putInt(previousKey.getAlgorithm().getNumber());
toVerify.put(previousKey.toBytes());
toVerify.flip();
return toVerify.array();
}

public static byte[] generateExternalBlockSignaturePayloadV1(
byte[] payload, byte[] previousSignature, int version) {
var capacity =
EXTERNAL_VERSION.length
+ Integer.BYTES
+ PAYLOAD.length
+ payload.length
+ PREVSIG.length
+ previousSignature.length;
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
toVerify.put(EXTERNAL_VERSION);
toVerify.putInt(version);
toVerify.put(PAYLOAD);
toVerify.put(payload);
toVerify.put(PREVSIG);
toVerify.put(previousSignature);
toVerify.flip();
return toVerify.array();
}

public static byte[] generateSealBlockSignaturePayloadV0(SignedBlock block) {
var keyBytes = block.getKey().toBytes();
var capacity =
block.getBlock().length + Integer.BYTES + keyBytes.length + block.getSignature().length;
var toVerify = ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
toVerify.put(block.getBlock());
toVerify.putInt(block.getKey().getAlgorithm().getNumber());
toVerify.put(keyBytes);
toVerify.put(block.getSignature());
toVerify.flip();
return toVerify.array();
}
}
12 changes: 9 additions & 3 deletions src/main/java/org/eclipse/biscuit/crypto/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Optional;
import org.eclipse.biscuit.error.Error;

class Token {
Expand All @@ -29,7 +30,9 @@ class Token {
this.keys = new ArrayList<>();
this.keys.add(next.getPublicKey());
this.signatures = new ArrayList<>();
byte[] payload = BlockSignatureBuffer.getBufferSignature(next.getPublicKey(), message);
byte[] payload =
BlockSignatureBuffer.generateBlockSignaturePayloadV0(
message, next.getPublicKey(), Optional.empty());
byte[] signature = rootSigner.sign(payload);
this.signatures.add(signature);
this.next = next;
Expand All @@ -48,7 +51,9 @@ class Token {

Token append(KeyPair keyPair, byte[] message)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
byte[] payload = BlockSignatureBuffer.getBufferSignature(keyPair.getPublicKey(), message);
byte[] payload =
BlockSignatureBuffer.generateBlockSignaturePayloadV0(
message, keyPair.getPublicKey(), Optional.empty());
byte[] signature = this.next.sign(payload);

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

byte[] payload = BlockSignatureBuffer.getBufferSignature(nextKey, block);
byte[] payload =
BlockSignatureBuffer.generateBlockSignaturePayloadV0(block, nextKey, Optional.empty());
if (currentKey.verify(payload, signature)) {
currentKey = nextKey;
} else {
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/org/eclipse/biscuit/token/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public Block(
this.scopes = scopes;
this.publicKeys = publicKeys;
this.externalKey = externalKey;
this.version = version;
}

public SymbolTable getSymbolTable() {
Expand Down Expand Up @@ -435,7 +436,10 @@ public boolean equals(Object o) {
if (!Objects.equals(publicKeys, block.publicKeys)) {
return false;
}
return Objects.equals(externalKey, block.externalKey);
if (!Objects.equals(externalKey, block.externalKey)) {
return false;
}
return Objects.equals(version, block.version);
}

@Override
Expand All @@ -448,6 +452,7 @@ 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 + Long.hashCode(version);
return result;
}

Expand All @@ -471,6 +476,8 @@ public String toString() {
+ publicKeys
+ ", externalKey="
+ externalKey
+ ", version="
+ version
+ '}';
}

Expand Down
Loading