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

Add ECDSA key support (SECP256R1) #101

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
87 changes: 87 additions & 0 deletions src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.biscuitsec.biscuit.crypto;

import biscuit.format.schema.Schema;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.biscuitsec.biscuit.token.builder.Utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;

class Ed25519KeyPair extends KeyPair {

private final EdDSAPrivateKey privateKey;
private final EdDSAPublicKey publicKey;

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

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

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

this.privateKey = privKey;
this.publicKey = pubKey;
}

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

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

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

this.privateKey = privKey;
this.publicKey = pubKey;
}

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

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

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

@Override
public byte[] toBytes() {
return privateKey.getSeed();
}

@Override
public String toHex() {
return Utils.byteArrayToHexString(toBytes());
}

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

@Override
public PrivateKey private_key() {
return privateKey;
}

@Override
public PublicKey public_key() {
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.publicKey);
}
}
95 changes: 26 additions & 69 deletions src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
Original file line number Diff line number Diff line change
@@ -1,106 +1,63 @@
package org.biscuitsec.biscuit.crypto;


import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.EdDSAEngine;
import org.biscuitsec.biscuit.token.builder.Utils;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.*;
import net.i2p.crypto.eddsa.Utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Signature;

/**
* Private and public key
* Private and public key.
*/
public final class KeyPair {
public final EdDSAPrivateKey private_key;
public final EdDSAPublicKey public_key;
public abstract class KeyPair {

private static final int ED25519_PUBLIC_KEYSIZE = 32;
private static final int ED25519_PRIVATE_KEYSIZE = 64;
private static final int ED25519_SEED_SIZE = 32;
public static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);

public KeyPair() {
this(new SecureRandom());
public static KeyPair generate(Algorithm algorithm) {
return generate(algorithm, new SecureRandom());
}

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

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

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

this.private_key = privKey;
this.public_key = pubKey;
public static KeyPair generate(Algorithm algorithm, String hex) {
return generate(algorithm, Utils.hexToBytes(hex));
}

public static KeyPair generate(Algorithm algorithm) {
return generate(algorithm, new SecureRandom());
public static KeyPair generate(Algorithm algorithm, byte[] bytes) {
if (algorithm == Algorithm.Ed25519) {
return new Ed25519KeyPair(bytes);
} else if (algorithm == Algorithm.SECP256R1) {
return new SECP256R1KeyPair(bytes);
} else {
throw new IllegalArgumentException("Unsupported algorithm");
}
}

public static KeyPair generate(Algorithm algorithm, SecureRandom rng) {
if (algorithm == Algorithm.Ed25519) {
return new KeyPair(rng);
return new Ed25519KeyPair(rng);
} else if (algorithm == Algorithm.SECP256R1) {
return new SECP256R1KeyPair(rng);
} else {
throw new IllegalArgumentException("Unsupported algorithm");
}
}

public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException {
if (algorithm == Algorithm.Ed25519) {
return KeyPair.getSignature();
return Ed25519KeyPair.getSignature();
} else if (algorithm == Algorithm.SECP256R1) {
return SECP256R1KeyPair.getSignature();
} else {
throw new NoSuchAlgorithmException("Unsupported algorithm");
}
}

public byte[] toBytes() {
return this.private_key.getSeed();
}

public KeyPair(byte[] b) {
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);

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

this.private_key = privKey;
this.public_key = pubKey;
}

public String toHex() {
return Utils.byteArrayToHexString(this.toBytes());
}

public KeyPair(String hex) {
byte[] b = Utils.hexStringToByteArray(hex);

EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
public abstract byte[] toBytes();

EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
public abstract String toHex();

this.private_key = privKey;
this.public_key = pubKey;
}
public abstract java.security.PublicKey publicKey();

public static Signature getSignature() throws NoSuchAlgorithmException {
return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
}
public abstract java.security.PrivateKey private_key();

public PublicKey public_key() {
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.public_key);
}
public abstract PublicKey public_key();
}
50 changes: 37 additions & 13 deletions src/main/java/org/biscuitsec/biscuit/crypto/PublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,46 @@

import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import org.biscuitsec.biscuit.error.Error;
import org.biscuitsec.biscuit.token.builder.Utils;
import com.google.protobuf.ByteString;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import java.util.List;

import static org.biscuitsec.biscuit.crypto.KeyPair.ed25519;

public class PublicKey {

public final EdDSAPublicKey key;
public final java.security.PublicKey key;
public final Algorithm algorithm;

public PublicKey(Algorithm algorithm, EdDSAPublicKey public_key) {
private static final List<Algorithm> SUPPORTED_ALGORITHMS = List.of(Algorithm.Ed25519, Algorithm.SECP256R1);

public PublicKey(Algorithm algorithm, java.security.PublicKey public_key) {
this.key = public_key;
this.algorithm = algorithm;
}

public PublicKey(Algorithm algorithm, byte[] data) {
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(data, ed25519);
this.key = new EdDSAPublicKey(pubKeySpec);
if (algorithm == Algorithm.Ed25519) {
this.key = Ed25519KeyPair.decode(data);
} else if (algorithm == Algorithm.SECP256R1) {
this.key = SECP256R1KeyPair.decode(data);
} else {
throw new IllegalArgumentException("Invalid algorithm");
}
this.algorithm = algorithm;
}

public byte[] toBytes() {
return this.key.getAbyte();
if (algorithm == Algorithm.Ed25519) {
return ((EdDSAPublicKey) key).getAbyte();
} else if (algorithm == Algorithm.SECP256R1) {
return ((BCECPublicKey) key).getQ().getEncoded(true); // true = compressed
} else {
throw new IllegalArgumentException("Invalid algorithm");
}
}

public String toHex() {
Expand All @@ -36,8 +50,13 @@ public String toHex() {

public PublicKey(Algorithm algorithm, String hex) {
byte[] data = Utils.hexStringToByteArray(hex);
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(data, ed25519);
this.key = new EdDSAPublicKey(pubKeySpec);
if (algorithm == Algorithm.Ed25519) {
this.key = Ed25519KeyPair.decode(data);
} else if (algorithm == Algorithm.SECP256R1) {
this.key = SECP256R1KeyPair.decode(data);
} else {
throw new IllegalArgumentException("Invalid algorithm");
}
this.algorithm = algorithm;
}

Expand All @@ -49,10 +68,9 @@ public Schema.PublicKey serialize() {
}

static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError.DeserializationError {
if(!pk.hasAlgorithm() || !pk.hasKey() || pk.getAlgorithm() != Algorithm.Ed25519) {
if(!pk.hasAlgorithm() || !pk.hasKey() || !SUPPORTED_ALGORITHMS.contains(pk.getAlgorithm())) {
throw new Error.FormatError.DeserializationError("Invalid public key");
}

return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray());
}

Expand All @@ -73,6 +91,12 @@ public int hashCode() {

@Override
public String toString() {
return "ed25519/" + toHex().toLowerCase();
if (algorithm == Algorithm.Ed25519) {
return "ed25519/" + toHex().toLowerCase();
} else if (algorithm == Algorithm.SECP256R1) {
return "secp256r1/" + toHex().toLowerCase();
} else {
return null;
}
}
}
Loading