-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal: Add libsecp256k1 c library. (#2810)
Add a c library that has some primitive cryptographic functions needed for working with adaptor signatures.
- Loading branch information
1 parent
33a397b
commit 11992ad
Showing
7 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
### Package libsecp256k1 | ||
|
||
Package libsecp256k1 includes some primative cryptographic functions needed for | ||
working with adaptor signatures that are not currently found in golang. This imports | ||
code from https://github.com/tecnovert/secp256k1 and uses that with cgo. Both | ||
that library and this package are in an experimental stage. | ||
|
||
### Usage | ||
|
||
Run the `build.sh` script. Currently untested on mac and will not work on Windows. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/usr/bin/env bash | ||
|
||
rm -fr secp256k1 | ||
git clone https://github.com/tecnovert/secp256k1 -b anonswap_v0.2 | ||
|
||
cd secp256k1 | ||
./autogen.sh | ||
./configure --enable-module-dleag --enable-experimental --enable-module-generator --enable-module-ed25519 --enable-module-recovery --enable-module-ecdsaotves | ||
make | ||
cd .. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// This code is available on the terms of the project LICENSE.md file, | ||
// also available online at https://blueoakcouncil.org/license/1.0.0. | ||
|
||
package libsecp256k1 | ||
|
||
/* | ||
#cgo CFLAGS: -g -Wall | ||
#cgo LDFLAGS: -L. -l:secp256k1/.libs/libsecp256k1.a | ||
#include "secp256k1/include/secp256k1_dleag.h" | ||
#include "secp256k1/include/secp256k1_ecdsaotves.h" | ||
#include <stdlib.h> | ||
secp256k1_context* _ctx() { | ||
return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); | ||
} | ||
*/ | ||
import "C" | ||
import ( | ||
"errors" | ||
"unsafe" | ||
|
||
"decred.org/dcrdex/dex/encode" | ||
"github.com/decred/dcrd/dcrec/edwards/v2" | ||
"github.com/decred/dcrd/dcrec/secp256k1/v4" | ||
) | ||
|
||
const ( | ||
ProofLen = 48893 | ||
CTLen = 196 | ||
maxSigLen = 72 // The actual size is variable. | ||
) | ||
|
||
// Ed25519DleagProve creates a proof for checking a discrete logarithm is equal | ||
// across the secp256k1 and ed25519 curves. | ||
func Ed25519DleagProve(privKey *edwards.PrivateKey) (proof [ProofLen]byte, err error) { | ||
secpCtx := C._ctx() | ||
defer C.free(unsafe.Pointer(secpCtx)) | ||
nonce := [32]byte{} | ||
copy(nonce[:], encode.RandomBytes(32)) | ||
key := [32]byte{} | ||
copy(key[:], privKey.Serialize()) | ||
n := (*C.uchar)(unsafe.Pointer(&nonce)) | ||
k := (*C.uchar)(unsafe.Pointer(&key)) | ||
nBits := uint64(252) | ||
nb := (*C.ulong)(unsafe.Pointer(&nBits)) | ||
plen := C.ulong(ProofLen) | ||
p := (*C.uchar)(unsafe.Pointer(&proof)) | ||
res := C.secp256k1_ed25519_dleag_prove(secpCtx, p, &plen, k, *nb, n) | ||
if int(res) != 1 { | ||
return [ProofLen]byte{}, errors.New("C.secp256k1_ed25519_dleag_prove exited with error") | ||
} | ||
return proof, nil | ||
} | ||
|
||
// Ed25519DleagVerify verifies that a descrete logarithm is equal across the | ||
// secp256k1 and ed25519 curves. | ||
func Ed25519DleagVerify(proof [ProofLen]byte) bool { | ||
secpCtx := C._ctx() | ||
defer C.free(unsafe.Pointer(secpCtx)) | ||
pl := C.ulong(ProofLen) | ||
p := (*C.uchar)(unsafe.Pointer(&proof)) | ||
res := C.secp256k1_ed25519_dleag_verify(secpCtx, p, pl) | ||
return res == 1 | ||
} | ||
|
||
// EcdsaotvesEncSign signs the hash and returns an encrypted signature. | ||
func EcdsaotvesEncSign(signPriv *secp256k1.PrivateKey, encPub *secp256k1.PublicKey, hash [32]byte) (cyphertext [CTLen]byte, err error) { | ||
secpCtx := C._ctx() | ||
defer C.free(unsafe.Pointer(secpCtx)) | ||
privBytes := [32]byte{} | ||
copy(privBytes[:], signPriv.Serialize()) | ||
priv := (*C.uchar)(unsafe.Pointer(&privBytes)) | ||
pubBytes := [33]byte{} | ||
copy(pubBytes[:], encPub.SerializeCompressed()) | ||
pub := (*C.uchar)(unsafe.Pointer(&pubBytes)) | ||
h := (*C.uchar)(unsafe.Pointer(&hash)) | ||
s := (*C.uchar)(unsafe.Pointer(&cyphertext)) | ||
res := C.ecdsaotves_enc_sign(secpCtx, s, priv, pub, h) | ||
if int(res) != 1 { | ||
return [CTLen]byte{}, errors.New("C.ecdsaotves_enc_sign exited with error") | ||
} | ||
return cyphertext, nil | ||
} | ||
|
||
// EcdsaotvesEncVerify verifies the encrypted signature. | ||
func EcdsaotvesEncVerify(signPub, encPub *secp256k1.PublicKey, hash [32]byte, cyphertext [CTLen]byte) bool { | ||
secpCtx := C._ctx() | ||
defer C.free(unsafe.Pointer(secpCtx)) | ||
signBytes := [33]byte{} | ||
copy(signBytes[:], signPub.SerializeCompressed()) | ||
sp := (*C.uchar)(unsafe.Pointer(&signBytes)) | ||
encBytes := [33]byte{} | ||
copy(encBytes[:], encPub.SerializeCompressed()) | ||
ep := (*C.uchar)(unsafe.Pointer(&encBytes)) | ||
h := (*C.uchar)(unsafe.Pointer(&hash)) | ||
c := (*C.uchar)(unsafe.Pointer(&cyphertext)) | ||
res := C.ecdsaotves_enc_verify(secpCtx, sp, ep, h, c) | ||
return res == 1 | ||
} | ||
|
||
// EcdsaotvesDecSig retrieves the signature. | ||
func EcdsaotvesDecSig(encPriv *secp256k1.PrivateKey, cyphertext [CTLen]byte) ([]byte, error) { | ||
secpCtx := C._ctx() | ||
defer C.free(unsafe.Pointer(secpCtx)) | ||
encBytes := [32]byte{} | ||
copy(encBytes[:], encPriv.Serialize()) | ||
ep := (*C.uchar)(unsafe.Pointer(&encBytes)) | ||
ct := (*C.uchar)(unsafe.Pointer(&cyphertext)) | ||
var sig [maxSigLen]byte | ||
s := (*C.uchar)(unsafe.Pointer(&sig)) | ||
slen := C.ulong(maxSigLen) | ||
res := C.ecdsaotves_dec_sig(secpCtx, s, &slen, ep, ct) | ||
if int(res) != 1 { | ||
return nil, errors.New("C.ecdsaotves_dec_sig exited with error") | ||
} | ||
sigCopy := make([]byte, maxSigLen) | ||
copy(sigCopy, sig[:]) | ||
// Remove trailing zeros. | ||
for i := maxSigLen - 1; i >= 0; i-- { | ||
if sigCopy[i] != 0 { | ||
break | ||
} | ||
sigCopy = sigCopy[:i] | ||
} | ||
return sigCopy, nil | ||
} | ||
|
||
// EcdsaotvesRecEncKey retrieves the encoded private key from signature and | ||
// cyphertext. | ||
func EcdsaotvesRecEncKey(encPub *secp256k1.PublicKey, cyphertext [CTLen]byte, sig []byte) (encPriv *secp256k1.PrivateKey, err error) { | ||
secpCtx := C._ctx() | ||
defer C.free(unsafe.Pointer(secpCtx)) | ||
pubBytes := [33]byte{} | ||
copy(pubBytes[:], encPub.SerializeCompressed()) | ||
ep := (*C.uchar)(unsafe.Pointer(&pubBytes)) | ||
ct := (*C.uchar)(unsafe.Pointer(&cyphertext)) | ||
sigCopy := [maxSigLen]byte{} | ||
copy(sigCopy[:], sig) | ||
s := (*C.uchar)(unsafe.Pointer(&sigCopy)) | ||
varSigLen := len(sig) | ||
slen := C.ulong(varSigLen) | ||
pkBytes := [32]byte{} | ||
pk := (*C.uchar)(unsafe.Pointer(&pkBytes)) | ||
res := C.ecdsaotves_rec_enc_key(secpCtx, pk, ep, ct, s, slen) | ||
if int(res) != 1 { | ||
return nil, errors.New("C.ecdsaotves_rec_enc_key exited with error") | ||
} | ||
return secp256k1.PrivKeyFromBytes(pkBytes[:]), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
//go:build libsecp256k1 | ||
|
||
package libsecp256k1 | ||
|
||
import ( | ||
"bytes" | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/decred/dcrd/dcrec/edwards/v2" | ||
"github.com/decred/dcrd/dcrec/secp256k1/v4" | ||
) | ||
|
||
func randBytes(n int) []byte { | ||
b := make([]byte, n) | ||
rand.Read(b) | ||
return b | ||
} | ||
|
||
func TestEd25519DleagProve(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
}{{ | ||
name: "ok", | ||
}} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
pk, err := edwards.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
sPk := secp256k1.PrivKeyFromBytes(pk.Serialize()) | ||
proof, err := Ed25519DleagProve(pk) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !bytes.Equal(sPk.PubKey().SerializeCompressed(), proof[:33]) { | ||
t.Fatal("first 33 bytes of proof not equal to secp256k1 pubkey") | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEd25519DleagVerify(t *testing.T) { | ||
pk, err := edwards.GeneratePrivateKey() | ||
if err != nil { | ||
panic(err) | ||
} | ||
proof, err := Ed25519DleagProve(pk) | ||
if err != nil { | ||
panic(err) | ||
} | ||
tests := []struct { | ||
name string | ||
proof [ProofLen]byte | ||
ok bool | ||
}{{ | ||
name: "ok", | ||
proof: proof, | ||
ok: true, | ||
}, { | ||
name: "bad proof", | ||
proof: func() (p [ProofLen]byte) { | ||
copy(p[:], proof[:]) | ||
p[0] ^= p[0] | ||
return p | ||
}(), | ||
}} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
ok := Ed25519DleagVerify(test.proof) | ||
if ok != test.ok { | ||
t.Fatalf("want %v but got %v", test.ok, ok) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEcdsaotvesEncSign(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
}{{ | ||
name: "ok", | ||
}} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
signPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
encPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
h := randBytes(32) | ||
var hash [32]byte | ||
copy(hash[:], h) | ||
_, err = EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEcdsaotvesEncVerify(t *testing.T) { | ||
signPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
encPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
h := randBytes(32) | ||
var hash [32]byte | ||
copy(hash[:], h) | ||
ct, err := EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
tests := []struct { | ||
name string | ||
ok bool | ||
ct [196]byte | ||
}{{ | ||
name: "ok", | ||
ct: ct, | ||
ok: true, | ||
}, { | ||
name: "bad sig", | ||
ct: func() (c [CTLen]byte) { | ||
copy(c[:], ct[:]) | ||
c[0] ^= c[0] | ||
return c | ||
}(), | ||
}} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
ok := EcdsaotvesEncVerify(signPk.PubKey(), encPk.PubKey(), hash, test.ct) | ||
if ok != test.ok { | ||
t.Fatalf("want %v but got %v", test.ok, ok) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEcdsaotvesDecSig(t *testing.T) { | ||
signPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
encPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
h := randBytes(32) | ||
var hash [32]byte | ||
copy(hash[:], h) | ||
ct, err := EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
tests := []struct { | ||
name string | ||
}{{ | ||
name: "ok", | ||
}} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
_, err := EcdsaotvesDecSig(encPk, ct) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEcdsaotvesRecEncKey(t *testing.T) { | ||
signPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
encPk, err := secp256k1.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
h := randBytes(32) | ||
var hash [32]byte | ||
copy(hash[:], h) | ||
ct, err := EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
sig, err := EcdsaotvesDecSig(encPk, ct) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
tests := []struct { | ||
name string | ||
}{{ | ||
name: "ok", | ||
}} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
pk, err := EcdsaotvesRecEncKey(encPk.PubKey(), ct, sig) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !bytes.Equal(pk.Serialize(), encPk.Serialize()) { | ||
t.Fatal("private keys not equal") | ||
} | ||
}) | ||
} | ||
} |
Submodule secp256k1
added at
e3ebcd
Oops, something went wrong.