Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote signer draft #2

Draft
wants to merge 15 commits into
base: ec_p256
Choose a base branch
from
Draft
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,24 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>kms</artifactId>
<version>2.25.65</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>localstack</artifactId>
<version>1.19.8</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
Expand Down
39 changes: 27 additions & 12 deletions src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.biscuitsec.biscuit.crypto;

import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
Expand All @@ -10,54 +10,59 @@
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.biscuitsec.biscuit.token.builder.Utils;

import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Optional;

class Ed25519KeyPair extends KeyPair {

private final EdDSAPrivateKey privateKey;
private final EdDSAPublicKey publicKey;
private final Signer signer;

private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
private static final EdDSANamedCurveSpec CURVE_SPEC = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);

public Ed25519KeyPair(byte[] bytes) {
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, ed25519);
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, CURVE_SPEC);
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);

EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), CURVE_SPEC);
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);

this.privateKey = privKey;
this.publicKey = pubKey;
this.signer = new PrivateKeySigner(Algorithm.Ed25519, privKey);
}

public Ed25519KeyPair(SecureRandom rng) {
byte[] b = new byte[32];
rng.nextBytes(b);

EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, CURVE_SPEC);
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);

EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), CURVE_SPEC);
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);

this.privateKey = privKey;
this.publicKey = pubKey;
this.signer = new PrivateKeySigner(Algorithm.Ed25519, privKey);
}

public Ed25519KeyPair(String hex) {
this(Utils.hexStringToByteArray(hex));
}

public static java.security.PublicKey decode(byte[] data) {
return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, ed25519));
return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, CURVE_SPEC));
}

public static Signature getSignature() throws NoSuchAlgorithmException {
return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
return new EdDSAEngine(MessageDigest.getInstance(CURVE_SPEC.getHashAlgorithm()));
}

@Override
Expand All @@ -76,12 +81,22 @@ public java.security.PublicKey publicKey() {
}

@Override
public PrivateKey private_key() {
return privateKey;
public byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signStandard(block, Algorithm.Ed25519, publicKey);
}

@Override
public byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signExternal(block, Algorithm.Ed25519, publicKey, external);
}

@Override
public byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signSealed(block, Algorithm.Ed25519, publicKey, seal);
}

@Override
public PublicKey public_key() {
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.publicKey);
return new PublicKey(Algorithm.Ed25519, this.publicKey);
}
}
17 changes: 15 additions & 2 deletions src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.Utils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Optional;

/**
* Private and public key.
Expand Down Expand Up @@ -41,6 +46,10 @@ public static KeyPair generate(Algorithm algorithm, SecureRandom rng) {
}
}

public static KeyPair generate(PublicKey publicKey, Signer signer) {
return new RemoteKeyPair(publicKey, signer);
}

public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException {
if (algorithm == Algorithm.Ed25519) {
return Ed25519KeyPair.getSignature();
Expand All @@ -57,7 +66,11 @@ public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgo

public abstract java.security.PublicKey publicKey();

public abstract java.security.PrivateKey private_key();

public abstract PublicKey public_key();

public abstract byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException;

public abstract byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException;

public abstract byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException;
}
27 changes: 27 additions & 0 deletions src/main/java/org/biscuitsec/biscuit/crypto/PrivateKeySigner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.biscuitsec.biscuit.crypto;

import biscuit.format.schema.Schema.PublicKey.Algorithm;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;

public class PrivateKeySigner implements Signer {

private final Algorithm algorithm;
private final PrivateKey privateKey;

public PrivateKeySigner(Algorithm algorithm, PrivateKey privateKey) {
this.algorithm = algorithm;
this.privateKey = privateKey;
}

@Override
public byte[] sign(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
var sgr = KeyPair.generateSignature(algorithm);
sgr.initSign(privateKey);
sgr.update(bytes);
return sgr.sign();
}
}
53 changes: 53 additions & 0 deletions src/main/java/org/biscuitsec/biscuit/crypto/RemoteKeyPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.biscuitsec.biscuit.crypto;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.Optional;

public class RemoteKeyPair extends KeyPair {

private final org.biscuitsec.biscuit.crypto.PublicKey publicKey;
private final Signer signer;

public RemoteKeyPair(org.biscuitsec.biscuit.crypto.PublicKey publicKey, Signer signer) {
this.publicKey = publicKey;
this.signer = signer;
}

@Override
public byte[] toBytes() {
throw new RuntimeException("Illegal operation; remote private private key cannot be accessed.");
}

@Override
public String toHex() {
throw new RuntimeException("Illegal operation; remote private private key cannot be accessed.");
}

@Override
public PublicKey publicKey() {
return publicKey.key;
}

@Override
public byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signStandard(block, this.publicKey.algorithm, publicKey);
}

@Override
public byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signExternal(block, this.publicKey.algorithm, publicKey, external);
}

@Override
public byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signSealed(block, this.publicKey.algorithm, publicKey, seal);
}

@Override
public org.biscuitsec.biscuit.crypto.PublicKey public_key() {
return publicKey;
}
}
40 changes: 25 additions & 15 deletions src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.biscuitsec.biscuit.crypto;

import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import org.biscuitsec.biscuit.token.builder.Utils;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
Expand All @@ -11,48 +11,48 @@
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.util.BigIntegers;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Optional;

class SECP256R1KeyPair extends KeyPair {

private final BCECPrivateKey privateKey;
private final BCECPublicKey publicKey;
private final Signer signer;

private static final String ALGORITHM = "ECDSA";
private static final String CURVE = "secp256r1";
private static final ECNamedCurveParameterSpec SECP256R1 = ECNamedCurveTable.getParameterSpec(CURVE);

static {
Security.addProvider(new BouncyCastleProvider());
}
private static final ECNamedCurveParameterSpec CURVE_SPEC = ECNamedCurveTable.getParameterSpec(CURVE);

public SECP256R1KeyPair(byte[] bytes) {
var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1);
var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), CURVE_SPEC);
var privateKey = new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION);

var publicKeySpec = new ECPublicKeySpec(SECP256R1.getG().multiply(privateKeySpec.getD()), SECP256R1);
var publicKeySpec = new ECPublicKeySpec(CURVE_SPEC.getG().multiply(privateKeySpec.getD()), CURVE_SPEC);
var publicKey = new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION);

this.privateKey = privateKey;
this.publicKey = publicKey;
this.signer = new PrivateKeySigner(Algorithm.SECP256R1, privateKey);
}

public SECP256R1KeyPair(SecureRandom rng) {
byte[] bytes = new byte[32];
rng.nextBytes(bytes);

var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1);
var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), CURVE_SPEC);
var privateKey = new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION);

var publicKeySpec = new ECPublicKeySpec(SECP256R1.getG().multiply(privateKeySpec.getD()), SECP256R1);
var publicKeySpec = new ECPublicKeySpec(CURVE_SPEC.getG().multiply(privateKeySpec.getD()), CURVE_SPEC);
var publicKey = new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION);

this.privateKey = privateKey;
this.publicKey = publicKey;
this.signer = new PrivateKeySigner(Algorithm.SECP256R1, privateKey);
}

public SECP256R1KeyPair(String hex) {
Expand Down Expand Up @@ -85,12 +85,22 @@ public java.security.PublicKey publicKey() {
}

@Override
public PrivateKey private_key() {
return privateKey;
public byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signStandard(block, Algorithm.SECP256R1, publicKey);
}

@Override
public byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signExternal(block, Algorithm.SECP256R1, publicKey, external);
}

@Override
public byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
return signer.signSealed(block, Algorithm.SECP256R1, publicKey, seal);
}

@Override
public PublicKey public_key() {
return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, publicKey);
return new PublicKey(Algorithm.SECP256R1, publicKey);
}
}
53 changes: 53 additions & 0 deletions src/main/java/org/biscuitsec/biscuit/crypto/Signer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.biscuitsec.biscuit.crypto;

import biscuit.format.schema.Schema;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Arrays;

public interface Signer {
byte[] sign(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException;

// format: block, algo, publicKey
default byte[] signStandard(byte[] block, Schema.PublicKey.Algorithm algorithm, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
var algorithmBytes = getBufferForAlgorithm(algorithm);
var payload = concatenateArrays(block, algorithmBytes, publicKey);
return sign(payload);
}

// format: block, algo, publicKey, seal
default byte[] signSealed(byte[] block, Schema.PublicKey.Algorithm algorithm, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
var algorithmBytes = getBufferForAlgorithm(algorithm);
var payload = concatenateArrays(block, algorithmBytes, publicKey, seal);
return sign(payload);
}

// format: block, external, algo, publicKey
default byte[] signExternal(byte[] block, Schema.PublicKey.Algorithm algorithm, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
var algorithmBytes = getBufferForAlgorithm(algorithm);
var payload = concatenateArrays(block, external, algorithmBytes, publicKey);
return sign(payload);
}

private static byte[] concatenateArrays(byte[]... arrays) {
int totalLength = Arrays.stream(arrays).mapToInt(arr -> arr.length).sum();
byte[] result = new byte[totalLength];
int currentPos = 0;
for (byte[] array : arrays) {
System.arraycopy(array, 0, result, currentPos, array.length);
currentPos += array.length;
}
return result;
}

private static byte[] getBufferForAlgorithm(Schema.PublicKey.Algorithm algorithm) {
var algorithmBuffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
algorithmBuffer.putInt(algorithm.getNumber());
algorithmBuffer.flip();
return algorithmBuffer.array();
}
}
Loading