Skip to content

Commit

Permalink
Merge pull request #108 from itstheceo/ec_p256
Browse files Browse the repository at this point in the history
Add ECDSA key support (SECP256R1) again
  • Loading branch information
Korbik authored Jan 17, 2025
2 parents 4e91e47 + a27f89b commit 20f3907
Show file tree
Hide file tree
Showing 30 changed files with 679 additions and 2,552 deletions.
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.79</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
89 changes: 89 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,89 @@
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;

final class Ed25519KeyPair extends KeyPair {

static final int SIGNATURE_LENGTH = 64;

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();
}
66 changes: 53 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 static org.biscuitsec.biscuit.crypto.KeyPair.ed25519;
import java.util.Optional;
import java.util.Set;

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 Set<Algorithm> SUPPORTED_ALGORITHMS = Set.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,13 +68,28 @@ 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());
}

public static Optional<Error> validateSignatureLength(Algorithm algorithm, int length) {
Optional<Error> error = Optional.empty();
if (algorithm == Algorithm.Ed25519) {
if (length != Ed25519KeyPair.SIGNATURE_LENGTH) {
error = Optional.of(new Error.FormatError.Signature.InvalidSignatureSize(length));
}
} else if (algorithm == Algorithm.SECP256R1) {
if (length < SECP256R1KeyPair.MINIMUM_SIGNATURE_LENGTH || length > SECP256R1KeyPair.MAXIMUM_SIGNATURE_LENGTH) {
error = Optional.of(new Error.FormatError.Signature.InvalidSignatureSize(length));
}
} else {
error = Optional.of(new Error.FormatError.Signature.InvalidSignature("unsupported algorithm"));
}
return error;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -73,6 +107,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

0 comments on commit 20f3907

Please sign in to comment.