Skip to content

Commit

Permalink
internal: Add libsecp256k1 c library.
Browse files Browse the repository at this point in the history
Add a c library that has some primitive cryptographic functions needed
for working with adaptor signatures.
  • Loading branch information
JoeGruffins committed Aug 16, 2024
1 parent b2addc7 commit e67b1dc
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ server/cmd/validatemarkets
client/cmd/translationsreport/translationsreport
client/cmd/translationsreport/worksheets
server/cmd/dexadm/dexadm
internal/libsecp256k1/secp256k1
10 changes: 10 additions & 0 deletions internal/libsecp256k1/README.md
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.
10 changes: 10 additions & 0 deletions internal/libsecp256k1/build.sh
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 ..
149 changes: 149 additions & 0 deletions internal/libsecp256k1/libsecp256k1.go
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
}
215 changes: 215 additions & 0 deletions internal/libsecp256k1/libsecp256k1_test.go
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")
}
})
}
}
1 change: 1 addition & 0 deletions internal/libsecp256k1/secp256k1
Submodule secp256k1 added at e3ebcd
Loading

0 comments on commit e67b1dc

Please sign in to comment.