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

Golang cgo bindings and CI tests #3

Merged
merged 1 commit into from
May 12, 2024
Merged
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
74 changes: 74 additions & 0 deletions .github/workflows/test-ref-golang-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Windows Msys2 64bit (msvcrt,ucrt) gcc golang build and test

on: [push]

jobs:
windows-build-and-test-golang:
runs-on: ${{ matrix.os }}
strategy:
matrix:
OS: ["windows-2019", "windows-2022"]
CC: ["gcc"]
ENVIRONMENT: ["UCRT64", "MINGW64"] # https://www.msys2.org/docs/environments/
go-version: ["1.22.x"]
hash:
- sha2
- shake
- haraka
size:
- 128
- 192
- 256
option:
- s
- f
thash:
- simple
- robust
fail-fast: false
steps:
- uses: actions/checkout@v4

- name: Set up Msys2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.ENVIRONMENT }}
install: >-
base-devel
mingw-w64-x86_64-toolchain
mingw-w64-x86_64-pkg-config
mingw-w64-x86_64-gcc
mingw-w64-ucrt-x86_64-gcc
mingw-w64-x86_64-go
mingw-w64-ucrt-x86_64-go
make
git
gcc

- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Gather runtime environment
shell: msys2 {0}
run: |
echo ${{ matrix.ENVIRONMENT }}
uname -a
bash --version
${{ matrix.CC }} -v
go version

- name: Build golang
shell: msys2 {0}
run: |
export CGO_ENABLE=1
export CGO_CFLAGS="-D HASH=${{ matrix.hash }} -D THASH=${{ matrix.thash }} -D PARAMS=sphincs-${{ matrix.hash }}-${{ matrix.size }}${{ matrix.option }}"
go build --tags=sphincs_${{ matrix.hash }}_${{ matrix.size }}${{ matrix.option }} -v ./...

- name: Golang test
shell: msys2 {0}
run: |
export CGO_ENABLE=1
export CGO_CFLAGS="-D HASH=${{ matrix.hash }} -D THASH=${{ matrix.thash }} -D PARAMS=sphincs-${{ matrix.hash }}-${{ matrix.size }}${{ matrix.option }}"
go test --tags=sphincs_${{ matrix.hash }}_${{ matrix.size }}${{ matrix.option }} -v ./...
45 changes: 45 additions & 0 deletions .github/workflows/test-ref-golang.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Tests golang bindings for ref implementation

on:
- push
- pull_request

jobs:
build:
runs-on: ${{ matrix.OS }}
strategy:
matrix:
hash:
- sha2
- shake
- haraka
size:
- 128
- 192
- 256
option:
- s
- f
thash:
- simple
- robust
OS: ["ubuntu-latest", "macos-latest"]
go-version: ["1.20"]
#go-version: ["1.22.x"]
# later: go-version: ["1.19", "1.20", "1.21.x", "1.22.x"]
fail-fast: false
steps:
- uses: actions/checkout@v4

- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Run go test
run: |
export CGO_ENABLE=1
export CGO_CFLAGS=" -D HASH=${{ matrix.hash }} -D THASH=${{ matrix.thash }} -D PARAMS=sphincs-${{ matrix.hash }}-${{ matrix.size }}${{ matrix.option }}"
go test --tags=sphincs_${{ matrix.hash }}_${{ matrix.size }}${{ matrix.option }} -v ./...

# vim: set ft=yaml ts=2 sw=2 et :
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:
- name: Run make
run: |
make -C ref HASH=${{ matrix.hash }} THASH=${{ matrix.thash }} clean
make -C ref HASH=${{ matrix.hash }} THASH=${{ matrix.thash }} PARAMS=sphincs-${{ matrix.hash }}-${{ matrix.size }}${{ matrix.option }} tests
make -C ref HASH=${{ matrix.hash }} THASH=${{ matrix.thash }} PARAMS=sphincs-${{ matrix.hash }}-${{ matrix.size }}${{ matrix.option }} test
make -C ref HASH=${{ matrix.hash }} THASH=${{ matrix.thash }} PARAMS=sphincs_${{ matrix.hash }}_${{ matrix.size }}${{ matrix.option }} tests
make -C ref HASH=${{ matrix.hash }} THASH=${{ matrix.thash }} PARAMS=sphincs_${{ matrix.hash }}_${{ matrix.size }}${{ matrix.option }} test
make -C ref THASH=${{ matrix.thash }} PQCgenKAT_sign
- name: Run PQCgenKAT_sign
run: python3 vectors.py sphincs-${{ matrix.hash }}-${{ matrix.size }}${{ matrix.option }}-${{ matrix.thash }} ref
Expand Down
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/katzenpost/sphincsplus

go 1.20

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/katzenpost/hpqc v0.0.0-20240114190904-bc8bcfcca4ee // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
19 changes: 19 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/katzenpost/hpqc v0.0.0-20240114190904-bc8bcfcca4ee h1:MiBBJNXAJkgv7asTOzrL0lbsL+UIFaFlm13boWz4Fxs=
github.com/katzenpost/hpqc v0.0.0-20240114190904-bc8bcfcca4ee/go.mod h1:i2+fQVPYzpv+WoZcN0KQqjQLEc/AgzRYwAFjYC/LiHg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 1 addition & 1 deletion ref/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PARAMS = sphincs-haraka-128f
PARAMS ?= sphincs-haraka-128f
THASH = robust

CC=/usr/bin/gcc
Expand Down
1 change: 1 addition & 0 deletions ref/PQCgenKAT_sign.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// +build PQCgenKAT_sign

//
// PQCgenKAT_sign.c
Expand Down
139 changes: 139 additions & 0 deletions ref/binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//go:build cgo && ((linux && amd64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && (sphincs_haraka_128f || sphincs_haraka_128s || sphincs_haraka_192f || sphincs_haraka_192s || sphincs_haraka_256f || sphincs_haraka_256s || sphincs_sha2_128f || sphincs_sha2_128s || sphincs_sha2_192f || sphincs_sha2_192s || sphincs_sha2_256f || sphincs_sha2_256s || sphincs_shake_128f || sphincs_shake_128s || sphincs_shake_192f || sphincs_shake_192s || sphincs_shake_256f || sphincs_shake_256s)

package sphincsplus

//#cgo amd64 LDFLAGS: -L./
//#cgo amd64 CFLAGS: -O3 -std=c99 -D CGO=1
//#cgo darwin/amd64 LDFLAGS: -L./
//#cgo darwin/amd64 CFLAGS: -O3 -std=c99 -D CGO=1
//#cgo linux/amd64 LDFLAGS: -L./ -L/usr/lib/x86_64-linux-gnu/
//#cgo linux/amd64 CFLAGS: -O3 -std=c99 -D CGO=1
//#include "api.h"
import "C"
import (
"fmt"
"unsafe"

"github.com/katzenpost/sphincsplus/ref/params"
)

var (

// PublicKeySize is the size in bytes of the public key.
PublicKeySize int = C.CRYPTO_PUBLICKEYBYTES

// PrivateKeySize is the size in bytes of the private key.
PrivateKeySize int = C.CRYPTO_SECRETKEYBYTES

// SignatureSize is the size in bytes of the signature.
SignatureSize int = C.CRYPTO_BYTES

// SignatureName is the parameterized signature system name

// Name returns the string naming of the current
// Sphincs+ that this binding is being used with.
SignatureName = params.Name()

// ErrPublicKeySize indicates the raw data is not the correct size for a public key.
ErrPublicKeySize error = fmt.Errorf("%s: raw public key data size is wrong", Name())

// ErrPrivateKeySize indicates the raw data is not the correct size for a private key.
ErrPrivateKeySize error = fmt.Errorf("%s: raw private key data size is wrong", Name())
)

func SchemeName() string {
return SignatureName
}

// NewKeypair generates a new Sphincs+ keypair.
func NewKeypair() (*PrivateKey, *PublicKey) {
privKey := &PrivateKey{
privateKey: make([]byte, C.CRYPTO_SECRETKEYBYTES),
}
pubKey := &PublicKey{
publicKey: make([]byte, C.CRYPTO_PUBLICKEYBYTES),
}
C.crypto_sign_keypair((*C.uchar)(unsafe.Pointer(&pubKey.publicKey[0])),
(*C.uchar)(unsafe.Pointer(&privKey.privateKey[0])))
return privKey, pubKey
}

// PublicKey is a public Sphincs+ key.
type PublicKey struct {
publicKey []byte
}

// Verify checks whether the given signature is valid.
func (p *PublicKey) Verify(signature, message []byte) bool {
ret := C.crypto_sign_verify((*C.uchar)(unsafe.Pointer(&signature[0])),
C.size_t(len(signature)),
(*C.uchar)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uchar)(unsafe.Pointer(&p.publicKey[0])))
if ret == 0 {
return true
}
return false
}

// Bytes returns the PublicKey as a byte slice.
func (p *PublicKey) Bytes() []byte {
return p.publicKey
}

// FromBytes loads a PublicKey from the given byte slice.
func (p *PublicKey) FromBytes(data []byte) error {
if len(data) != PublicKeySize {
return ErrPublicKeySize
}

p.publicKey = data
return nil
}

// Verify checks whether the given signature is valid.
/*
func (p *PublicKey) Verify(signature, message []byte) bool {
ret := C.crypto_sign_verify((*C.uchar)(unsafe.Pointer(&signature[0])),
C.ulong(len(signature)),
(*C.uchar)(unsafe.Pointer(&message[0])),
C.ulong(len(message)),
(*C.uchar)(unsafe.Pointer(&p.publicKey[0])))
if ret == 0 {
return true
}
return false
}
*/

// PrivateKey is a private Sphincs+ key.
type PrivateKey struct {
privateKey []byte
}

// Sign signs the given message and returns the signature.
func (p *PrivateKey) Sign(message []byte) []byte {
signature := make([]byte, C.CRYPTO_BYTES)
sigLen := C.size_t(C.CRYPTO_BYTES)
C.crypto_sign_signature((*C.uchar)(unsafe.Pointer(&signature[0])),
&sigLen,
(*C.uchar)(unsafe.Pointer(&message[0])),
(C.size_t)(len(message)),
(*C.uchar)(unsafe.Pointer(&p.privateKey[0])))
return signature
}

// Bytes returns the PrivateKey as a byte slice.
func (p *PrivateKey) Bytes() []byte {
return p.privateKey
}

// FromBytes loads a PrivateKey from the given byte slice.
func (p *PrivateKey) FromBytes(data []byte) error {
if len(data) != PrivateKeySize {
return ErrPrivateKeySize
}

p.privateKey = data
return nil
}
68 changes: 68 additions & 0 deletions ref/binding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//go:build ((linux && amd64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && (sphincs_haraka_128f || sphincs_haraka_128s || sphincs_haraka_192f || sphincs_haraka_192s || sphincs_haraka_256f || sphincs_haraka_256s || sphincs_sha2_128f || sphincs_sha2_128s || sphincs_sha2_192f || sphincs_sha2_192s || sphincs_sha2_256f || sphincs_sha2_256s || sphincs_shake_128f || sphincs_shake_128s || sphincs_shake_192f || sphincs_shake_192s || sphincs_shake_256f || sphincs_shake_256s)

package sphincsplus

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestSignVerify(t *testing.T) {
t.Parallel()
privKey1, pubKey1 := NewKeypair()
message := []byte("i am a message")
sig1 := privKey1.Sign(message)
require.True(t, pubKey1.Verify(sig1, message))

privKey2, pubKey2 := NewKeypair()
require.False(t, pubKey2.Verify(sig1, message))

sig2 := privKey2.Sign(message)
require.True(t, pubKey2.Verify(sig2, message))
require.False(t, pubKey1.Verify(sig2, message))

// non-determinism
sig3 := privKey1.Sign(message)
require.NotEqual(t, sig1, sig3)
}

func TestSerialization(t *testing.T) {
t.Parallel()
privKey1, pubKey1 := NewKeypair()
message := []byte("i am a message")
sig := privKey1.Sign(message)
require.True(t, pubKey1.Verify(sig, message))

pubKeyBytes1 := pubKey1.Bytes()
pubKey2 := &PublicKey{}
err := pubKey2.FromBytes(pubKeyBytes1)
require.NoError(t, err)
require.True(t, pubKey2.Verify(sig, message))
require.False(t, pubKey2.Verify(sig, message[:len(message)-3]))

privKeyBytes2 := privKey1.Bytes()
privKey2 := &PrivateKey{}
err = privKey2.FromBytes(privKeyBytes2)
require.NoError(t, err)
sig2 := privKey2.Sign(message)
require.True(t, pubKey1.Verify(sig2, message))
require.True(t, pubKey2.Verify(sig2, message))
}

func TestSizes(t *testing.T) {
t.Parallel()
privKey, pubKey := NewKeypair()
message := []byte("i am a message")
sig := privKey.Sign(message)
require.True(t, pubKey.Verify(sig, message))

require.Equal(t, PrivateKeySize, len(privKey.Bytes()))
require.Equal(t, PublicKeySize, len(pubKey.Bytes()))
require.Equal(t, SignatureSize, len(sig))

t.Logf("SPHINCS+ scheme parameters: %s", SchemeName())
t.Logf("PrivateKeySize %d", PrivateKeySize)
t.Logf("PublicKeySize %d", PublicKeySize)
t.Logf("SignatureSize %d", SignatureSize)
}
2 changes: 2 additions & 0 deletions ref/fips202.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build cgo && ((linux && amd64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && (sphincs_shake_128f || sphincs_shake_128s || sphincs_shake_192f || sphincs_shake_192s || sphincs_shake_256f || sphincs_shake_256s)

/* Based on the public domain implementation in
* crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html
* by Ronny Van Keer
Expand Down
2 changes: 2 additions & 0 deletions ref/haraka.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build cgo && ((linux && amd64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && (sphincs_haraka_128f || sphincs_haraka_128s || sphincs_haraka_192f || sphincs_haraka_192s || sphincs_haraka_256f || sphincs_haraka_256s)

/*
* Constant time implementation of the Haraka hash function.
*
Expand Down
Loading
Loading