Skip to content

Commit

Permalink
Add golang FFI.
Browse files Browse the repository at this point in the history
  • Loading branch information
liorgold2 committed Dec 26, 2021
1 parent 601de40 commit 95864fb
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This repo requires the installation of the Golang compiler on the system in order to run the
presubmit script and the provided Go FFI and test. To install Go under Ubuntu, run the following:

``sudo apt install golang-go``
8 changes: 8 additions & 0 deletions presubmit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@

set -e

# install npm packages.
(cd src/starkware/crypto/ffi/js; npm install)

# Compile the code.
mkdir -p build/Release
(cd build/Release; cmake -DCMAKE_BUILD_TYPE=Release ../..)
make -C build/Release

# Run tests.
CTEST_OUTPUT_ON_FAILURE=1 make -C build/Release test

# Run go testing (we assume golang-go is installed).
go test build/Release/src/starkware/crypto/ffi/crypto_lib_test.go

clang-tidy-6.0 -header-filter=src/starkware -p=build/Release $(find src/starkware -name "*.cc" | grep -v node_modules)
cpplint --extensions=cc,h $(find src/starkware | grep -v node_modules | grep -E '\.(cc|h)$')
33 changes: 33 additions & 0 deletions src/starkware/crypto/ffi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,36 @@ add_subdirectory(js)

add_library(crypto_c_exports SHARED pedersen_hash.cc ecdsa.cc utils.cc)
target_link_libraries(crypto_c_exports crypto)

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib/crypto_lib.go
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib/ ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib/
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib/crypto_lib.go
)

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib_test.go
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib_test.go ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib_test.go
)

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ecdsa.h
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/ecdsa.h ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ecdsa.h
)

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pedersen_hash.h
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/pedersen_hash.h ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/pedersen_hash.h
)

add_custom_target(
CopyGoFiles ALL
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/crypto_lib/crypto_lib.go
${CMAKE_CURRENT_BINARY_DIR}/crypto_lib_test.go
${CMAKE_CURRENT_BINARY_DIR}/ecdsa.h
${CMAKE_CURRENT_BINARY_DIR}/pedersen_hash.h
)
195 changes: 195 additions & 0 deletions src/starkware/crypto/ffi/crypto_lib/crypto_lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package crypto_lib

import "encoding/hex"
import (
"fmt"
"math/big"
)
/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L./.. -lcrypto_c_exports -Wl,-rpath=./.
#include <stdlib.h>
#include "../ecdsa.h"
#include "../pedersen_hash.h"
*/
import "C"
import "unsafe"


const curveOrder = "800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f"

/*
Computes the component s of a signature given the component w.
*/
func invertOnCurve(w string) string {
w_big_int := new(big.Int)
w_big_int.SetString(w[2:], 16)
order := new(big.Int)
order.SetString(curveOrder, 16)
w_big_int.ModInverse(w_big_int, order)
s := fmt.Sprintf("0x0%x", w_big_int)
return s
}

/*
Given a hex string reverses its endian represenation.
*/
func reverseHexEndianRepresentation(s string) string {
rns := []rune(s)
for i, j := 0, len(rns)-2; i < j; i, j = i+2, j-2 {
rns[i], rns[j] = rns[j], rns[i]
rns[i+1], rns[j+1] = rns[j+1], rns[i+1]
}
return string(rns)
}

/*
Pads the given hex string with leading zeroes so that its length is 64 characters (32 bytes).
*/
func padHexString(s string) string {
padded := fmt.Sprintf("0x%064s", s[2:])
return padded
}

/*
Computes the StarkWare version of the Pedersen hash of x and y.
Full specification of the hash function can be found here:
https://docs.starkware.co/starkex-docs/crypto/pedersen-hash-function
*/
func Hash(input1, input2 string) string {
input1_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(input1))
input2_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(input2))
in1 := C.CBytes(input1_dec)
in2 := C.CBytes(input2_dec)
var o [1024]byte
out := C.CBytes(o[:])

res := C.Hash(
(*C.char)(unsafe.Pointer(in1)),
(*C.char)(unsafe.Pointer(in2)),
(*C.char)(unsafe.Pointer(out)))

if res != 0 {
fmt.Printf("Pedersen hash encountered an error: %s\n", C.GoBytes(unsafe.Pointer(out), 1024))
C.free(unsafe.Pointer(in1))
C.free(unsafe.Pointer(in2))
C.free(unsafe.Pointer(out))
return ""
}

hash_result := "0x" + reverseHexEndianRepresentation(
hex.EncodeToString(C.GoBytes(unsafe.Pointer(out), 32)))

C.free(unsafe.Pointer(in1))
C.free(unsafe.Pointer(in2))
C.free(unsafe.Pointer(out))

return hash_result
}

/*
Deduces the public key given a private key.
*/
func GetPublicKey(private_key string) string {
private_key_dec, _ := hex.DecodeString(
reverseHexEndianRepresentation(padHexString(private_key)))
private_key_bytes := C.CBytes(private_key_dec)
var o [1024]byte
out := C.CBytes(o[:])

res := C.GetPublicKey(
(*C.char)(unsafe.Pointer(private_key_bytes)), (*C.char)(unsafe.Pointer(out)))

if res != 0 {
fmt.Printf("GetPublicKey encountered an error: %s\n", C.GoBytes(unsafe.Pointer(out), 1024))
C.free(unsafe.Pointer(private_key_bytes))
C.free(unsafe.Pointer(out))
return ""
}

public_key_result := "0x" + reverseHexEndianRepresentation(
hex.EncodeToString(C.GoBytes(unsafe.Pointer(out), 32)))

C.free(unsafe.Pointer(private_key_bytes))
C.free(unsafe.Pointer(out))

return public_key_result
}

/*
Verifies ECDSA signature of a given message hash z with a given public key.
Returns true if public_key signs the message.
NOTE: This function assumes that the public_key is on the curve.
*/
func Verify(stark_key, msg_hash, r, s string) bool {
stark_key_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(stark_key)))
stark_key_bytes := C.CBytes(stark_key_dec)

message_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(msg_hash)))
message_bytes := C.CBytes(message_dec)

r_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(r)))
r_bytes := C.CBytes(r_dec)

w := invertOnCurve(s)
w_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(w)))
w_bytes := C.CBytes(w_dec)

res := C.Verify(
(*C.char)(unsafe.Pointer(stark_key_bytes)),
(*C.char)(unsafe.Pointer(message_bytes)),
(*C.char)(unsafe.Pointer(r_bytes)),
(*C.char)(unsafe.Pointer(w_bytes)))

C.free(unsafe.Pointer(stark_key_bytes))
C.free(unsafe.Pointer(message_bytes))
C.free(unsafe.Pointer(r_bytes))
C.free(unsafe.Pointer(w_bytes))

if res == 0 {
return false
}
return true
}

/*
Signs the given message hash with the provided private_key, with randomness k.
NOTE: k should be a strong cryptographical random, and not repeat.
See: https://tools.ietf.org/html/rfc6979.
*/
func Sign(private_key, message, k string) (string, string) {
private_key_dec, _ := hex.DecodeString(
reverseHexEndianRepresentation(padHexString(private_key)))
private_key_bytes := C.CBytes(private_key_dec)

message_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(message)))
message_bytes := C.CBytes(message_dec)

k_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(k)))
k_bytes := C.CBytes(k_dec)

var o [1024]byte
out := C.CBytes(o[:])

C.Sign(
(*C.char)(unsafe.Pointer(private_key_bytes)),
(*C.char)(unsafe.Pointer(message_bytes)),
(*C.char)(unsafe.Pointer(k_bytes)),
(*C.char)(unsafe.Pointer(out)))

res := reverseHexEndianRepresentation(hex.EncodeToString(C.GoBytes(unsafe.Pointer(out), 64)))
signature_w := "0x" + res[0:64]
signature_r := "0x" + res[64:]

signature_s := invertOnCurve(signature_w)

C.free(unsafe.Pointer(private_key_bytes))
C.free(unsafe.Pointer(message_bytes))
C.free(unsafe.Pointer(k_bytes))
C.free(unsafe.Pointer(out))

return signature_r, signature_s
}
61 changes: 61 additions & 0 deletions src/starkware/crypto/ffi/crypto_lib_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import "./crypto_lib"
import "testing"

func TestHash(t *testing.T) {
res := crypto_lib.Hash(
"0x03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb",
"0x0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a")

expected_hash := "0x030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662"
if res != expected_hash {
t.Errorf("Hash error: expected %s but got %s.", expected_hash, res)
}
}

func TestGetPublicKey(t *testing.T) {
res := crypto_lib.GetPublicKey(
"0x03c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc")

expected_key := "0x077a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43"
if res != expected_key {
t.Errorf("GetPublicKey error: expected %s but got %s.", expected_key, res)
}

res = crypto_lib.GetPublicKey("0x12")

expected_key = "0x019661066e96a8b9f06a1d136881ee924dfb6a885239caa5fd3f87a54c6b25c4"
if res != expected_key {
t.Errorf("GetPublicKey error: expected %s but got %s.", expected_key, res)
}
}

func TestVerify(t *testing.T) {
res := crypto_lib.Verify(
"0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
"0x2",
"0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20",
"0x405c3191ab3883ef2b763af35bc5f5d15b3b4e99461d70e84c654a351a7c81b")
if !(res) {
t.Errorf("Verify error: valid message was not verified correctly.")
}

res = crypto_lib.Verify(
"0x077a4b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43",
"0x0397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f",
"0x0173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882",
"0x01f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e")
if (res) {
t.Errorf("Verify error: invalid message was not rejected.")
}
}

func TestSign(t *testing.T) {
r, s := crypto_lib.Sign("0x1", "0x2", "0x3")
public_key := crypto_lib.GetPublicKey("0x1")
res := crypto_lib.Verify(public_key, "0x2", r, s)
if !(res) {
t.Errorf("Sign error: signature rejected by verification.")
}
}
4 changes: 3 additions & 1 deletion src/starkware/crypto/ffi/ecdsa.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "starkware/crypto/ffi/ecdsa.h"
#include "starkware/crypto/ecdsa.h"

#include <array>

#include "third_party/gsl/gsl-lite.hpp"

#include "starkware/algebra/prime_field_element.h"
#include "starkware/crypto/ecdsa.h"
#include "starkware/crypto/ffi/utils.h"

namespace starkware {
Expand Down
10 changes: 10 additions & 0 deletions src/starkware/crypto/ffi/ecdsa.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef STARKWARE_CRYPTO_FFI_ECDSA_H_
#define STARKWARE_CRYPTO_FFI_ECDSA_H_

int GetPublicKey(const char* private_key, char* out);

int Verify(const char* stark_key, const char* msg_hash, const char* r_bytes, const char* w_bytes);

int Sign(const char* private_key, const char* message, const char* k, char* out);

#endif // STARKWARE_CRYPTO_FFI_ECDSA_H_
8 changes: 5 additions & 3 deletions src/starkware/crypto/ffi/pedersen_hash.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include <array>
#include "starkware/crypto/ffi/pedersen_hash.h"
#include "starkware/crypto/pedersen_hash.h"

#include "third_party/gsl/gsl-lite.hpp"
#include <array>

#include "starkware/algebra/prime_field_element.h"
#include "starkware/crypto/ffi/utils.h"
#include "starkware/crypto/pedersen_hash.h"

#include "third_party/gsl/gsl-lite.hpp"

namespace starkware {

Expand Down
6 changes: 6 additions & 0 deletions src/starkware/crypto/ffi/pedersen_hash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef STARKWARE_CRYPTO_FFI_PEDERSEN_HASH_H_
#define STARKWARE_CRYPTO_FFI_PEDERSEN_HASH_H_

int Hash(const char* in1, const char* in2, char* out);

#endif // STARKWARE_CRYPTO_FFI_PEDERSEN_HASH_H_

0 comments on commit 95864fb

Please sign in to comment.