Skip to content
This repository has been archived by the owner on Feb 9, 2021. It is now read-only.

Add C interoperability for Go lang #6

Open
wants to merge 6 commits 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
35 changes: 35 additions & 0 deletions go/zkscrypto/example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"encoding/hex"
"log"

"github.com/matter-labs/zksync-sdk/go/zkscrypto"
)

func main() {
seed := make([]byte, 32)
message := []byte("hello")

privateKey, err := zkscrypto.NewPrivateKey(seed)
if err != nil {
log.Fatalf("error creating private key: %s", err.Error())
}
publicKey, err := privateKey.PublicKey()
if err != nil {
log.Fatalf("error creating public key: %s", err.Error())
}
publicKeyHash, err := publicKey.Hash()
if err != nil {
log.Fatalf("error creating public key hash: %s", err.Error())
}
signature, err := privateKey.Sign(message)
if err != nil {
log.Fatalf("error signing message: %s", err.Error())
}
log.Printf("Seed: %s\n", hex.EncodeToString(seed))
log.Printf("Private key: %s\n", privateKey.HexString())
log.Printf("Public key: %s\n", publicKey.HexString())
log.Printf("Public key hash: %s\n", publicKeyHash.HexString())
log.Printf("Signature: %s\n", signature.HexString())
}
3 changes: 3 additions & 0 deletions go/zkscrypto/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/matter-labs/zksync-sdk/go/zkscrypto

go 1.15
21 changes: 21 additions & 0 deletions go/zkscrypto/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package zkscrypto

// PrivateKey represents a private key.
type PrivateKey struct {
data []byte
}

// PublicKey represents a public key
type PublicKey struct {
data []byte
}

// PublicKeyHash represents a public key hash
type PublicKeyHash struct {
data []byte
}

// Signature represents a multi-signature
type Signature struct {
data []byte
}
106 changes: 106 additions & 0 deletions go/zkscrypto/zks_crypto.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#ifndef ZKS_CRYPTO_H
#define ZKS_CRYPTO_H

/* Generated with cbindgen:0.14.3 */

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>


/**
* Maximum byte length of the message that can be signed.
*/
#define MAX_SIGNED_MESSAGE_LEN 92

/**
* Byte length of the signature. Signature contains r and s points.
*/
#define PACKED_SIGNATURE_LEN 64

/**
* Byte length of the private key
*/
#define PRIVATE_KEY_LEN 32

/**
* Byte length of the public key hash
*/
#define PUBKEY_HASH_LEN 20

/**
* Byte length of the public key
*/
#define PUBLIC_KEY_LEN 32

typedef enum MUSIG_SIGN_RES {
MUSIG_SIGN_OK = 0,
MUSIG_SIGN_MSG_TOO_LONG,
} MUSIG_SIGN_RES;

typedef enum PRIVATE_KEY_FROM_SEED_RES {
PRIVATE_KEY_FROM_SEED_OK = 0,
/**
* Seed should be at least 32 bytes long
*/
PRIVATE_KEY_FROM_SEED_SEED_TOO_SHORT,
} PRIVATE_KEY_FROM_SEED_RES;

typedef enum PUBKEY_HASH_FROM_PUBKEY_RES {
PUBKEY_HASH_FROM_PUBKEY_OK = 0,
} PUBKEY_HASH_FROM_PUBKEY_RES;

typedef enum PUBLIC_KEY_FROM_PRIVATE_RES {
PUBLIC_KEY_FROM_PRIVATE_OK = 0,
} PUBLIC_KEY_FROM_PRIVATE_RES;

typedef struct ZksPrivateKey {
uint8_t data[PRIVATE_KEY_LEN];
} ZksPrivateKey;

typedef struct ZksPackedPublicKey {
uint8_t data[PUBLIC_KEY_LEN];
} ZksPackedPublicKey;

typedef struct ZksPubkeyHash {
uint8_t data[PUBKEY_HASH_LEN];
} ZksPubkeyHash;

typedef struct ZksSignature {
uint8_t data[PACKED_SIGNATURE_LEN];
} ZksSignature;

/**
* Initializes thread local storage of the parameters used for calculations.
* Calling this before other calls is optional since parameters will be initialized when needed.
* Can save time for the first call of other functions in the thread
* since it takes time to init parameters.
*/
void zks_crypto_init(void);

PRIVATE_KEY_FROM_SEED_RES zks_crypto_private_key_from_seed(const uint8_t *seed,
size_t seed_len,
ZksPrivateKey *private_key);

PUBLIC_KEY_FROM_PRIVATE_RES zks_crypto_private_key_to_public_key(const ZksPrivateKey *private_key,
ZksPackedPublicKey *public_key);

PUBKEY_HASH_FROM_PUBKEY_RES zks_crypto_public_key_to_pubkey_hash(const ZksPackedPublicKey *public_key,
ZksPubkeyHash *pubkey_hash);

/**
* We use musig Schnorr signature scheme.
* It is impossible to restore signer for signature, that is why we provide public key of the signer
* along with signature.
* [0..32] - packed r point of the signature.
* [32..64] - s point of the signature.
*/
MUSIG_SIGN_RES zks_crypto_sign_musig(const ZksPrivateKey *private_key,
const uint8_t *msg,
size_t msg_len,
ZksSignature *signature_output);

#endif /* ZKS_CRYPTO_H */
150 changes: 150 additions & 0 deletions go/zkscrypto/zkscrypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package zkscrypto

/*
#cgo LDFLAGS: -L${SRCDIR}/libs -lzks_crypto

#include "zks_crypto.h"
*/
import "C"
import (
"encoding/hex"
"errors"
"unsafe"
)

var (
errSeedLen = errors.New("given seed is too short, length must be greater than 32")
errPrivateKey = errors.New("error on private key generation")
errSignedMsgLen = errors.New("musig message length must not be larger than 92")
errSign = errors.New("error on sign message")
)

func init() {
C.zks_crypto_init()
}

/*
************************************************************************************************
Private key implementation
************************************************************************************************
*/

// NewPrivateKey generates private key from seed
func NewPrivateKey(seed []byte) (*PrivateKey, error) {
pointer := C.struct_ZksPrivateKey{}
rawSeed := C.CBytes(seed)
defer C.free(rawSeed)
result := C.zks_crypto_private_key_from_seed((*C.uint8_t)(rawSeed), C.ulong(len(seed)), &pointer)
if result != 0 {
switch result {
case 1:
return nil, errSeedLen
default:
return nil, errPrivateKey
}
}
data := unsafe.Pointer(&pointer.data)
return &PrivateKey{data: C.GoBytes(data, C.PRIVATE_KEY_LEN)}, nil
}

// Sign message with musig Schnorr signature scheme
func (pk *PrivateKey) Sign(message []byte) (*Signature, error) {
privateKeyC := C.struct_ZksPrivateKey{}
rawMessage := C.CBytes(message)
defer C.free(rawMessage)
for i := range pk.data {
privateKeyC.data[i] = C.uint8_t(pk.data[i])
}
signatureC := C.struct_ZksSignature{}
result := C.zks_crypto_sign_musig(&privateKeyC, (*C.uint8_t)(rawMessage), C.ulong(len(message)), &signatureC)
if result != 0 {
switch result {
case 1:
return nil, errSignedMsgLen
default:
return nil, errSign
}
}
data := unsafe.Pointer(&signatureC.data)
return &Signature{data: C.GoBytes(data, C.PACKED_SIGNATURE_LEN)}, nil
}

// PublicKey generates public key from private key
func (pk *PrivateKey) PublicKey() (*PublicKey, error) {
privateKeyC := C.struct_ZksPrivateKey{}
for i := range pk.data {
privateKeyC.data[i] = C.uint8_t(pk.data[i])
}
pointer := C.struct_ZksPackedPublicKey{}
result := C.zks_crypto_private_key_to_public_key(&privateKeyC, &pointer)
if result != 0 {
return nil, errors.New("error on public key generation")
}
data := unsafe.Pointer(&pointer.data)
return &PublicKey{data: C.GoBytes(data, C.PUBLIC_KEY_LEN)}, nil
}

// HexString creates a hex string representation of a private key
func (pk *PrivateKey) HexString() string {
if pk.data == nil || len(pk.data) == 0 {
return "0x"
}
return hex.EncodeToString(pk.data)
}

/*
************************************************************************************************
Public key implementation
************************************************************************************************
*/

// Hash generates hash from public key
func (pk *PublicKey) Hash() (*PublicKeyHash, error) {
publicKeyC := C.struct_ZksPackedPublicKey{}
for i := range pk.data {
publicKeyC.data[i] = C.uint8_t(pk.data[i])
}
pointer := C.struct_ZksPubkeyHash{}
result := C.zks_crypto_public_key_to_pubkey_hash(&publicKeyC, &pointer)
if result != 0 {
return nil, errors.New("Error on public key hash generation")
}
data := unsafe.Pointer(&pointer.data)
return &PublicKeyHash{data: C.GoBytes(data, C.PUBKEY_HASH_LEN)}, nil
}

// HexString creates a hex string representation of a public key
func (pk *PublicKey) HexString() string {
if pk.data == nil || len(pk.data) == 0 {
return "0x"
}
return hex.EncodeToString(pk.data)
}

/*
************************************************************************************************
Private key Hash implementation
************************************************************************************************
*/

// HexString creates a hex string representation of a public key hash
func (pk *PublicKeyHash) HexString() string {
if pk.data == nil || len(pk.data) == 0 {
return "0x"
}
return hex.EncodeToString(pk.data)
}

/*
************************************************************************************************
Signature implementation
************************************************************************************************
*/

// HexString creates a hex string representation of a signature
func (pk *Signature) HexString() string {
if pk.data == nil || len(pk.data) == 0 {
return "0x"
}
return hex.EncodeToString(pk.data)
}
61 changes: 61 additions & 0 deletions go/zkscrypto/zkscrypto_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package zkscrypto

import (
"bytes"
"testing"
)

func TestPrivateKeyGeneration(t *testing.T) {
seed := make([]byte, 32)
expected := []byte{1, 31, 91, 153, 8, 76, 92, 46, 45, 94, 99, 72, 142, 15, 113, 104, 213, 153, 165, 192, 31, 233, 254, 196, 201, 150, 5, 116, 61, 165, 232, 92}
privateKey, err := NewPrivateKey(seed)
if err != nil {
t.Fatalf("%s", err.Error())
}
if !bytes.Equal(privateKey.data, expected) {
t.Fatalf("%s,%v,%v must be equal to %v", "Unexpected private key", seed, privateKey.data, expected)
}
}

func TestPublicKeyGenerationFromPrivateKey(t *testing.T) {
privateKeyRaw := []byte{1, 31, 91, 153, 8, 76, 92, 46, 45, 94, 99, 72, 142, 15, 113, 104, 213, 153, 165, 192, 31, 233, 254, 196, 201, 150, 5, 116, 61, 165, 232, 92}
expected := []byte{23, 156, 58, 89, 20, 125, 48, 49, 108, 136, 102, 40, 133, 35, 72, 201, 180, 42, 24, 184, 33, 8, 74, 201, 239, 121, 189, 115, 233, 185, 78, 141}
pk := PrivateKey{data: privateKeyRaw}

publicKey, err := pk.PublicKey()
if err != nil {
t.Fatalf("%s", err.Error())
}
if !bytes.Equal(publicKey.data, expected) {
t.Fatalf("%s,%v must be equal to %v", "Unexpected public key", publicKey.data, expected)
}
}

func TestHashGenerationFromPublicKey(t *testing.T) {
publicKeyRaw := []byte{23, 156, 58, 89, 20, 125, 48, 49, 108, 136, 102, 40, 133, 35, 72, 201, 180, 42, 24, 184, 33, 8, 74, 201, 239, 121, 189, 115, 233, 185, 78, 141}
expected := []byte{199, 113, 39, 22, 185, 239, 107, 210, 23, 83, 196, 233, 29, 236, 195, 81, 177, 17, 192, 109}
pk := PublicKey{data: publicKeyRaw}

publicKeyHash, err := pk.Hash()
if err != nil {
t.Fatalf("%s", err.Error())
}
if !bytes.Equal(publicKeyHash.data, expected) {
t.Fatalf("%s,%v must be equal to %v", "Unexpected public key hash", publicKeyHash.data, expected)
}
}

func TestSigningMessageUsingPrivateKey(t *testing.T) {
privateKeyRaw := []byte{1, 31, 91, 153, 8, 76, 92, 46, 45, 94, 99, 72, 142, 15, 113, 104, 213, 153, 165, 192, 31, 233, 254, 196, 201, 150, 5, 116, 61, 165, 232, 92}
message := []byte("hello")
expected := []byte{66, 111, 115, 126, 202, 53, 46, 252, 88, 149, 33, 63, 156, 220, 202, 144, 162, 98, 68, 248, 76, 194, 149, 192, 31, 0, 20, 92, 6, 200, 13, 37, 62, 28, 185, 253, 66, 183, 96, 128, 196, 211, 32, 85, 182, 137, 234, 62, 1, 229, 111, 152, 128, 227, 145, 47, 155, 27, 153, 193, 228, 91, 80, 4}
pk := PrivateKey{data: privateKeyRaw}

signature, err := pk.Sign(message)
if err != nil {
t.Fatalf("%s", err.Error())
}
if !bytes.Equal(signature.data, expected) {
t.Fatalf("%s,%v must be equal to %v", "Unexpected signature", signature.data, expected)
}
}