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

internal: Add libsecp256k1 c library. #2810

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ client/cmd/translationsreport/translationsreport
client/cmd/translationsreport/worksheets
server/cmd/dexadm/dexadm
server/cmd/geogame/geogame
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
Loading