org.biscuitsec.biscuit.token.format.SerializedBiscuit Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biscuit Show documentation
Show all versions of biscuit Show documentation
Java support for the biscuit auth token and policy language
package org.biscuitsec.biscuit.token.format;
import biscuit.format.schema.Schema;
import io.vavr.Tuple2;
import org.biscuitsec.biscuit.crypto.KeyDelegate;
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.Block;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
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;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.*;
import java.util.*;
import static io.vavr.API.Left;
import static io.vavr.API.Right;
/**
* Intermediate representation of a token before full serialization
*/
public class SerializedBiscuit {
public SignedBlock authority;
public List blocks;
public Proof proof;
public Option root_key_id;
public static int MIN_SCHEMA_VERSION = 3;
public static int MAX_SCHEMA_VERSION = 5;
/**
* Deserializes a SerializedBiscuit from a byte array
*
* @param slice
* @return
*/
static public SerializedBiscuit from_bytes(byte[] slice, org.biscuitsec.biscuit.crypto.PublicKey root) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
try {
Schema.Biscuit data = Schema.Biscuit.parseFrom(slice);
return from_bytes_inner(data, root);
} catch (InvalidProtocolBufferException e) {
throw new Error.FormatError.DeserializationError(e.toString());
}
}
/**
* Deserializes a SerializedBiscuit from a byte array
*
* @param slice
* @return
*/
static public SerializedBiscuit from_bytes(byte[] slice, KeyDelegate delegate) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
try {
Schema.Biscuit data = Schema.Biscuit.parseFrom(slice);
Option root_key_id = Option.none();
if (data.hasRootKeyId()) {
root_key_id = Option.some(data.getRootKeyId());
}
Option root = delegate.root_key(root_key_id);
if (root.isEmpty()) {
throw new InvalidKeyException("unknown root key id");
}
return from_bytes_inner(data, root.get());
} catch (InvalidProtocolBufferException e) {
throw new Error.FormatError.DeserializationError(e.toString());
}
}
static SerializedBiscuit from_bytes_inner(Schema.Biscuit data, org.biscuitsec.biscuit.crypto.PublicKey root) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
SerializedBiscuit b = SerializedBiscuit.deserialize(data);
if (data.hasRootKeyId()) {
b.root_key_id = Option.some(data.getRootKeyId());
}
Either res = b.verify(root);
if (res.isLeft()) {
throw res.getLeft();
} else {
return b;
}
}
/**
* Warning: this deserializes without verifying the signature
*
* @param slice
* @return SerializedBiscuit
* @throws Error.FormatError.DeserializationError
*/
static public SerializedBiscuit unsafe_deserialize(byte[] slice) throws Error.FormatError.DeserializationError {
try {
Schema.Biscuit data = Schema.Biscuit.parseFrom(slice);
return SerializedBiscuit.deserialize(data);
} catch (InvalidProtocolBufferException e) {
throw new Error.FormatError.DeserializationError(e.toString());
}
}
/**
* Warning: this deserializes without verifying the signature
*
* @param data
* @return SerializedBiscuit
* @throws Error.FormatError.DeserializationError
*/
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");
}
SignedBlock authority = new SignedBlock(
data.getAuthority().getBlock().toByteArray(),
org.biscuitsec.biscuit.crypto.PublicKey.deserialize(data.getAuthority().getNextKey()),
data.getAuthority().getSignature().toByteArray(),
Option.none()
);
ArrayList blocks = new ArrayList<>();
for (Schema.SignedBlock block : data.getBlocksList()) {
Option external = Option.none();
if(block.hasExternalSignature() && block.getExternalSignature().hasPublicKey()
&& block.getExternalSignature().hasSignature()) {
Schema.ExternalSignature ex = block.getExternalSignature();
external = Option.some(new ExternalSignature(
org.biscuitsec.biscuit.crypto.PublicKey.deserialize(ex.getPublicKey()),
ex.getSignature().toByteArray()));
}
blocks.add(new SignedBlock(
block.getBlock().toByteArray(),
org.biscuitsec.biscuit.crypto.PublicKey.deserialize(block.getNextKey()),
block.getSignature().toByteArray(),
external
));
}
Option secretKey = Option.none();
if (data.getProof().hasNextSecret()) {
secretKey = Option.some(new org.biscuitsec.biscuit.crypto.KeyPair(data.getProof().getNextSecret().toByteArray()));
}
Option signature = Option.none();
if (data.getProof().hasFinalSignature()) {
signature = Option.some(data.getProof().getFinalSignature().toByteArray());
}
if (secretKey.isEmpty() && signature.isEmpty()) {
throw new Error.FormatError.DeserializationError("empty proof");
}
Proof proof = new Proof(secretKey, signature);
return new SerializedBiscuit(authority, blocks, proof);
}
/**
* Serializes a SerializedBiscuit to a byte array
*
* @return
*/
public byte[] serialize() throws Error.FormatError.SerializationError {
Schema.Biscuit.Builder biscuitBuilder = Schema.Biscuit.newBuilder();
Schema.SignedBlock.Builder authorityBuilder = Schema.SignedBlock.newBuilder();
{
SignedBlock block = this.authority;
authorityBuilder.setBlock(ByteString.copyFrom(block.block));
authorityBuilder.setNextKey(block.key.serialize());
authorityBuilder.setSignature(ByteString.copyFrom(block.signature));
}
biscuitBuilder.setAuthority(authorityBuilder.build());
for (SignedBlock block : this.blocks) {
Schema.SignedBlock.Builder blockBuilder = Schema.SignedBlock.newBuilder();
blockBuilder.setBlock(ByteString.copyFrom(block.block));
blockBuilder.setNextKey(block.key.serialize());
blockBuilder.setSignature(ByteString.copyFrom(block.signature));
if (block.externalSignature.isDefined()) {
ExternalSignature externalSignature = block.externalSignature.get();
Schema.ExternalSignature.Builder externalSignatureBuilder = Schema.ExternalSignature.newBuilder();
externalSignatureBuilder.setPublicKey(externalSignature.key.serialize());
externalSignatureBuilder.setSignature(ByteString.copyFrom(externalSignature.signature));
blockBuilder.setExternalSignature(externalSignatureBuilder.build());
}
biscuitBuilder.addBlocks(blockBuilder.build());
}
Schema.Proof.Builder proofBuilder = Schema.Proof.newBuilder();
if (!this.proof.secretKey.isEmpty()) {
proofBuilder.setNextSecret(ByteString.copyFrom(this.proof.secretKey.get().toBytes()));
} else {
proofBuilder.setFinalSignature(ByteString.copyFrom(this.proof.signature.get()));
}
biscuitBuilder.setProof(proofBuilder.build());
if (!this.root_key_id.isEmpty()) {
biscuitBuilder.setRootKeyId(this.root_key_id.get());
}
Schema.Biscuit biscuit = biscuitBuilder.build();
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
biscuit.writeTo(stream);
return stream.toByteArray();
} catch (IOException e) {
throw new Error.FormatError.SerializationError(e.toString());
}
}
static public Either make(final org.biscuitsec.biscuit.crypto.KeyPair root,
final Block authority, final org.biscuitsec.biscuit.crypto.KeyPair next) {
return make(root, Option.none(), authority, next);
}
static public Either make(final org.biscuitsec.biscuit.crypto.KeyPair root, final Option root_key_id,
final Block authority, final org.biscuitsec.biscuit.crypto.KeyPair next) {
Schema.Block b = authority.serialize();
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
b.writeTo(stream);
byte[] block = stream.toByteArray();
org.biscuitsec.biscuit.crypto.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 = KeyPair.generateSignature(root.public_key().algorithm);
sgr.initSign(root.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());
Proof proof = new Proof(next);
return Right(new SerializedBiscuit(signedBlock, new ArrayList<>(), proof, root_key_id));
} catch (IOException | NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
return Left(new Error.FormatError.SerializationError(e.toString()));
}
}
public Either append(final org.biscuitsec.biscuit.crypto.KeyPair next,
final Block newBlock, Option externalSignature) {
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();
org.biscuitsec.biscuit.crypto.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(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm()));
sgr.initSign(this.proof.secretKey.get().private_key);
sgr.update(block);
if(externalSignature.isDefined()) {
sgr.update(externalSignature.get().signature);
}
sgr.update(algo_buf);
sgr.update(next_key.toBytes());
byte[] signature = sgr.sign();
SignedBlock signedBlock = new SignedBlock(block, next_key, signature, externalSignature);
ArrayList blocks = new ArrayList<>();
for (SignedBlock bl : this.blocks) {
blocks.add(bl);
}
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 verify(org.biscuitsec.biscuit.crypto.PublicKey root) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
org.biscuitsec.biscuit.crypto.PublicKey current_key = root;
ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
{
Either res = verifyBlockSignature(this.authority, current_key);
if(res.isRight()) {
current_key = res.get();
} else {
return Left(res.getLeft());
}
}
for (SignedBlock b : this.blocks) {
Either res = verifyBlockSignature(b, current_key);
if(res.isRight()) {
current_key = res.get();
} else {
return Left(res.getLeft());
}
}
//System.out.println("signatures verified, checking proof");
if (!this.proof.secretKey.isEmpty()) {
//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");
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"));
}
} else {
//System.out.println("checking final signature");
byte[] finalSignature = this.proof.signature.get();
SignedBlock b;
if (this.blocks.isEmpty()) {
b = this.authority;
} else {
b = this.blocks.get(this.blocks.size() - 1);
}
byte[] block = b.block;
org.biscuitsec.biscuit.crypto.PublicKey next_key = b.key;
byte[] signature = b.signature;
algo_buf.clear();
algo_buf.putInt(next_key.algorithm.getNumber());
algo_buf.flip();
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(org.biscuitsec.biscuit.crypto.KeyPair.ed25519.getHashAlgorithm()));
sgr.initVerify(current_key.key);
sgr.update(block);
sgr.update(algo_buf);
sgr.update(next_key.toBytes());
sgr.update(signature);
if (sgr.verify(finalSignature)) {
return Right(null);
} else {
return Left(new Error.FormatError.Signature.SealedSignature());
}
}
}
static Either verifyBlockSignature(SignedBlock signedBlock, org.biscuitsec.biscuit.crypto.PublicKey publicKey)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
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));
}
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(publicKey.algorithm);
sgr.initVerify(publicKey.key);
sgr.update(block);
if(signedBlock.externalSignature.isDefined()) {
sgr.update(signedBlock.externalSignature.get().signature);
}
sgr.update(algo_buf);
sgr.update(next_key.toBytes());
if (!sgr.verify(signature)) {
return Left(new Error.FormatError.Signature.InvalidSignature("signature error: Verification equation was not satisfied"));
}
if(signedBlock.externalSignature.isDefined()) {
ByteBuffer algo_buf2 = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
algo_buf2.putInt(Integer.valueOf(publicKey.algorithm.getNumber()));
algo_buf2.flip();
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());
if (!sgr2.verify(signedBlock.externalSignature.get().signature)) {
return Left(new Error.FormatError.Signature.InvalidSignature("external signature error: Verification equation was not satisfied"));
}
}
return Right(next_key);
}
public Tuple2> extractBlocks(SymbolTable symbols) throws Error {
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy