Skip to content

add go wrap of this lib #221

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

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
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/jestan/easy-ecc

go 1.21.3

require (
github.com/petermattis/fastcgo v0.0.0-20170816164731-e33892440abb // indirect
golang.org/x/crypto v0.15.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/petermattis/fastcgo v0.0.0-20170816164731-e33892440abb h1:NO9wJiBhQI0rgcuo5m4QLVe2lFDZxiI7OuQmFlScFC0=
github.com/petermattis/fastcgo v0.0.0-20170816164731-e33892440abb/go.mod h1:5CA9I6tKHPWMxtx7rxplJ5ScFukOelD7qe06IL83a7I=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
32 changes: 32 additions & 0 deletions gomicroecc/cgo_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package gomicroecc

/*
#define _NEED_SIGN_VERRIFY
#include "go_wrap.h"
*/
import "C"
import (
"unsafe"

"github.com/petermattis/fastcgo"
)

func Sign(out []byte, privateKey []byte, hash []byte) {
fastcgo.UnsafeCall4(C.sign,
uint64(uintptr(unsafe.Pointer(&out))),
uint64(uintptr(unsafe.Pointer(&privateKey))),
uint64(uintptr(unsafe.Pointer(&hash))),
0,
)
}

func Verify(publicKey []byte, hash []byte, signature []byte) bool {
var ret int64
fastcgo.UnsafeCall4(C.verify,
uint64(uintptr(unsafe.Pointer(&publicKey))),
uint64(uintptr(unsafe.Pointer(&hash))),
uint64(uintptr(unsafe.Pointer(&signature))),
uint64(uintptr(unsafe.Pointer(&ret))),
)
return ret != 0
}
29 changes: 29 additions & 0 deletions gomicroecc/cgo_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package gomicroecc

/*
#define _NEED_SIGN_VERRIFY
#include "go_wrap.h"
*/
import "C"
import (
"unsafe"
)

func Sign(out []byte, privateKey []byte, hash []byte) {
C.sign(
(*C.SliceHeader)(unsafe.Pointer(&out)),
(*C.SliceHeader)(unsafe.Pointer(&privateKey)),
(*C.SliceHeader)(unsafe.Pointer(&hash)),
)
}

func Verify(publicKey []byte, hash []byte, signature []byte) bool {
var ret C.longlong
C.verify(
(*C.SliceHeader)(unsafe.Pointer(&publicKey)),
(*C.SliceHeader)(unsafe.Pointer(&hash)),
(*C.SliceHeader)(unsafe.Pointer(&signature)),
&ret,
)
return ret != 0
}
42 changes: 42 additions & 0 deletions gomicroecc/cmd/example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"encoding/hex"
"log"
"os"

"github.com/jestan/easy-ecc/gomicroecc"
)

func main() {
s, err := os.ReadFile("./private_key.txt")
if err != nil {
log.Fatalln(err.Error())
}
privateKey, err := gomicroecc.ParsePrivateKey(string(s))
if err != nil {
log.Fatalln(err.Error())
}
sha256Value, err := hex.DecodeString("758a18de504f9aed6e23a203cdb1d207cb86571d84c41e1f66c1deec48c01de5")
if err != nil {
log.Fatalln(err.Error())
}
//
outSign := make([]byte, 64)
gomicroecc.Sign(outSign, privateKey.D.Bytes(), sha256Value)
golangSign, err := gomicroecc.EncodeSignature(outSign)
if err != nil {
log.Fatalln(err.Error())
}
log.Println("sign:", hex.EncodeToString(golangSign))
//
publicKeyBytes := make([]byte, 0, 64)
publicKeyBytes = append(publicKeyBytes, privateKey.PublicKey.X.Bytes()...)
publicKeyBytes = append(publicKeyBytes, privateKey.PublicKey.Y.Bytes()...)
ret := gomicroecc.Verify(publicKeyBytes, sha256Value, outSign)
if !ret {
log.Println("Verify fail")
return
}
log.Println("Verify ok")
}
5 changes: 5 additions & 0 deletions gomicroecc/cmd/example/private_key.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTRK9HOn+vaDmUqnQ
gBqPaavgIi7plGQmgyfZO/rN4mqhRANCAATW9d/H1AvF5Uqz9HhFvmQqi8+tdkeB
kgNcjnZpWzJ5NhSABXMi4sUJRL7wMTAWvDeg9TMqt7cPYfcpDUVOe1k5
-----END PRIVATE KEY-----
21 changes: 21 additions & 0 deletions gomicroecc/cmd/gen_keys/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"

"github.com/jestan/easy-ecc/gomicroecc"
)

func main() {
privateKeyPEM, publicKeyPEM, err := gomicroecc.GenerateECDSAP256KeyPair()
if err != nil {
fmt.Println("Error:", err)
return
}

fmt.Println("Private Key (PKCS#8 PEM):")
fmt.Println(privateKeyPEM)

fmt.Println("\nPublic Key (PEM):")
fmt.Println(publicKeyPEM)
}
97 changes: 97 additions & 0 deletions gomicroecc/ecc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package gomicroecc

/*
#define _NEED_INIT
#include "go_wrap.h"
#include "../uECC.c"
*/
import "C"
import (
"errors"
"fmt"

"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"

"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
)

func init() {
C.init_ecc()
}

// GenerateECDSAP256KeyPair generates an ECDSA P-256 key pair and returns them as PEM-encoded strings.
func GenerateECDSAP256KeyPair() (string, string, error) {
// Generate a new ECDSA P-256 private key
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return "", "", err
}

// Encode the private key as PKCS#8 PEM
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return "", "", err
}
privateKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateKeyBytes})

// Encode the public key as PEM
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return "", "", err
}
publicKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: publicKeyBytes})

return string(privateKeyPEM), string(publicKeyPEM), nil
}

func ParsePrivateKey(s string) (*ecdsa.PrivateKey, error) {
privateKeyPEMBlock, _ := pem.Decode([]byte(s))
if privateKeyPEMBlock == nil {
return nil, errors.New("decode error")
}
var err error
var privateKeyAny any
privateKeyAny, err = x509.ParsePKCS8PrivateKey(privateKeyPEMBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("x509.ParsePKCS8PrivateKey error, err=%s", err.Error())
}
return privateKeyAny.(*ecdsa.PrivateKey), nil
}

// addASN1IntBytes encodes in ASN.1 a positive integer represented as
// a big-endian byte slice with zero or more leading zeroes.
// copy from crypto/ecdsa
func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) {
for len(bytes) > 0 && bytes[0] == 0 {
bytes = bytes[1:]
}
if len(bytes) == 0 {
b.SetError(errors.New("invalid integer"))
return
}
b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) {
if bytes[0]&0x80 != 0 {
c.AddUint8(0)
}
c.AddBytes(bytes)
})
}

// EncodeSignature encode signature as golang format
// copy from crypto/ecdsa
func EncodeSignature(signature []byte) ([]byte, error) {
if len(signature) != 64 {
return nil, errors.New("signature len must be 64")
}
var b cryptobyte.Builder
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
addASN1IntBytes(b, signature[:32])
addASN1IntBytes(b, signature[32:])
})
return b.Bytes()
}
36 changes: 36 additions & 0 deletions gomicroecc/go_wrap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef _GOLANG_WRAP_H_
#define _GOLANG_WRAP_H_

#include "../uECC.h"

#ifdef _NEED_INIT
const struct uECC_Curve_t *curve = NULL;

void init_ecc() { curve = uECC_secp256r1(); }
#endif

typedef struct {
uint64_t ptr;
uint64_t len;
uint64_t cap;
} __attribute__((packed)) SliceHeader;

#ifdef _NEED_SIGN_VERRIFY
extern const struct uECC_Curve_t *curve;

void sign(SliceHeader *out, const SliceHeader *private_key,
const SliceHeader *message_hash) {
uECC_sign((const uint8_t *)private_key->ptr,
(const uint8_t *)message_hash->ptr, message_hash->len,
(uint8_t *)out->ptr, curve);
}

void verify(const SliceHeader *public_key, const SliceHeader *message_hash,
const SliceHeader *signature, int64_t *ret) {
*ret = uECC_verify((const uint8_t *)public_key->ptr,
(const uint8_t *)message_hash->ptr, message_hash->len,
(const uint8_t *)signature->ptr, curve);
}
#endif

#endif