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

2nd attempt to refork from upstream #2

Closed
wants to merge 3 commits into from
Closed
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
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=
12 changes: 2 additions & 10 deletions ref/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,7 @@ void copy_subtree_addr(uint32_t out[8], const uint32_t in[8])
*/
void set_keypair_addr(uint32_t addr[8], uint32_t keypair)
{
#if SPX_FULL_HEIGHT/SPX_D > 8
/* We have > 256 OTS at the bottom of the Merkle tree; to specify */
/* which one, we'd need to express it in two bytes */
((unsigned char *)addr)[SPX_OFFSET_KP_ADDR2] = (unsigned char)(keypair >> 8);
#endif
((unsigned char *)addr)[SPX_OFFSET_KP_ADDR1] = (unsigned char)keypair;
u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_KP_ADDR], keypair);
}

/*
Expand All @@ -67,10 +62,7 @@ void set_keypair_addr(uint32_t addr[8], uint32_t keypair)
void copy_keypair_addr(uint32_t out[8], const uint32_t in[8])
{
memcpy( out, in, SPX_OFFSET_TREE+8 );
#if SPX_FULL_HEIGHT/SPX_D > 8
((unsigned char *)out)[SPX_OFFSET_KP_ADDR2] = ((unsigned char *)in)[SPX_OFFSET_KP_ADDR2];
#endif
((unsigned char *)out)[SPX_OFFSET_KP_ADDR1] = ((unsigned char *)in)[SPX_OFFSET_KP_ADDR1];
memcpy( (unsigned char *)out + SPX_OFFSET_KP_ADDR, (unsigned char *)in + SPX_OFFSET_KP_ADDR, 4);
}

/*
Expand Down
141 changes: 141 additions & 0 deletions ref/binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package sphincsplus

//#cgo linux LDFLAGS: "-L./ -L/usr/lib/x86_64-linux-gnu/ -lcrypto"
//#include "api.h"
import "C"
import (
"fmt"
"unsafe"

"github.com/katzenpost/hpqc/util"

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

var (
_ = params.A

// 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

// 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())
)

// Name returns the string naming of the current
// Sphincs+ that this binding is being used with.
func Name() string {
return "Sphincs+shake-256f"
}

// 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
}

// Reset overwrites the key with zeros.
func (p *PublicKey) Reset() {
util.ExplicitBzero(p.publicKey)
}

// 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
}

// Reset overwrites the key with zeros.
func (p *PrivateKey) Reset() {
util.ExplicitBzero(p.privateKey)
}

// 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
}
65 changes: 65 additions & 0 deletions ref/binding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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("PrivateKeySize %d", PrivateKeySize)
t.Logf("PublicKeySize %d", PublicKeySize)
t.Logf("SignatureSize %d", SignatureSize)
}
17 changes: 17 additions & 0 deletions ref/explicitBzero.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: (C) 2017 Yawning Angel.
// SPDX-License-Identifier: AGPL-3.0-only

package sphincsplus

import "runtime"

// ExplicitBzero explicitly clears out the buffer b, by filling it with 0x00
// bytes.
//
//go:noinline
func ExplicitBzero(b []byte) {
for i := range b {
b[i] = 0
}
runtime.KeepAlive(b)
}
3 changes: 1 addition & 2 deletions ref/haraka_offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
#define SPX_OFFSET_LAYER 3 /* The byte used to specify the Merkle tree layer */
#define SPX_OFFSET_TREE 8 /* The start of the 8 byte field used to specify the tree */
#define SPX_OFFSET_TYPE 19 /* The byte used to specify the hash type (reason) */
#define SPX_OFFSET_KP_ADDR2 22 /* The high byte used to specify the key pair (which one-time signature) */
#define SPX_OFFSET_KP_ADDR1 23 /* The low byte used to specify the key pair */
#define SPX_OFFSET_KP_ADDR 20 /* The start of the 4 byte field used to specify the key pair address */
#define SPX_OFFSET_CHAIN_ADDR 27 /* The byte used to specify the chain address (which Winternitz chain) */
#define SPX_OFFSET_HASH_ADDR 31 /* The byte used to specify the hash address (where in the Winternitz chain) */
#define SPX_OFFSET_TREE_HGT 27 /* The byte used to specify the height of this node in the FORS or Merkle tree */
Expand Down
8 changes: 6 additions & 2 deletions ref/hash_haraka.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,12 @@ void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx,
#error For given height and depth, 64 bits cannot represent all subtrees
#endif

*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
if (SPX_D == 1) {
*tree = 0;
} else {
*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
}
bufp += SPX_TREE_BYTES;

*leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES);
Expand Down
8 changes: 6 additions & 2 deletions ref/hash_sha2.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,12 @@ void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx,
#error For given height and depth, 64 bits cannot represent all subtrees
#endif

*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
if (SPX_D == 1) {
*tree = 0;
} else {
*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
}
bufp += SPX_TREE_BYTES;

*leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES);
Expand Down
8 changes: 6 additions & 2 deletions ref/hash_shake.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,12 @@ void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx,
#error For given height and depth, 64 bits cannot represent all subtrees
#endif

*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
if (SPX_D == 1) {
*tree = 0;
} else {
*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
}
bufp += SPX_TREE_BYTES;

*leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES);
Expand Down
2 changes: 2 additions & 0 deletions ref/params.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#define str(s) #s
#define xstr(s) str(s)

#define PARAMS sphincs-shake-256f

#include xstr(params/params-PARAMS.h)

3 changes: 3 additions & 0 deletions ref/params/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package params

const A = 1
3 changes: 1 addition & 2 deletions ref/sha2_offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
#define SPX_OFFSET_LAYER 0 /* The byte used to specify the Merkle tree layer */
#define SPX_OFFSET_TREE 1 /* The start of the 8 byte field used to specify the tree */
#define SPX_OFFSET_TYPE 9 /* The byte used to specify the hash type (reason) */
#define SPX_OFFSET_KP_ADDR2 12 /* The high byte used to specify the key pair (which one-time signature) */
#define SPX_OFFSET_KP_ADDR1 13 /* The low byte used to specify the key pair */
#define SPX_OFFSET_KP_ADDR 10 /* The start of the 4 byte field used to specify the key pair address */
#define SPX_OFFSET_CHAIN_ADDR 17 /* The byte used to specify the chain address (which Winternitz chain) */
#define SPX_OFFSET_HASH_ADDR 21 /* The byte used to specify the hash address (where in the Winternitz chain) */
#define SPX_OFFSET_TREE_HGT 17 /* The byte used to specify the height of this node in the FORS or Merkle tree */
Expand Down
3 changes: 1 addition & 2 deletions ref/shake_offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
#define SPX_OFFSET_LAYER 3 /* The byte used to specify the Merkle tree layer */
#define SPX_OFFSET_TREE 8 /* The start of the 8 byte field used to specify the tree */
#define SPX_OFFSET_TYPE 19 /* The byte used to specify the hash type (reason) */
#define SPX_OFFSET_KP_ADDR2 22 /* The high byte used to specify the key pair (which one-time signature) */
#define SPX_OFFSET_KP_ADDR1 23 /* The low byte used to specify the key pair */
#define SPX_OFFSET_KP_ADDR 20 /* The start of the 4 byte field used to specify the key pair address */
#define SPX_OFFSET_CHAIN_ADDR 27 /* The byte used to specify the chain address (which Winternitz chain) */
#define SPX_OFFSET_HASH_ADDR 31 /* The byte used to specify the hash address (where in the Winternitz chain) */
#define SPX_OFFSET_TREE_HGT 27 /* The byte used to specify the height of this node in the FORS or Merkle tree */
Expand Down
Loading
Loading