Skip to content
Open
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
21 changes: 20 additions & 1 deletion src/main/java/org/eclipse/biscuit/crypto/SECP256R1KeyPair.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.security.SecureRandom;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
Expand Down Expand Up @@ -67,14 +68,32 @@ final class SECP256R1KeyPair extends KeyPair {
this.publicKey = publicKey;
}

/// By default sign message digests with a deterministic k
/// computed using the algorithm described in [RFC6979 § 3.2].
///
/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
///
/// Although deterministic ECDSA signing is typically slower than
/// signing with an RNG, it prevents accidental nonce-reuse due to
/// a weak RNG.
@Override
public byte[] sign(byte[] data) {
return sign(data, true);
}

public byte[] sign(byte[] data, boolean deterministicNonce) {
var digest = new SHA256Digest();
digest.update(data, 0, data.length);
var hash = new byte[digest.getDigestSize()];
digest.doFinal(hash, 0);

var signer = new ECDSASigner();
ECDSASigner signer;
if (deterministicNonce) {
signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
} else {
signer = new ECDSASigner();
}

signer.init(true, privateKey.engineGetKeyParameters());
var sig = signer.generateSignature(hash);

Expand Down
8 changes: 7 additions & 1 deletion src/main/java/org/eclipse/biscuit/token/Biscuit.java
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,13 @@ public Biscuit attenuate(final SecureRandom rng, final KeyPair keypair, Block bl
/** 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);
return appendThirdPartyBlock(externalKey, blockResponse, new SecureRandom());
}

public Biscuit appendThirdPartyBlock(
PublicKey externalKey, ThirdPartyBlockContents blockResponse, SecureRandom rng)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
UnverifiedBiscuit b = super.appendThirdPartyBlock(externalKey, blockResponse, rng);

// no need to verify again, we are already working from a verified token
return Biscuit.fromSerializedBiscuit(b.serializedBiscuit, b.symbolTable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,20 @@ public ThirdPartyBlockRequest thirdPartyRequest() {
public UnverifiedBiscuit appendThirdPartyBlock(
PublicKey externalKey, ThirdPartyBlockContents blockResponse)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
return appendThirdPartyBlock(externalKey, blockResponse, new SecureRandom());
}

public UnverifiedBiscuit appendThirdPartyBlock(
PublicKey externalKey, ThirdPartyBlockContents blockResponse, SecureRandom rng)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
SignedBlock previousBlock;
if (this.serializedBiscuit.getBlocks().isEmpty()) {
previousBlock = this.serializedBiscuit.getAuthority();
} else {
previousBlock =
this.serializedBiscuit.getBlocks().get(this.serializedBiscuit.getBlocks().size() - 1);
}
KeyPair nextKeyPair = KeyPair.generate(previousBlock.getKey().getAlgorithm());
KeyPair nextKeyPair = KeyPair.generate(previousBlock.getKey().getAlgorithm(), rng);
byte[] payload =
BlockSignatureBuffer.generateExternalBlockSignaturePayloadV1(
blockResponse.getPayload(),
Expand Down
24 changes: 14 additions & 10 deletions src/test/java/org/eclipse/biscuit/crypto/SignatureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@
import org.eclipse.biscuit.error.Error;
import org.eclipse.biscuit.error.Result;
import org.eclipse.biscuit.token.Biscuit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
* @serial exclude
*/
public class SignatureTest {
private SecureRandom rng;

@BeforeEach
public void setUp() throws NoSuchAlgorithmException {
byte[] seed = {0, 0, 0, 0};
rng = SecureRandom.getInstance("SHA1PRNG");
rng.setSeed(seed);
}

@Test
public void testSerialize() throws Error.FormatError {
public void testSerialize() throws Error.FormatError, NoSuchAlgorithmException {
prTestSerialize(Schema.PublicKey.Algorithm.Ed25519, 32);
prTestSerialize(
// compressed - 0x02 or 0x03 prefix byte, 32 bytes for X coordinate
Expand Down Expand Up @@ -86,11 +96,8 @@ void testInvalidEd25519PublicKey() {
() -> PublicKey.load(Schema.PublicKey.Algorithm.Ed25519, "badkey".getBytes()));
}

private static void prTestSerialize(
Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength) throws Error.FormatError {
byte[] seed = {1, 2, 3, 4};
SecureRandom rng = new SecureRandom(seed);

private void prTestSerialize(Schema.PublicKey.Algorithm algorithm, int expectedPublicKeyLength)
throws Error.FormatError, NoSuchAlgorithmException {
KeyPair keypair = KeyPair.generate(algorithm, rng);
PublicKey pubkey = keypair.getPublicKey();

Expand All @@ -112,11 +119,8 @@ private static void prTestSerialize(
assertEquals(pubkey.toHex(), deserializedPublicKey.toHex());
}

private static void prTestThreeMessages(Schema.PublicKey.Algorithm algorithm)
private void prTestThreeMessages(Schema.PublicKey.Algorithm algorithm)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

String message1 = "hello";
KeyPair root = KeyPair.generate(algorithm, rng);
KeyPair keypair2 = KeyPair.generate(algorithm, rng);
Expand Down
14 changes: 11 additions & 3 deletions src/test/java/org/eclipse/biscuit/token/AuthorizerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@
import org.eclipse.biscuit.error.Error.Parser;
import org.eclipse.biscuit.token.builder.Expression;
import org.eclipse.biscuit.token.builder.Term;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class AuthorizerTest {
final RunLimits runLimits = new RunLimits(500, 100, Duration.ofMillis(500));
private SecureRandom rng;

@BeforeEach
public void setUp() throws Exception {
byte[] seed = {0, 0, 0, 0};
rng = SecureRandom.getInstance("SHA1PRNG");
rng.setSeed(seed);
}

@Test
public void testAuthorizerPolicy() throws Parser {
Expand All @@ -51,8 +60,7 @@ public void testAuthorizerPolicy() throws Parser {

@Test
public void testPuttingSomeFactsInBiscuitAndGettingThemBackOutAgain() throws Exception {

KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, new SecureRandom());
KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);

Biscuit token =
Biscuit.builder(keypair)
Expand Down Expand Up @@ -84,7 +92,7 @@ public void testPuttingSomeFactsInBiscuitAndGettingThemBackOutAgain() throws Exc

@Test
public void testDatalogAuthorizer() throws Exception {
KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, new SecureRandom());
KeyPair keypair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);

Biscuit token =
Biscuit.builder(keypair)
Expand Down
37 changes: 12 additions & 25 deletions src/test/java/org/eclipse/biscuit/token/BiscuitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,23 @@
import org.eclipse.biscuit.error.LogicError;
import org.eclipse.biscuit.token.builder.Block;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class BiscuitTest {

private SecureRandom rng;

@BeforeEach
public void setUp() throws NoSuchAlgorithmException {
byte[] seed = {0, 0, 0, 0};
rng = SecureRandom.getInstance("SHA1PRNG");
rng.setSeed(seed);
}

@Test
public void testBasic()
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

Expand Down Expand Up @@ -177,9 +185,6 @@ public void testBasic()

@Test
public void testFolders() throws NoSuchAlgorithmException, Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -288,10 +293,7 @@ public void testMultipleAttenuation()
}

@Test
public void testReset() throws Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

public void testReset() throws Error, NoSuchAlgorithmException {
System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -361,10 +363,7 @@ public void testReset() throws Error {
}

@Test
public void testEmptyAuthorizer() throws Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

public void testEmptyAuthorizer() throws Error, NoSuchAlgorithmException {
System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -405,9 +404,6 @@ public void testEmptyAuthorizer() throws Error {
@Test
public void testBasicWithNamespaces()
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -535,9 +531,6 @@ public void testBasicWithNamespaces()
@Test
public void testBasicWithNamespacesWithAddAuthorityFact()
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -664,9 +657,6 @@ public void testBasicWithNamespacesWithAddAuthorityFact()
@Test
public void testRootKeyId()
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -739,9 +729,6 @@ public Optional<PublicKey> getRootKey(Optional<Integer> keyId) {
@Test
public void testCheckAll()
throws Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down
72 changes: 62 additions & 10 deletions src/test/java/org/eclipse/biscuit/token/ThirdPartyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,19 @@
import org.eclipse.biscuit.error.FailedCheck;
import org.eclipse.biscuit.error.LogicError;
import org.eclipse.biscuit.token.builder.Block;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class ThirdPartyTest {
private SecureRandom rng;

@BeforeEach
public void setUp() throws NoSuchAlgorithmException {
byte[] seed = {0, 0, 0, 0};
rng = SecureRandom.getInstance("SHA1PRNG");
rng.setSeed(seed);
}

@Test
public void testRoundTrip()
throws NoSuchAlgorithmException,
Expand All @@ -34,8 +44,6 @@ public void testRoundTrip()
CloneNotSupportedException,
Error,
IOException {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

Expand Down Expand Up @@ -105,11 +113,6 @@ public void testPublicKeyInterning()
InvalidKeyException,
CloneNotSupportedException,
Error {
// this makes a deterministic RNG
SecureRandom rng = SecureRandom.getInstance("SHA1PRNG");
byte[] seed = {0, 0, 0, 0};
rng.setSeed(seed);

System.out.println("preparing the authority block");

final KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -200,9 +203,6 @@ public void testReusedSymbols()
InvalidKeyException,
CloneNotSupportedException,
Error {
byte[] seed = {0, 0, 0, 0};
SecureRandom rng = new SecureRandom(seed);

System.out.println("preparing the authority block");

final KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519, rng);
Expand Down Expand Up @@ -254,4 +254,56 @@ public void testReusedSymbols()
e);
}
}

private org.eclipse.biscuit.token.Biscuit generateDeterministicBiscuit(SecureRandom rng)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
final KeyPair root = KeyPair.generate(Schema.PublicKey.Algorithm.SECP256R1, rng);
final KeyPair external = KeyPair.generate(Schema.PublicKey.Algorithm.SECP256R1, rng);

Block authorityBuilder = new Block();
Biscuit b1 = Biscuit.make(rng, root, authorityBuilder.build());

ThirdPartyBlockRequest request = b1.thirdPartyRequest();
Block builder = new Block();
ThirdPartyBlockContents blockResponse = request.createBlock(external, builder).getOk();
Biscuit b2 = b1.appendThirdPartyBlock(external.getPublicKey(), blockResponse, rng);

return b2;
}

@Test
public void testDeterministicECDSA()
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
// Generate the same set of root and 3rd-party keys and confirm that they result in the same
// ECDSA signatures.

// Create fresh RNG with same seed for first biscuit
byte[] seed = {0, 0, 0, 0};
SecureRandom rng1 = SecureRandom.getInstance("SHA1PRNG");
rng1.setSeed(seed);
Biscuit b1 = generateDeterministicBiscuit(rng1);

// Create fresh RNG with same seed for second biscuit to get identical signatures
SecureRandom rng2 = SecureRandom.getInstance("SHA1PRNG");
rng2.setSeed(seed);
Biscuit b2 = generateDeterministicBiscuit(rng2);

assert (Arrays.equals(
b1.serializedBiscuit.getAuthority().getSignature(),
b2.serializedBiscuit.getAuthority().getSignature()));

assert (Arrays.equals(
b1.serializedBiscuit.getBlocks().get(0).getSignature(),
b2.serializedBiscuit.getBlocks().get(0).getSignature()));

byte[] data1 = b1.serialize();
byte[] data2 = b2.serialize();
assert (Arrays.equals(data1, data2));
assertEquals(b1.print(), b2.print());

// Make sure that the token is still valid
Authorizer authorizer = b1.authorizer();
authorizer.addPolicy("allow if true");
authorizer.authorize(new RunLimits(500, 100, Duration.ofMillis(500)));
}
}
Loading