@ -16,8 +16,15 @@ package org.hyperledger.besu.crypto;
import static com.google.common.base.Preconditions.checkArgument ;
import static com.google.common.base.Preconditions.checkNotNull ;
import static org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1.SECP256K1_EC_UNCOMPRESSED ;
import org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1 ;
import org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1.secp256k1_ecdsa_recoverable_signature ;
import org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1.secp256k1_ecdsa_signature ;
import org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1.secp256k1_pubkey ;
import java.math.BigInteger ;
import java.nio.ByteBuffer ;
import java.security.InvalidAlgorithmParameterException ;
import java.security.KeyPairGenerator ;
import java.security.Security ;
@ -25,8 +32,14 @@ import java.security.spec.ECGenParameterSpec;
import java.util.Arrays ;
import java.util.Objects ;
import java.util.Optional ;
import java.util.function.Supplier ;
import java.util.function.UnaryOperator ;
import com.google.common.base.Suppliers ;
import com.sun.jna.ptr.IntByReference ;
import com.sun.jna.ptr.LongByReference ;
import org.apache.logging.log4j.LogManager ;
import org.apache.logging.log4j.Logger ;
import org.apache.tuweni.bytes.Bytes ;
import org.apache.tuweni.bytes.Bytes32 ;
import org.apache.tuweni.bytes.MutableBytes ;
@ -59,6 +72,10 @@ import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve;
* /
public class SECP256K1 {
private static final Logger LOG = LogManager . getLogger ( ) ;
private static boolean useNative = false ;
public static final String ALGORITHM = "ECDSA" ;
public static final String CURVE_NAME = "secp256k1" ;
public static final String PROVIDER = "BC" ;
@ -89,6 +106,38 @@ public class SECP256K1 {
}
}
public static void enableNative ( ) {
useNative = LibSecp256k1 . CONTEXT ! = null ;
LOG . info ( useNative ? "Using native secp256k1" : "Native secp256k1 requested but not available" ) ;
}
public static Signature sign ( final Bytes32 dataHash , final KeyPair keyPair ) {
if ( useNative ) {
return signNative ( dataHash , keyPair ) ;
} else {
return signDefault ( dataHash , keyPair ) ;
}
}
/ * *
* Verifies the given ECDSA signature against the message bytes using the public key bytes .
*
* < p > When using native ECDSA verification , data must be 32 bytes , and no element may be larger
* than 520 bytes .
*
* @param data Hash of the data to verify .
* @param signature ASN . 1 encoded signature .
* @param pub The public key bytes to use .
* @return True if the verification is successful .
* /
public static boolean verify ( final Bytes data , final Signature signature , final PublicKey pub ) {
if ( useNative ) {
return verifyNative ( data , signature , pub ) ;
} else {
return verifyDefault ( data , signature , pub ) ;
}
}
/** Decompress a compressed public key (x co-ord and low-bit of y-coord). */
private static ECPoint decompressKey ( final BigInteger xBN , final boolean yBit ) {
final X9IntegerConverter x9 = new X9IntegerConverter ( ) ;
@ -178,7 +227,7 @@ public class SECP256K1 {
return new BigInteger ( 1 , Arrays . copyOfRange ( qBytes , 1 , qBytes . length ) ) ;
}
public static Signature sign ( final Bytes32 dataHash , final KeyPair keyPair ) {
private static Signature signDefault ( final Bytes32 dataHash , final KeyPair keyPair ) {
final ECDSASigner signer = new ECDSASigner ( new HMacDSAKCalculator ( new SHA256Digest ( ) ) ) ;
final ECPrivateKeyParameters privKey =
@ -232,18 +281,8 @@ public class SECP256K1 {
return new Signature ( nativeR , s , ( byte ) recId ) ;
}
/ * *
* Verifies the given ECDSA signature against the message bytes using the public key bytes .
*
* < p > When using native ECDSA verification , data must be 32 bytes , and no element may be larger
* than 520 bytes .
*
* @param data Hash of the data to verify .
* @param signature ASN . 1 encoded signature .
* @param pub The public key bytes to use .
* @return True if the verification is successful .
* /
public static boolean verify ( final Bytes data , final Signature signature , final PublicKey pub ) {
private static boolean verifyDefault (
final Bytes data , final Signature signature , final PublicKey pub ) {
final ECDSASigner signer = new ECDSASigner ( ) ;
final Bytes toDecode = Bytes . wrap ( Bytes . of ( ( byte ) 4 ) , pub . getEncodedBytes ( ) ) ;
final ECPublicKeyParameters params =
@ -301,6 +340,95 @@ public class SECP256K1 {
return UInt256 . valueOf ( agreed ) . toBytes ( ) ;
}
private static Signature signNative ( final Bytes32 dataHash , final KeyPair keyPair ) {
final LibSecp256k1 . secp256k1_ecdsa_recoverable_signature signature =
new secp256k1_ecdsa_recoverable_signature ( ) ;
// sign in internal form
if ( LibSecp256k1 . secp256k1_ecdsa_sign_recoverable (
LibSecp256k1 . CONTEXT ,
signature ,
dataHash . toArrayUnsafe ( ) ,
keyPair . privateKey . getEncoded ( ) ,
null ,
null )
= = 0 ) {
throw new RuntimeException (
"Could not natively sign. Private Key is invalid or default nonce generation failed." ) ;
}
// encode to compact form
final ByteBuffer compactSig = ByteBuffer . allocate ( 64 ) ;
final IntByReference recId = new IntByReference ( 0 ) ;
LibSecp256k1 . secp256k1_ecdsa_recoverable_signature_serialize_compact (
LibSecp256k1 . CONTEXT , compactSig , recId , signature ) ;
compactSig . flip ( ) ;
final byte [ ] sig = compactSig . array ( ) ;
// wrap in signature object
final Bytes32 r = Bytes32 . wrap ( sig , 0 ) ;
final Bytes32 s = Bytes32 . wrap ( sig , 32 ) ;
return Signature . create (
r . toUnsignedBigInteger ( ) , s . toUnsignedBigInteger ( ) , ( byte ) recId . getValue ( ) ) ;
}
private static boolean verifyNative (
final Bytes data , final Signature signature , final PublicKey pub ) {
// translate signature
final LibSecp256k1 . secp256k1_ecdsa_signature _signature = new secp256k1_ecdsa_signature ( ) ;
if ( LibSecp256k1 . secp256k1_ecdsa_signature_parse_compact (
LibSecp256k1 . CONTEXT , _signature , signature . encodedBytes ( ) . toArrayUnsafe ( ) )
= = 0 ) {
throw new IllegalArgumentException ( "Could not parse signature" ) ;
}
// translate key
final LibSecp256k1 . secp256k1_pubkey _pub = new secp256k1_pubkey ( ) ;
final Bytes encodedPubKey = Bytes . concatenate ( Bytes . of ( 0x04 ) , pub . getEncodedBytes ( ) ) ;
if ( LibSecp256k1 . secp256k1_ec_pubkey_parse (
LibSecp256k1 . CONTEXT , _pub , encodedPubKey . toArrayUnsafe ( ) , encodedPubKey . size ( ) )
= = 0 ) {
throw new IllegalArgumentException ( "Could not parse public key" ) ;
}
return LibSecp256k1 . secp256k1_ecdsa_verify (
LibSecp256k1 . CONTEXT , _signature , data . toArrayUnsafe ( ) , _pub )
! = 0 ;
}
private static Optional < PublicKey > recoverFromSignatureNative (
final Bytes32 dataHash , final Signature signature ) {
// parse the sig
final LibSecp256k1 . secp256k1_ecdsa_recoverable_signature parsedSignature =
new LibSecp256k1 . secp256k1_ecdsa_recoverable_signature ( ) ;
final Bytes encodedSig = signature . encodedBytes ( ) ;
if ( LibSecp256k1 . secp256k1_ecdsa_recoverable_signature_parse_compact (
LibSecp256k1 . CONTEXT ,
parsedSignature ,
encodedSig . slice ( 0 , 64 ) . toArrayUnsafe ( ) ,
encodedSig . get ( 64 ) )
= = 0 ) {
throw new IllegalArgumentException ( "Could not parse signature" ) ;
}
// recover the key
final LibSecp256k1 . secp256k1_pubkey newPubKey = new LibSecp256k1 . secp256k1_pubkey ( ) ;
if ( LibSecp256k1 . secp256k1_ecdsa_recover (
LibSecp256k1 . CONTEXT , newPubKey , parsedSignature , dataHash . toArrayUnsafe ( ) )
= = 0 ) {
throw new IllegalArgumentException ( "Could not recover public key" ) ;
}
// parse the key
final ByteBuffer recoveredKey = ByteBuffer . allocate ( 65 ) ;
final LongByReference keySize = new LongByReference ( recoveredKey . limit ( ) ) ;
LibSecp256k1 . secp256k1_ec_pubkey_serialize (
LibSecp256k1 . CONTEXT , recoveredKey , keySize , newPubKey , SECP256K1_EC_UNCOMPRESSED ) ;
return Optional . of ( PublicKey . create ( Bytes . wrapByteBuffer ( recoveredKey ) . slice ( 1 ) ) ) ;
}
public static class PrivateKey implements java . security . PrivateKey {
private final Bytes32 encoded ;
@ -319,10 +447,6 @@ public class SECP256K1 {
return new PrivateKey ( key ) ;
}
public ECPoint asEcPoint ( ) {
return CURVE . getCurve ( ) . decodePoint ( encoded . toArrayUnsafe ( ) ) ;
}
@Override
public boolean equals ( final Object other ) {
if ( ! ( other instanceof PrivateKey ) ) {
@ -411,10 +535,14 @@ public class SECP256K1 {
public static Optional < PublicKey > recoverFromSignature (
final Bytes32 dataHash , final Signature signature ) {
final BigInteger publicKeyBI =
SECP256K1 . recoverFromSignature (
signature . getRecId ( ) , signature . getR ( ) , signature . getS ( ) , dataHash ) ;
return Optional . ofNullable ( publicKeyBI ) . map ( PublicKey : : create ) ;
if ( useNative ) {
return recoverFromSignatureNative ( dataHash , signature ) ;
} else {
final BigInteger publicKeyBI =
SECP256K1 . recoverFromSignature (
signature . getRecId ( ) , signature . getR ( ) , signature . getS ( ) , dataHash ) ;
return Optional . ofNullable ( publicKeyBI ) . map ( PublicKey : : create ) ;
}
}
private PublicKey ( final Bytes encoded ) {
@ -552,7 +680,9 @@ public class SECP256K1 {
private final BigInteger r ;
private final BigInteger s ;
private Signature ( final BigInteger r , final BigInteger s , final byte recId ) {
private final Supplier < Bytes > encoded = Suppliers . memoize ( this : : _encodedBytes ) ;
Signature ( final BigInteger r , final BigInteger s , final byte recId ) {
this . r = r ;
this . s = s ;
this . recId = recId ;
@ -604,6 +734,10 @@ public class SECP256K1 {
}
public Bytes encodedBytes ( ) {
return encoded . get ( ) ;
}
private Bytes _encodedBytes ( ) {
final MutableBytes bytes = MutableBytes . create ( BYTES_REQUIRED ) ;
UInt256 . valueOf ( r ) . toBytes ( ) . copyTo ( bytes , 0 ) ;
UInt256 . valueOf ( s ) . toBytes ( ) . copyTo ( bytes , 32 ) ;