mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 01:41:44 +00:00
Executable
+232
@@ -0,0 +1,232 @@
|
||||
// Copyright (c) 2014 Dropbox, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package mina
|
||||
|
||||
// A BitVector is a variable sized vector of bits. It supports
|
||||
// lookups, sets, appends, insertions, and deletions.
|
||||
//
|
||||
// This class is not thread safe.
|
||||
type BitVector struct {
|
||||
data []byte
|
||||
length int
|
||||
}
|
||||
|
||||
// NewBitVector creates and initializes a new bit vector with length
|
||||
// elements, using data as its initial contents.
|
||||
func NewBitVector(data []byte, length int) *BitVector {
|
||||
return &BitVector{
|
||||
data: data,
|
||||
length: length,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns a slice of the contents of the bit vector. If the caller changes the returned slice,
|
||||
// the contents of the bit vector may change.
|
||||
func (vector *BitVector) Bytes() []byte {
|
||||
return vector.data
|
||||
}
|
||||
|
||||
// Length returns the current number of elements in the bit vector.
|
||||
func (vector *BitVector) Length() int {
|
||||
return vector.length
|
||||
}
|
||||
|
||||
// This function shifts a byte slice one bit lower (less significant).
|
||||
// bit (either 1 or 0) contains the bit to put in the most significant
|
||||
// position of the last byte in the slice.
|
||||
// This returns the bit that was shifted off of the last byte.
|
||||
func shiftLower(bit byte, b []byte) byte {
|
||||
bit = bit << 7
|
||||
for i := len(b) - 1; i >= 0; i-- {
|
||||
newByte := b[i] >> 1
|
||||
newByte |= bit
|
||||
bit = (b[i] & 1) << 7
|
||||
b[i] = newByte
|
||||
}
|
||||
return bit >> 7
|
||||
}
|
||||
|
||||
// This function shifts a byte slice one bit higher (more significant).
|
||||
// bit (either 1 or 0) contains the bit to put in the least significant
|
||||
// position of the first byte in the slice.
|
||||
// This returns the bit that was shifted off the last byte.
|
||||
func shiftHigher(bit byte, b []byte) byte {
|
||||
for i := 0; i < len(b); i++ {
|
||||
newByte := b[i] << 1
|
||||
newByte |= bit
|
||||
bit = (b[i] & 0x80) >> 7
|
||||
b[i] = newByte
|
||||
}
|
||||
return bit
|
||||
}
|
||||
|
||||
// Returns the minimum number of bytes needed for storing the bit vector.
|
||||
func (vector *BitVector) bytesLength() int {
|
||||
lastBitIndex := vector.length - 1
|
||||
lastByteIndex := lastBitIndex >> 3
|
||||
return lastByteIndex + 1
|
||||
}
|
||||
|
||||
// Panics if the given index is not within the bounds of the bit vector.
|
||||
func (vector *BitVector) indexAssert(i int) {
|
||||
if i < 0 || i >= vector.length {
|
||||
panic("Attempted to access element outside buffer")
|
||||
}
|
||||
}
|
||||
|
||||
// Append adds a bit to the end of a bit vector.
|
||||
func (vector *BitVector) Append(bit byte) {
|
||||
index := uint32(vector.length)
|
||||
vector.length++
|
||||
|
||||
if vector.bytesLength() > len(vector.data) {
|
||||
vector.data = append(vector.data, 0)
|
||||
}
|
||||
|
||||
byteIndex := index >> 3
|
||||
byteOffset := index % 8
|
||||
oldByte := vector.data[byteIndex]
|
||||
var newByte byte
|
||||
if bit == 1 {
|
||||
newByte = oldByte | 1<<byteOffset
|
||||
} else {
|
||||
// Set all bits except the byteOffset
|
||||
mask := byte(^(1 << byteOffset))
|
||||
newByte = oldByte & mask
|
||||
}
|
||||
|
||||
vector.data[byteIndex] = newByte
|
||||
}
|
||||
|
||||
// Element returns the bit in the ith index of the bit vector.
|
||||
// Returned value is either 1 or 0.
|
||||
func (vector *BitVector) Element(i int) byte {
|
||||
vector.indexAssert(i)
|
||||
byteIndex := i >> 3
|
||||
byteOffset := uint32(i % 8)
|
||||
b := vector.data[byteIndex]
|
||||
// Check the offset bit
|
||||
return (b >> byteOffset) & 1
|
||||
}
|
||||
|
||||
// Set changes the bit in the ith index of the bit vector to the value specified in
|
||||
// bit.
|
||||
func (vector *BitVector) Set(bit byte, index int) {
|
||||
vector.indexAssert(index)
|
||||
byteIndex := uint32(index >> 3)
|
||||
byteOffset := uint32(index % 8)
|
||||
|
||||
oldByte := vector.data[byteIndex]
|
||||
|
||||
var newByte byte
|
||||
if bit == 1 {
|
||||
// turn on the byteOffset'th bit
|
||||
newByte = oldByte | 1<<byteOffset
|
||||
} else {
|
||||
// turn off the byteOffset'th bit
|
||||
removeMask := byte(^(1 << byteOffset))
|
||||
newByte = oldByte & removeMask
|
||||
}
|
||||
vector.data[byteIndex] = newByte
|
||||
}
|
||||
|
||||
// Insert inserts bit into the supplied index of the bit vector. All
|
||||
// bits in positions greater than or equal to index before the call will
|
||||
// be shifted up by one.
|
||||
func (vector *BitVector) Insert(bit byte, index int) {
|
||||
vector.indexAssert(index)
|
||||
vector.length++
|
||||
|
||||
// Append an additional byte if necessary.
|
||||
if vector.bytesLength() > len(vector.data) {
|
||||
vector.data = append(vector.data, 0)
|
||||
}
|
||||
|
||||
byteIndex := uint32(index >> 3)
|
||||
byteOffset := uint32(index % 8)
|
||||
var bitToInsert byte
|
||||
if bit == 1 {
|
||||
bitToInsert = 1 << byteOffset
|
||||
}
|
||||
|
||||
oldByte := vector.data[byteIndex]
|
||||
// This bit will need to be shifted into the next byte
|
||||
leftoverBit := (oldByte & 0x80) >> 7
|
||||
// Make masks to pull off the bits below and above byteOffset
|
||||
// This mask has the byteOffset lowest bits set.
|
||||
bottomMask := byte((1 << byteOffset) - 1)
|
||||
// This mask has the 8 - byteOffset top bits set.
|
||||
topMask := ^bottomMask
|
||||
top := (oldByte & topMask) << 1
|
||||
newByte := bitToInsert | (oldByte & bottomMask) | top
|
||||
|
||||
vector.data[byteIndex] = newByte
|
||||
// Shift the rest of the bytes in the slice one higher, append
|
||||
// the leftoverBit obtained above.
|
||||
shiftHigher(leftoverBit, vector.data[byteIndex+1:])
|
||||
}
|
||||
|
||||
// Delete removes the bit in the supplied index of the bit vector. All
|
||||
// bits in positions greater than or equal to index before the call will
|
||||
// be shifted down by one.
|
||||
func (vector *BitVector) Delete(index int) {
|
||||
vector.indexAssert(index)
|
||||
vector.length--
|
||||
byteIndex := uint32(index >> 3)
|
||||
byteOffset := uint32(index % 8)
|
||||
|
||||
oldByte := vector.data[byteIndex]
|
||||
|
||||
// Shift all the bytes above the byte we're modifying, return the
|
||||
// leftover bit to include in the byte we're modifying.
|
||||
bit := shiftLower(0, vector.data[byteIndex+1:])
|
||||
|
||||
// Modify oldByte.
|
||||
// At a high level, we want to select the bits above byteOffset,
|
||||
// and shift them down by one, removing the bit at byteOffset.
|
||||
|
||||
// This selects the bottom bits
|
||||
bottomMask := byte((1 << byteOffset) - 1)
|
||||
// This selects the top (8 - byteOffset - 1) bits
|
||||
topMask := byte(^((1 << (byteOffset + 1)) - 1))
|
||||
// newTop is the top bits, shifted down one, combined with the leftover bit from shifting
|
||||
// the other bytes.
|
||||
newTop := (oldByte&topMask)>>1 | (bit << 7)
|
||||
// newByte takes the bottom bits and combines with the new top.
|
||||
newByte := (bottomMask & oldByte) | newTop
|
||||
vector.data[byteIndex] = newByte
|
||||
|
||||
// The desired length is the byte index of the last element plus one,
|
||||
// where the byte index of the last element is the bit index of the last
|
||||
// element divided by 8.
|
||||
byteLength := vector.bytesLength()
|
||||
if byteLength < len(vector.data) {
|
||||
vector.data = vector.data[:byteLength]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/core/curves"
|
||||
)
|
||||
|
||||
type MinaTSchnorrHandler struct{}
|
||||
|
||||
func (m MinaTSchnorrHandler) DeriveChallenge(
|
||||
msg []byte,
|
||||
pubKey curves.Point,
|
||||
r curves.Point,
|
||||
) (curves.Scalar, error) {
|
||||
txn := new(Transaction)
|
||||
err := txn.UnmarshalBinary(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input := new(roinput).Init(3, 75)
|
||||
txn.addRoInput(input)
|
||||
|
||||
pt, ok := pubKey.(*curves.PointPallas)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid point")
|
||||
}
|
||||
R, ok := r.(*curves.PointPallas)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid point")
|
||||
}
|
||||
|
||||
pk := new(PublicKey)
|
||||
pk.value = pt.GetEp()
|
||||
|
||||
sc := msgHash(pk, R.X(), input, ThreeW, MainNet)
|
||||
s := new(curves.ScalarPallas)
|
||||
s.SetFq(sc)
|
||||
return s, nil
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/core/curves"
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fp"
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fq"
|
||||
)
|
||||
|
||||
const (
|
||||
version = 0xcb
|
||||
nonZeroCurvePointVersion = 0x01
|
||||
isCompressed = 0x01
|
||||
)
|
||||
|
||||
// PublicKey is the verification key
|
||||
type PublicKey struct {
|
||||
value *curves.Ep
|
||||
}
|
||||
|
||||
// GenerateAddress converts the public key to an address
|
||||
func (pk PublicKey) GenerateAddress() string {
|
||||
var payload [40]byte
|
||||
payload[0] = version
|
||||
payload[1] = nonZeroCurvePointVersion
|
||||
payload[2] = isCompressed
|
||||
|
||||
buffer := pk.value.ToAffineUncompressed()
|
||||
copy(payload[3:35], buffer[:32])
|
||||
payload[35] = buffer[32] & 1
|
||||
hash1 := sha256.Sum256(payload[:36])
|
||||
hash2 := sha256.Sum256(hash1[:])
|
||||
copy(payload[36:40], hash2[:4])
|
||||
return base58.Encode(payload[:])
|
||||
}
|
||||
|
||||
// ParseAddress converts a given string into a public key returning an error on failure
|
||||
func (pk *PublicKey) ParseAddress(b58 string) error {
|
||||
buffer, err := base58.Decode(b58)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(buffer) != 40 {
|
||||
return fmt.Errorf("invalid byte sequence")
|
||||
}
|
||||
if buffer[0] != version {
|
||||
return fmt.Errorf("invalid version")
|
||||
}
|
||||
if buffer[1] != nonZeroCurvePointVersion {
|
||||
return fmt.Errorf("invalid non-zero curve point version")
|
||||
}
|
||||
if buffer[2] != isCompressed {
|
||||
return fmt.Errorf("invalid compressed flag")
|
||||
}
|
||||
hash1 := sha256.Sum256(buffer[:36])
|
||||
hash2 := sha256.Sum256(hash1[:])
|
||||
if subtle.ConstantTimeCompare(hash2[:4], buffer[36:40]) != 1 {
|
||||
return fmt.Errorf("invalid checksum")
|
||||
}
|
||||
x := buffer[3:35]
|
||||
x[31] |= buffer[35] << 7
|
||||
value, err := new(curves.Ep).FromAffineCompressed(x)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk PublicKey) MarshalBinary() ([]byte, error) {
|
||||
return pk.value.ToAffineCompressed(), nil
|
||||
}
|
||||
|
||||
func (pk *PublicKey) UnmarshalBinary(input []byte) error {
|
||||
pt, err := new(curves.Ep).FromAffineCompressed(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk.value = pt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PublicKey) SetPointPallas(pallas *curves.PointPallas) {
|
||||
pk.value = pallas.GetEp()
|
||||
}
|
||||
|
||||
// SecretKey is the signing key
|
||||
type SecretKey struct {
|
||||
value *fq.Fq
|
||||
}
|
||||
|
||||
// GetPublicKey returns the corresponding verification
|
||||
func (sk SecretKey) GetPublicKey() *PublicKey {
|
||||
pk := new(curves.Ep).Mul(new(curves.Ep).Generator(), sk.value)
|
||||
return &PublicKey{pk}
|
||||
}
|
||||
|
||||
func (sk SecretKey) MarshalBinary() ([]byte, error) {
|
||||
t := sk.value.Bytes()
|
||||
return t[:], nil
|
||||
}
|
||||
|
||||
func (sk *SecretKey) UnmarshalBinary(input []byte) error {
|
||||
if len(input) != 32 {
|
||||
return fmt.Errorf("invalid byte sequence")
|
||||
}
|
||||
var buf [32]byte
|
||||
copy(buf[:], input)
|
||||
value, err := new(fq.Fq).SetBytes(&buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sk.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sk *SecretKey) SetFq(fq *fq.Fq) {
|
||||
sk.value = fq
|
||||
}
|
||||
|
||||
// NewKeys creates a new keypair using a CSPRNG
|
||||
func NewKeys() (*PublicKey, *SecretKey, error) {
|
||||
return NewKeysFromReader(crand.Reader)
|
||||
}
|
||||
|
||||
// NewKeysFromReader creates a new keypair using the specified reader
|
||||
func NewKeysFromReader(reader io.Reader) (*PublicKey, *SecretKey, error) {
|
||||
t := new(curves.ScalarPallas).Random(reader)
|
||||
sc, ok := t.(*curves.ScalarPallas)
|
||||
if !ok || t.IsZero() {
|
||||
return nil, nil, fmt.Errorf("invalid key")
|
||||
}
|
||||
sk := sc.GetFq()
|
||||
pk := new(curves.Ep).Mul(new(curves.Ep).Generator(), sk)
|
||||
if pk.IsIdentity() {
|
||||
return nil, nil, fmt.Errorf("invalid key")
|
||||
}
|
||||
|
||||
return &PublicKey{pk}, &SecretKey{sk}, nil
|
||||
}
|
||||
|
||||
// SignTransaction generates a signature over the specified txn and network id
|
||||
// See https://github.com/MinaProtocol/c-reference-signer/blob/master/crypto.c#L1020
|
||||
func (sk *SecretKey) SignTransaction(transaction *Transaction) (*Signature, error) {
|
||||
input := new(roinput).Init(3, 75)
|
||||
transaction.addRoInput(input)
|
||||
return sk.finishSchnorrSign(input, transaction.NetworkId)
|
||||
}
|
||||
|
||||
// SignMessage signs a _string_. this is somewhat non-standard; we do it by just adding bytes to the roinput.
|
||||
// See https://github.com/MinaProtocol/c-reference-signer/blob/master/crypto.c#L1020
|
||||
func (sk *SecretKey) SignMessage(message string) (*Signature, error) {
|
||||
input := new(roinput).Init(0, len(message))
|
||||
input.AddBytes([]byte(message))
|
||||
return sk.finishSchnorrSign(input, MainNet)
|
||||
}
|
||||
|
||||
func (sk *SecretKey) finishSchnorrSign(input *roinput, networkId NetworkType) (*Signature, error) {
|
||||
if sk.value.IsZero() {
|
||||
return nil, fmt.Errorf("invalid secret key")
|
||||
}
|
||||
pk := sk.GetPublicKey()
|
||||
k := sk.msgDerive(input, pk, networkId)
|
||||
if k.IsZero() {
|
||||
return nil, fmt.Errorf("invalid nonce generated")
|
||||
}
|
||||
// r = k*G
|
||||
r := new(curves.Ep).Generator()
|
||||
r.Mul(r, k)
|
||||
|
||||
if r.Y().IsOdd() {
|
||||
k.Neg(k)
|
||||
}
|
||||
rx := r.X()
|
||||
e := msgHash(pk, rx, input, ThreeW, networkId)
|
||||
|
||||
// S = k + e*sk
|
||||
e.Mul(e, sk.value)
|
||||
s := new(fq.Fq).Add(k, e)
|
||||
if rx.IsZero() || s.IsZero() {
|
||||
return nil, fmt.Errorf("invalid signature")
|
||||
}
|
||||
return &Signature{
|
||||
R: rx,
|
||||
S: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifyTransaction checks if the signature is over the given transaction using this public key
|
||||
func (pk *PublicKey) VerifyTransaction(sig *Signature, transaction *Transaction) error {
|
||||
input := new(roinput).Init(3, 75)
|
||||
transaction.addRoInput(input)
|
||||
return pk.finishSchnorrVerify(sig, input, transaction.NetworkId)
|
||||
}
|
||||
|
||||
// VerifyMessage checks if the claimed signature on a _string_ is valid. this is nonstandard; see above.
|
||||
func (pk *PublicKey) VerifyMessage(sig *Signature, message string) error {
|
||||
input := new(roinput).Init(0, len(message))
|
||||
input.AddBytes([]byte(message))
|
||||
return pk.finishSchnorrVerify(sig, input, MainNet)
|
||||
}
|
||||
|
||||
func (pk *PublicKey) finishSchnorrVerify(
|
||||
sig *Signature,
|
||||
input *roinput,
|
||||
networkId NetworkType,
|
||||
) error {
|
||||
if pk.value.IsIdentity() {
|
||||
return fmt.Errorf("invalid public key")
|
||||
}
|
||||
if sig.R.IsZero() || sig.S.IsZero() {
|
||||
return fmt.Errorf("invalid signature")
|
||||
}
|
||||
e := msgHash(pk, sig.R, input, ThreeW, networkId)
|
||||
sg := new(curves.Ep).Generator()
|
||||
sg.Mul(sg, sig.S)
|
||||
|
||||
epk := new(curves.Ep).Mul(pk.value, e)
|
||||
epk.Neg(epk)
|
||||
|
||||
r := new(curves.Ep).Add(sg, epk)
|
||||
if !r.Y().IsOdd() && r.X().Equal(sig.R) {
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("signature verification failed")
|
||||
}
|
||||
}
|
||||
|
||||
func msgHash(
|
||||
pk *PublicKey,
|
||||
rx *fp.Fp,
|
||||
input *roinput,
|
||||
hashType Permutation,
|
||||
networkId NetworkType,
|
||||
) *fq.Fq {
|
||||
input.AddFp(pk.value.X())
|
||||
input.AddFp(pk.value.Y())
|
||||
input.AddFp(rx)
|
||||
|
||||
ctx := new(Context).Init(hashType, networkId)
|
||||
fields := input.Fields()
|
||||
ctx.Update(fields)
|
||||
return ctx.Digest()
|
||||
}
|
||||
|
||||
func (sk SecretKey) msgDerive(msg *roinput, pk *PublicKey, networkId NetworkType) *fq.Fq {
|
||||
input := msg.Clone()
|
||||
input.AddFp(pk.value.X())
|
||||
input.AddFp(pk.value.Y())
|
||||
input.AddFq(sk.value)
|
||||
input.AddBytes([]byte{byte(networkId)})
|
||||
inputBytes := input.Bytes()
|
||||
|
||||
h, _ := blake2b.New(32, []byte{})
|
||||
_, _ = h.Write(inputBytes)
|
||||
hash := h.Sum(nil)
|
||||
|
||||
// Clear top two bits
|
||||
hash[31] &= 0x3F
|
||||
tmp := [4]uint64{
|
||||
binary.LittleEndian.Uint64(hash[:8]),
|
||||
binary.LittleEndian.Uint64(hash[8:16]),
|
||||
binary.LittleEndian.Uint64(hash[16:24]),
|
||||
binary.LittleEndian.Uint64(hash[24:32]),
|
||||
}
|
||||
return new(fq.Fq).SetRaw(&tmp)
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fq"
|
||||
)
|
||||
|
||||
func TestNewKeys(t *testing.T) {
|
||||
pk, sk, err := NewKeys()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sk)
|
||||
require.NotNil(t, pk)
|
||||
require.False(t, sk.value.IsZero())
|
||||
require.False(t, pk.value.IsIdentity())
|
||||
}
|
||||
|
||||
func TestSecretKeySignTransaction(t *testing.T) {
|
||||
// See https://github.com/MinaProtocol/c-reference-signer/blob/master/reference_signer.c#L15
|
||||
skValue := &fq.Fq{
|
||||
0xca14d6eed923f6e3, 0x61185a1b5e29e6b2, 0xe26d38de9c30753b, 0x3fdf0efb0a5714,
|
||||
}
|
||||
sk := &SecretKey{value: skValue}
|
||||
/*
|
||||
This illustrates constructing and signing the following transaction.
|
||||
amounts are in nanocodas.
|
||||
{
|
||||
"common": {
|
||||
"fee": "3",
|
||||
"fee_token": "1",
|
||||
"fee_payer_pk": "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
|
||||
"nonce": "200",
|
||||
"valid_until": "10000",
|
||||
"memo": "E4Yq8cQXC1m9eCYL8mYtmfqfJ5cVdhZawrPQ6ahoAay1NDYfTi44K"
|
||||
},
|
||||
"body": [
|
||||
"Payment",
|
||||
{
|
||||
"source_pk": "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
|
||||
"receiver_pk": "B62qrcFstkpqXww1EkSGrqMCwCNho86kuqBd4FrAAUsPxNKdiPzAUsy",
|
||||
"token_id": "1",
|
||||
"amount": "42"
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
feePayerPk := new(PublicKey)
|
||||
err := feePayerPk.ParseAddress("B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg")
|
||||
require.NoError(t, err)
|
||||
sourcePk := new(PublicKey)
|
||||
err = sourcePk.ParseAddress("B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg")
|
||||
require.NoError(t, err)
|
||||
receiverPk := new(PublicKey)
|
||||
err = receiverPk.ParseAddress("B62qrcFstkpqXww1EkSGrqMCwCNho86kuqBd4FrAAUsPxNKdiPzAUsy")
|
||||
require.NoError(t, err)
|
||||
txn := &Transaction{
|
||||
Fee: 3,
|
||||
FeeToken: 1,
|
||||
Nonce: 200,
|
||||
ValidUntil: 10000,
|
||||
Memo: "this is a memo",
|
||||
FeePayerPk: feePayerPk,
|
||||
SourcePk: sourcePk,
|
||||
ReceiverPk: receiverPk,
|
||||
TokenId: 1,
|
||||
Amount: 42,
|
||||
Locked: false,
|
||||
Tag: [3]bool{false, false, false},
|
||||
NetworkId: MainNet,
|
||||
}
|
||||
sig, err := sk.SignTransaction(txn)
|
||||
require.NoError(t, err)
|
||||
pk := sk.GetPublicKey()
|
||||
require.NoError(t, pk.VerifyTransaction(sig, txn))
|
||||
}
|
||||
|
||||
func TestSecretKeySignMessage(t *testing.T) {
|
||||
// See https://github.com/MinaProtocol/c-reference-signer/blob/master/reference_signer.c#L15
|
||||
skValue := &fq.Fq{
|
||||
0xca14d6eed923f6e3, 0x61185a1b5e29e6b2, 0xe26d38de9c30753b, 0x3fdf0efb0a5714,
|
||||
}
|
||||
sk := &SecretKey{value: skValue}
|
||||
sig, err := sk.SignMessage("A test message.")
|
||||
require.NoError(t, err)
|
||||
pk := sk.GetPublicKey()
|
||||
require.NoError(t, pk.VerifyMessage(sig, "A test message."))
|
||||
}
|
||||
|
||||
func TestSecretKeySignTransactionStaking(t *testing.T) {
|
||||
// https://github.com/MinaProtocol/c-reference-signer/blob/master/reference_signer.c#L128
|
||||
skValue := &fq.Fq{
|
||||
0xca14d6eed923f6e3, 0x61185a1b5e29e6b2, 0xe26d38de9c30753b, 0x3fdf0efb0a5714,
|
||||
}
|
||||
sk := &SecretKey{value: skValue}
|
||||
|
||||
feePayerPk := new(PublicKey)
|
||||
err := feePayerPk.ParseAddress("B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg")
|
||||
require.NoError(t, err)
|
||||
sourcePk := new(PublicKey)
|
||||
err = sourcePk.ParseAddress("B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg")
|
||||
require.NoError(t, err)
|
||||
receiverPk := new(PublicKey)
|
||||
err = receiverPk.ParseAddress("B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RR")
|
||||
require.NoError(t, err)
|
||||
txn := &Transaction{
|
||||
Fee: 3,
|
||||
FeeToken: 1,
|
||||
Nonce: 10,
|
||||
ValidUntil: 4000,
|
||||
Memo: "more delegates more fun",
|
||||
FeePayerPk: feePayerPk,
|
||||
SourcePk: sourcePk,
|
||||
ReceiverPk: receiverPk,
|
||||
TokenId: 1,
|
||||
Amount: 0,
|
||||
Locked: false,
|
||||
Tag: [3]bool{false, false, true},
|
||||
NetworkId: MainNet,
|
||||
}
|
||||
sig, err := sk.SignTransaction(txn)
|
||||
require.NoError(t, err)
|
||||
pk := sk.GetPublicKey()
|
||||
require.NoError(t, pk.VerifyTransaction(sig, txn))
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fp"
|
||||
)
|
||||
|
||||
// SBox is the type of exponentiation to perform
|
||||
type SBox int
|
||||
|
||||
const (
|
||||
Cube = iota // x^3
|
||||
Quint // x^5
|
||||
Sept // x^7
|
||||
Inverse // x^-1
|
||||
)
|
||||
|
||||
// Exp mutates f by computing x^3, x^5, x^7 or x^-1 as described in
|
||||
// https://eprint.iacr.org/2019/458.pdf page 8
|
||||
func (sbox SBox) Exp(f *fp.Fp) {
|
||||
switch sbox {
|
||||
case Cube:
|
||||
t := new(fp.Fp).Square(f)
|
||||
f.Mul(t, f)
|
||||
case Quint:
|
||||
t := new(fp.Fp).Square(f)
|
||||
t.Square(t)
|
||||
f.Mul(t, f)
|
||||
case Sept:
|
||||
f2 := new(fp.Fp).Square(f)
|
||||
f4 := new(fp.Fp).Square(f2)
|
||||
t := new(fp.Fp).Mul(f2, f4)
|
||||
f.Mul(t, f)
|
||||
case Inverse:
|
||||
f.Invert(f)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Permutation is the permute function to use
|
||||
type Permutation int
|
||||
|
||||
const (
|
||||
ThreeW = iota
|
||||
FiveW
|
||||
Three
|
||||
)
|
||||
|
||||
// Permute executes the poseidon hash function
|
||||
func (p Permutation) Permute(ctx *Context) {
|
||||
switch p {
|
||||
case ThreeW:
|
||||
for r := 0; r < ctx.fullRounds; r++ {
|
||||
ark(ctx, r)
|
||||
sbox(ctx)
|
||||
mds(ctx)
|
||||
}
|
||||
ark(ctx, ctx.fullRounds)
|
||||
case Three:
|
||||
fallthrough
|
||||
case FiveW:
|
||||
// Full rounds only
|
||||
for r := 0; r < ctx.fullRounds; r++ {
|
||||
sbox(ctx)
|
||||
mds(ctx)
|
||||
ark(ctx, r)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func ark(ctx *Context, round int) {
|
||||
for i := 0; i < ctx.spongeWidth; i++ {
|
||||
ctx.state[i].Add(ctx.state[i], ctx.roundKeys[round][i])
|
||||
}
|
||||
}
|
||||
|
||||
func sbox(ctx *Context) {
|
||||
for i := 0; i < ctx.spongeWidth; i++ {
|
||||
ctx.sBox.Exp(ctx.state[i])
|
||||
}
|
||||
}
|
||||
|
||||
func mds(ctx *Context) {
|
||||
state2 := make([]*fp.Fp, len(ctx.state))
|
||||
for i := range ctx.state {
|
||||
state2[i] = new(fp.Fp).SetZero()
|
||||
}
|
||||
for row := 0; row < ctx.spongeWidth; row++ {
|
||||
for col := 0; col < ctx.spongeWidth; col++ {
|
||||
t := new(fp.Fp).Mul(ctx.state[col], ctx.mdsMatrix[row][col])
|
||||
state2[row].Add(state2[row], t)
|
||||
}
|
||||
}
|
||||
for i, f := range state2 {
|
||||
ctx.state[i].Set(f)
|
||||
}
|
||||
}
|
||||
|
||||
// NetworkType is which Mina network id to use
|
||||
type NetworkType int
|
||||
|
||||
const (
|
||||
TestNet = iota
|
||||
MainNet
|
||||
NullNet
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fp"
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fq"
|
||||
)
|
||||
|
||||
func TestPoseidonHash(t *testing.T) {
|
||||
// Reference https://github.com/o1-labs/proof-systems/blob/master/oracle/tests/test_vectors/3w.json
|
||||
testVectors := []struct {
|
||||
input []*fp.Fp
|
||||
output *fq.Fq
|
||||
}{
|
||||
{
|
||||
input: []*fp.Fp{},
|
||||
output: hexToFq("1b3251b6912d82edc78bbb0a5c88f0c6fde1781bc3e654123fa6862a4c63e617"),
|
||||
},
|
||||
{
|
||||
input: []*fp.Fp{
|
||||
hexToFp("df698e389c6f1987ffe186d806f8163738f5bf22e8be02572cce99dc6a4ab030"),
|
||||
},
|
||||
output: hexToFq("f9b1b6c5f8c98017c6b35ac74bc689b6533d6dbbee1fd868831b637a43ea720c"),
|
||||
},
|
||||
{
|
||||
input: []*fp.Fp{
|
||||
hexToFp("56b648a5a85619814900a6b40375676803fe16fb1ad2d1fb79115eb1b52ac026"),
|
||||
hexToFp("f26a8a03d9c9bbd9c6b2a1324d2a3f4d894bafe25a7e4ad1a498705f4026ff2f"),
|
||||
},
|
||||
output: hexToFq("7a556e93bcfbd27b55867f533cd1df293a7def60dd929a086fdd4e70393b0918"),
|
||||
},
|
||||
{
|
||||
input: []*fp.Fp{
|
||||
hexToFp("075c41fa23e4690694df5ded43624fd60ab7ee6ec6dd48f44dc71bc206cecb26"),
|
||||
hexToFp("a4e2beebb09bd02ad42bbccc11051e8262b6ef50445d8382b253e91ab1557a0d"),
|
||||
hexToFp("7dfc23a1242d9c0d6eb16e924cfba342bb2fccf36b8cbaf296851f2e6c469639"),
|
||||
},
|
||||
output: hexToFq("f94b39a919aab06f43f4a4b5a3e965b719a4dbd2b9cd26d2bba4197b10286b35"),
|
||||
},
|
||||
{
|
||||
input: []*fp.Fp{
|
||||
hexToFp("a1a659b14e80d47318c6fcdbbd388de4272d5c2815eb458cf4f196d52403b639"),
|
||||
hexToFp("5e33065d1801131b64d13038ff9693a7ef6283f24ec8c19438d112ff59d50f04"),
|
||||
hexToFp("38a8f4d0a9b6d0facdc4e825f6a2ba2b85401d5de119bf9f2bcb908235683e06"),
|
||||
hexToFp("3456d0313a30d7ccb23bd71ed6aa70ab234dad683d8187b677aef73f42f4f52e"),
|
||||
},
|
||||
output: hexToFq("cc1ccfa964fd6ef9ff1994beb53cfce9ebe1212847ce30e4c64f0777875aec34"),
|
||||
},
|
||||
{
|
||||
input: []*fp.Fp{
|
||||
hexToFp("bccfee48dc76bb991c97bd531cf489f4ee37a66a15f5cfac31bdd4f159d4a905"),
|
||||
hexToFp("2d106fb21a262f85fd400a995c6d74bad48d8adab2554046871c215e585b072b"),
|
||||
hexToFp("8300e93ee8587956534d0756bb2aa575e5878c670cff5c8e3e55c62632333c06"),
|
||||
hexToFp("879c32da31566f6d16afdefff94cba5260fec1057e97f19fc9a61dc2c54a6417"),
|
||||
hexToFp("9c0aa6e5501cfb2d08aeaea5b3cddac2c9bee85d13324118b44bafb63a59611e"),
|
||||
},
|
||||
output: hexToFq("cf7b9c2128f0e2c0fed4e1eca8d5954b629640c2458d24ba238c1bd3ccbc8e12"),
|
||||
},
|
||||
}
|
||||
for _, tv := range testVectors {
|
||||
ctx := new(Context).Init(ThreeW, NetworkType(NullNet))
|
||||
ctx.Update(tv.input)
|
||||
res := ctx.Digest()
|
||||
require.True(t, res.Equal(tv.output))
|
||||
}
|
||||
testVectors = []struct {
|
||||
input []*fp.Fp
|
||||
output *fq.Fq
|
||||
}{
|
||||
{
|
||||
input: []*fp.Fp{
|
||||
hexToFp("0f48c65bd25f85f3e4ea4efebeb75b797bd743603be04b4ead845698b76bd331"),
|
||||
hexToFp("0f48c65bd25f85f3e4ea4efebeb75b797bd743603be04b4ead845698b76bd331"),
|
||||
hexToFp("f34b505e1a05ecfb327d8d664ff6272ddf5cc1f69618bb6a4407e9533067e703"),
|
||||
hexToFp("0f48c65bd25f85f3e4ea4efebeb75b797bd743603be04b4ead845698b76bd331"),
|
||||
hexToFp("ac7cb9c568955737eca56f855954f394cc6b05ac9b698ba3d974f029177cb427"),
|
||||
hexToFp("010141eca06991fe68dcbd799d93037522dc6c4dead1d77202e8c2ea8f5b1005"),
|
||||
hexToFp("0300000000000000010000000000000090010000204e0000021ce8d0d2e64012"),
|
||||
hexToFp("9b030903692b6b7b030000000000000000000000000000000000800100000000"),
|
||||
hexToFp("000000a800000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
output: &fq.Fq{
|
||||
1348483115953159504,
|
||||
14115862092770957043,
|
||||
15858311826851986539,
|
||||
1644043871107534594,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tv := range testVectors {
|
||||
ctx := new(Context).Init(ThreeW, NetworkType(MainNet))
|
||||
ctx.Update(tv.input)
|
||||
res := ctx.Digest()
|
||||
require.True(t, res.Equal(tv.output))
|
||||
}
|
||||
}
|
||||
|
||||
func hexToFp(s string) *fp.Fp {
|
||||
var buffer [32]byte
|
||||
input, _ := hex.DecodeString(s)
|
||||
copy(buffer[:], input)
|
||||
f, _ := new(fp.Fp).SetBytes(&buffer)
|
||||
return f
|
||||
}
|
||||
|
||||
func hexToFq(s string) *fq.Fq {
|
||||
var buffer [32]byte
|
||||
input, _ := hex.DecodeString(s)
|
||||
copy(buffer[:], input)
|
||||
f, _ := new(fq.Fq).SetBytes(&buffer)
|
||||
return f
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fp"
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fq"
|
||||
)
|
||||
|
||||
// Handles the packing of bits and fields according to Mina spec
|
||||
type roinput struct {
|
||||
fields []*fp.Fp
|
||||
bits *BitVector
|
||||
}
|
||||
|
||||
var conv = map[bool]int{
|
||||
true: 1,
|
||||
false: 0,
|
||||
}
|
||||
|
||||
func (r *roinput) Init(fields int, bytes int) *roinput {
|
||||
r.fields = make([]*fp.Fp, 0, fields)
|
||||
r.bits = NewBitVector(make([]byte, bytes), 0)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *roinput) Clone() *roinput {
|
||||
t := new(roinput)
|
||||
t.fields = make([]*fp.Fp, len(r.fields))
|
||||
for i, f := range r.fields {
|
||||
t.fields[i] = new(fp.Fp).Set(f)
|
||||
}
|
||||
buffer := r.bits.Bytes()
|
||||
data := make([]byte, len(buffer))
|
||||
copy(data, buffer)
|
||||
t.bits = NewBitVector(data, r.bits.Length())
|
||||
return t
|
||||
}
|
||||
|
||||
func (r *roinput) AddFp(fp *fp.Fp) {
|
||||
r.fields = append(r.fields, fp)
|
||||
}
|
||||
|
||||
func (r *roinput) AddFq(fq *fq.Fq) {
|
||||
scalar := fq.ToRaw()
|
||||
// Mina handles fields as 255 bit numbers
|
||||
// with each field we lose a bit
|
||||
for i := 0; i < 255; i++ {
|
||||
limb := i / 64
|
||||
idx := i % 64
|
||||
b := (scalar[limb] >> idx) & 1
|
||||
r.bits.Append(byte(b))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roinput) AddBit(b bool) {
|
||||
r.bits.Append(byte(conv[b]))
|
||||
}
|
||||
|
||||
func (r *roinput) AddBytes(input []byte) {
|
||||
for _, b := range input {
|
||||
for i := 0; i < 8; i++ {
|
||||
r.bits.Append(byte((b >> i) & 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roinput) AddUint32(x uint32) {
|
||||
for i := 0; i < 32; i++ {
|
||||
r.bits.Append(byte((x >> i) & 1))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roinput) AddUint64(x uint64) {
|
||||
for i := 0; i < 64; i++ {
|
||||
r.bits.Append(byte((x >> i) & 1))
|
||||
}
|
||||
}
|
||||
|
||||
func (r roinput) Bytes() []byte {
|
||||
out := make([]byte, (r.bits.Length()+7)/8+32*len(r.fields))
|
||||
res := NewBitVector(out, 0)
|
||||
// Mina handles fields as 255 bit numbers
|
||||
// with each field we lose a bit
|
||||
for _, f := range r.fields {
|
||||
buf := f.ToRaw()
|
||||
for i := 0; i < 255; i++ {
|
||||
limb := i / 64
|
||||
idx := i % 64
|
||||
b := (buf[limb] >> idx) & 1
|
||||
res.Append(byte(b))
|
||||
}
|
||||
}
|
||||
for i := 0; i < r.bits.Length(); i++ {
|
||||
res.Append(r.bits.Element(i))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r roinput) Fields() []*fp.Fp {
|
||||
fields := make([]*fp.Fp, 0, len(r.fields)+r.bits.Length()/256)
|
||||
for _, f := range r.fields {
|
||||
fields = append(fields, new(fp.Fp).Set(f))
|
||||
}
|
||||
const maxChunkSize = 254
|
||||
bitsConsumed := 0
|
||||
bitIdx := 0
|
||||
|
||||
for bitsConsumed < r.bits.Length() {
|
||||
var chunk [4]uint64
|
||||
|
||||
remaining := r.bits.Length() - bitsConsumed
|
||||
var chunkSizeInBits int
|
||||
if remaining > maxChunkSize {
|
||||
chunkSizeInBits = maxChunkSize
|
||||
} else {
|
||||
chunkSizeInBits = remaining
|
||||
}
|
||||
|
||||
for i := 0; i < chunkSizeInBits; i++ {
|
||||
limb := i >> 6
|
||||
idx := i & 0x3F
|
||||
b := r.bits.Element(bitIdx)
|
||||
chunk[limb] |= uint64(b) << idx
|
||||
bitIdx++
|
||||
}
|
||||
fields = append(fields, new(fp.Fp).SetRaw(&chunk))
|
||||
bitsConsumed += chunkSizeInBits
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fp"
|
||||
"github.com/sonr-io/sonr/crypto/core/curves/native/pasta/fq"
|
||||
)
|
||||
|
||||
// Signature is a Mina compatible signature either for payment or delegation
|
||||
type Signature struct {
|
||||
R *fp.Fp
|
||||
S *fq.Fq
|
||||
}
|
||||
|
||||
func (sig Signature) MarshalBinary() ([]byte, error) {
|
||||
var buf [64]byte
|
||||
rx := sig.R.Bytes()
|
||||
s := sig.S.Bytes()
|
||||
copy(buf[:32], rx[:])
|
||||
copy(buf[32:], s[:])
|
||||
return buf[:], nil
|
||||
}
|
||||
|
||||
func (sig *Signature) UnmarshalBinary(input []byte) error {
|
||||
if len(input) != 64 {
|
||||
return fmt.Errorf("invalid byte sequence")
|
||||
}
|
||||
var buf [32]byte
|
||||
copy(buf[:], input[:32])
|
||||
rx, err := new(fp.Fp).SetBytes(&buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(buf[:], input[32:])
|
||||
s, err := new(fq.Fq).SetBytes(&buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sig.R = rx
|
||||
sig.S = s
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mina
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/btcutil/base58"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/core/curves"
|
||||
)
|
||||
|
||||
// Transaction is a Mina transaction for payments or delegations
|
||||
type Transaction struct {
|
||||
Fee, FeeToken uint64
|
||||
FeePayerPk *PublicKey
|
||||
Nonce, ValidUntil uint32
|
||||
Memo string
|
||||
Tag [3]bool
|
||||
SourcePk, ReceiverPk *PublicKey
|
||||
TokenId, Amount uint64
|
||||
Locked bool
|
||||
NetworkId NetworkType
|
||||
}
|
||||
|
||||
type txnJson struct {
|
||||
Common txnCommonJson
|
||||
Body [2]any
|
||||
}
|
||||
|
||||
type txnCommonJson struct {
|
||||
Fee uint64 `json:"fee"`
|
||||
FeeToken uint64 `json:"fee_token"`
|
||||
FeePayerPk string `json:"fee_payer_pk"`
|
||||
Nonce uint32 `json:"nonce"`
|
||||
ValidUntil uint32 `json:"valid_until"`
|
||||
Memo string `json:"memo"`
|
||||
NetworkId uint8 `json:"network_id"`
|
||||
}
|
||||
|
||||
type txnBodyPaymentJson struct {
|
||||
SourcePk string `json:"source_pk"`
|
||||
ReceiverPk string `json:"receiver_pk"`
|
||||
TokenId uint64 `json:"token_id"`
|
||||
Amount uint64 `json:"amount"`
|
||||
}
|
||||
|
||||
type txnBodyDelegationJson struct {
|
||||
Delegator string `json:"delegator"`
|
||||
NewDelegate string `json:"new_delegate"`
|
||||
}
|
||||
|
||||
func (txn *Transaction) MarshalBinary() ([]byte, error) {
|
||||
mapper := map[bool]byte{
|
||||
true: 1,
|
||||
false: 0,
|
||||
}
|
||||
out := make([]byte, 175)
|
||||
binary.LittleEndian.PutUint64(out, txn.Fee)
|
||||
binary.LittleEndian.PutUint64(out[8:16], txn.FeeToken)
|
||||
copy(out[16:48], txn.FeePayerPk.value.ToAffineCompressed())
|
||||
binary.LittleEndian.PutUint32(out[48:52], txn.Nonce)
|
||||
binary.LittleEndian.PutUint32(out[52:56], txn.ValidUntil)
|
||||
|
||||
out[56] = 0x01
|
||||
out[57] = byte(len(txn.Memo))
|
||||
copy(out[58:90], txn.Memo[:])
|
||||
out[90] = mapper[txn.Tag[0]]
|
||||
out[91] = mapper[txn.Tag[1]]
|
||||
out[92] = mapper[txn.Tag[2]]
|
||||
copy(out[93:125], txn.SourcePk.value.ToAffineCompressed())
|
||||
copy(out[125:157], txn.ReceiverPk.value.ToAffineCompressed())
|
||||
binary.LittleEndian.PutUint64(out[157:165], txn.TokenId)
|
||||
binary.LittleEndian.PutUint64(out[165:173], txn.Amount)
|
||||
out[173] = mapper[txn.Locked]
|
||||
out[174] = byte(txn.NetworkId)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (txn *Transaction) UnmarshalBinary(input []byte) error {
|
||||
mapper := map[byte]bool{
|
||||
1: true,
|
||||
0: false,
|
||||
}
|
||||
if len(input) < 175 {
|
||||
return fmt.Errorf("invalid byte sequence")
|
||||
}
|
||||
feePayerPk := new(PublicKey)
|
||||
sourcePk := new(PublicKey)
|
||||
receiverPk := new(PublicKey)
|
||||
err := feePayerPk.UnmarshalBinary(input[16:48])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sourcePk.UnmarshalBinary(input[93:125])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = receiverPk.UnmarshalBinary(input[125:157])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txn.Fee = binary.LittleEndian.Uint64(input[:8])
|
||||
txn.FeeToken = binary.LittleEndian.Uint64(input[8:16])
|
||||
txn.FeePayerPk = feePayerPk
|
||||
txn.Nonce = binary.LittleEndian.Uint32(input[48:52])
|
||||
txn.ValidUntil = binary.LittleEndian.Uint32(input[52:56])
|
||||
txn.Memo = string(input[58 : 58+input[57]])
|
||||
txn.Tag[0] = mapper[input[90]]
|
||||
txn.Tag[1] = mapper[input[91]]
|
||||
txn.Tag[2] = mapper[input[92]]
|
||||
txn.SourcePk = sourcePk
|
||||
txn.ReceiverPk = receiverPk
|
||||
txn.TokenId = binary.LittleEndian.Uint64(input[157:165])
|
||||
txn.Amount = binary.LittleEndian.Uint64(input[165:173])
|
||||
txn.Locked = mapper[input[173]]
|
||||
txn.NetworkId = NetworkType(input[174])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (txn *Transaction) UnmarshalJSON(input []byte) error {
|
||||
var t txnJson
|
||||
err := json.Unmarshal(input, &t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
strType, ok := t.Body[0].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
memo, _, err := base58.CheckDecode(t.Common.Memo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch strType {
|
||||
case "Payment":
|
||||
b, ok := t.Body[1].(txnBodyPaymentJson)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
feePayerPk := new(PublicKey)
|
||||
err = feePayerPk.ParseAddress(b.SourcePk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
receiverPk := new(PublicKey)
|
||||
err = receiverPk.ParseAddress(b.ReceiverPk)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
txn.FeePayerPk = feePayerPk
|
||||
txn.ReceiverPk = receiverPk
|
||||
case "Stake_delegation":
|
||||
bType, ok := t.Body[1].([2]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
delegateType, ok := bType[0].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
if delegateType == "Set_delegate" {
|
||||
b, ok := bType[1].(txnBodyDelegationJson)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
feePayerPk := new(PublicKey)
|
||||
err = feePayerPk.ParseAddress(b.Delegator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
receiverPk := new(PublicKey)
|
||||
err = receiverPk.ParseAddress(b.NewDelegate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txn.FeePayerPk = feePayerPk
|
||||
txn.ReceiverPk = receiverPk
|
||||
} else {
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected type")
|
||||
}
|
||||
txn.Memo = string(memo[2 : 2+memo[1]])
|
||||
sourcePk := new(PublicKey)
|
||||
sourcePk.value = new(curves.Ep).Set(txn.FeePayerPk.value)
|
||||
txn.Fee = t.Common.Fee
|
||||
txn.FeeToken = t.Common.FeeToken
|
||||
txn.Nonce = t.Common.Nonce
|
||||
txn.ValidUntil = t.Common.ValidUntil
|
||||
txn.NetworkId = NetworkType(t.Common.NetworkId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (txn Transaction) addRoInput(input *roinput) {
|
||||
input.AddFp(txn.FeePayerPk.value.X())
|
||||
input.AddFp(txn.SourcePk.value.X())
|
||||
input.AddFp(txn.ReceiverPk.value.X())
|
||||
|
||||
input.AddUint64(txn.Fee)
|
||||
input.AddUint64(txn.FeeToken)
|
||||
input.AddBit(txn.FeePayerPk.value.Y().IsOdd())
|
||||
input.AddUint32(txn.Nonce)
|
||||
input.AddUint32(txn.ValidUntil)
|
||||
memo := [34]byte{0x01, byte(len(txn.Memo))}
|
||||
copy(memo[2:], txn.Memo)
|
||||
input.AddBytes(memo[:])
|
||||
for _, b := range txn.Tag {
|
||||
input.AddBit(b)
|
||||
}
|
||||
|
||||
input.AddBit(txn.SourcePk.value.Y().IsOdd())
|
||||
input.AddBit(txn.ReceiverPk.value.Y().IsOdd())
|
||||
input.AddUint64(txn.TokenId)
|
||||
input.AddUint64(txn.Amount)
|
||||
input.AddBit(txn.Locked)
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// This file implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
// This code is a port of the public domain, “ref10” implementation of ed25519
|
||||
// from SUPERCOP.
|
||||
|
||||
package nem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
cryptorand "crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"filippo.io/edwards25519"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64
|
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32
|
||||
)
|
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
type PublicKey []byte
|
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
type PrivateKey []byte
|
||||
|
||||
// Bytes returns the publicKey in byte array
|
||||
func (p PublicKey) Bytes() []byte {
|
||||
return p
|
||||
}
|
||||
|
||||
// Public returns the PublicKey corresponding to priv.
|
||||
func (priv PrivateKey) Public() crypto.PublicKey {
|
||||
publicKey := make([]byte, PublicKeySize)
|
||||
copy(publicKey, priv[32:])
|
||||
return PublicKey(publicKey)
|
||||
}
|
||||
|
||||
func Keccak512(data []byte) ([]byte, error) {
|
||||
k512 := sha3.NewLegacyKeccak512()
|
||||
_, err := k512.Write(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return k512.Sum(nil), nil
|
||||
}
|
||||
|
||||
// Seed returns the private key seed corresponding to priv. It is provided for
|
||||
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
|
||||
// in this package.
|
||||
func (priv PrivateKey) Seed() []byte {
|
||||
seed := make([]byte, SeedSize)
|
||||
copy(seed, priv[:32])
|
||||
return seed
|
||||
}
|
||||
|
||||
// Sign signs the given message with priv.
|
||||
// Ed25519 performs two passes over messages to be signed and therefore cannot
|
||||
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
|
||||
// indicate the message hasn't been hashed. This can be achieved by passing
|
||||
// crypto.Hash(0) as the value for opts.
|
||||
func (priv PrivateKey) Sign(
|
||||
rand io.Reader,
|
||||
message []byte,
|
||||
opts crypto.SignerOpts,
|
||||
) (signature []byte, err error) {
|
||||
if opts.HashFunc() != crypto.Hash(0) {
|
||||
return nil, fmt.Errorf("ed25519: cannot sign hashed message")
|
||||
}
|
||||
sig, err := Sign(priv, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
|
||||
if rand == nil {
|
||||
rand = cryptorand.Reader
|
||||
}
|
||||
|
||||
seed := make([]byte, SeedSize)
|
||||
if _, err := io.ReadFull(rand, seed); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
privateKey, err := NewKeyFromSeed(seed)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
publicKey := make([]byte, PublicKeySize)
|
||||
copy(publicKey, privateKey[32:])
|
||||
|
||||
return publicKey, privateKey, nil
|
||||
}
|
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) (PrivateKey, error) {
|
||||
// Outline the function body so that the returned key can be stack-allocated.
|
||||
privateKey := make([]byte, PrivateKeySize)
|
||||
err := newKeyFromSeed(privateKey, seed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func newKeyFromSeed(privateKey, seed []byte) error {
|
||||
if l := len(seed); l != SeedSize {
|
||||
return fmt.Errorf("ed25519: bad seed length: %d", l)
|
||||
}
|
||||
|
||||
// Weird required step to get compatibility with the NEM test vectors
|
||||
// Have to reverse the bytes from the given seed
|
||||
digest, err := Keccak512(internal.ReverseScalarBytes(seed))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sc, err := edwards25519.NewScalar().SetBytesWithClamping(digest[:32])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
A := edwards25519.Point{}
|
||||
A.ScalarBaseMult(sc)
|
||||
publicKeyBytes := A.Bytes()
|
||||
|
||||
copy(privateKey, seed)
|
||||
copy(privateKey[32:], publicKeyBytes[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) ([]byte, error) {
|
||||
// Outline the function body so that the returned signature can be
|
||||
// stack-allocated.
|
||||
signature := make([]byte, SignatureSize)
|
||||
err := sign(signature, privateKey, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
func sign(signature, privateKey, message []byte) error {
|
||||
if l := len(privateKey); l != PrivateKeySize {
|
||||
return fmt.Errorf("ed25519: bad private key length: %d", l)
|
||||
}
|
||||
|
||||
seed := privateKey[:32]
|
||||
digest, err := Keccak512(internal.ReverseScalarBytes(seed))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// H(seed) ie. privkey
|
||||
expandedSecretKey := digest[:32]
|
||||
sc, err := edwards25519.NewScalar().SetBytesWithClamping(expandedSecretKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// r = H(H(seed) + msg)
|
||||
hEngine := sha3.NewLegacyKeccak512()
|
||||
_, err = hEngine.Write(digest[32:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = hEngine.Write(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hOut1 [64]byte
|
||||
hEngine.Sum(hOut1[:0])
|
||||
|
||||
// hash output -> scalar
|
||||
// Take 64 byte output from keccak512 so need to set bytes as long
|
||||
r, err := edwards25519.NewScalar().SetUniformBytes(hOut1[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// R = r*G
|
||||
R := edwards25519.Point{}
|
||||
R.ScalarBaseMult(r)
|
||||
RBytes := R.Bytes()
|
||||
|
||||
// s = H(R + pubkey + msg)
|
||||
hEngine.Reset()
|
||||
_, err = hEngine.Write(RBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = hEngine.Write(privateKey[32:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = hEngine.Write(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hOut2 [64]byte
|
||||
hEngine.Sum(hOut2[:0])
|
||||
|
||||
// hash output -> scalar
|
||||
// Take 64 byte output from keccak512 so need to set bytes as long
|
||||
h, err := edwards25519.NewScalar().SetUniformBytes(hOut2[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// s = (r + h * privKey)
|
||||
s := edwards25519.NewScalar().MultiplyAdd(h, sc, r)
|
||||
|
||||
copy(signature[:], RBytes)
|
||||
copy(signature[32:], s.Bytes())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
// Previously publicKey is of type PublicKey
|
||||
func Verify(publicKey PublicKey, message, sig []byte) (bool, error) {
|
||||
if l := len(publicKey); l != PublicKeySize {
|
||||
return false, fmt.Errorf("ed25519: bad public key length: %d", l)
|
||||
}
|
||||
|
||||
if len(sig) != SignatureSize || sig[63]&224 != 0 {
|
||||
return false, fmt.Errorf("ed25519: bad signature size: %d", len(sig))
|
||||
}
|
||||
|
||||
RBytes := sig[:32]
|
||||
sBytes := sig[32:]
|
||||
|
||||
var publicKeyBytes [32]byte
|
||||
copy(publicKeyBytes[:], publicKey)
|
||||
|
||||
A := edwards25519.Point{}
|
||||
_, err := A.SetBytes(publicKeyBytes[:])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
negA := edwards25519.Point{}
|
||||
negA.Negate(&A)
|
||||
|
||||
// h = H(R + pubkey + msg)
|
||||
hEngine := sha3.NewLegacyKeccak512()
|
||||
_, err = hEngine.Write(RBytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = hEngine.Write(publicKeyBytes[:])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = hEngine.Write(message)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var hOut1 [64]byte
|
||||
hEngine.Sum(hOut1[:0])
|
||||
|
||||
// hash output -> scalar
|
||||
// Take 64 byte output from keccak512 so need to set bytes as long
|
||||
h, err := edwards25519.NewScalar().SetUniformBytes(hOut1[:])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// s was generated in sign so can set as canonical
|
||||
s, err := edwards25519.NewScalar().SetCanonicalBytes(sBytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// R' = s*G - h*Pubkey = h*negPubkey + s*G
|
||||
RPrime := edwards25519.Point{}
|
||||
RPrime.VarTimeDoubleScalarBaseMult(h, &negA, s)
|
||||
RPrimeBytes := RPrime.Bytes()
|
||||
|
||||
// Check R == R'
|
||||
return bytes.Equal(RBytes, RPrimeBytes), nil
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package nem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
type KeyPair struct {
|
||||
Privkey string `json:"privateKey"`
|
||||
Pubkey string `json:"publicKey"`
|
||||
}
|
||||
|
||||
type TestSig struct {
|
||||
Privkey string `json:"privateKey"`
|
||||
Pubkey string `json:"publicKey"`
|
||||
Data string `json:"data"`
|
||||
Length int `json:"length"`
|
||||
Sig string `json:"signature"`
|
||||
}
|
||||
|
||||
// NOTE: NEM provides no test vectors for Keccak512, but has test vectors for Keccak256
|
||||
// We use Keccak256 and 512 in the exact same manner, so ensuring this test passes
|
||||
// gives decent confidence in our Keccak512 use as well
|
||||
func TestKeccak256SanityCheck(t *testing.T) {
|
||||
data := "A6151D4904E18EC288243028CEDA30556E6C42096AF7150D6A7232CA5DBA52BD2192E23DAA5FA2BEA3D4BD95EFA2389CD193FCD3376E70A5C097B32C1C62C80AF9D710211545F7CDDDF63747420281D64529477C61E721273CFD78F8890ABB4070E97BAA52AC8FF61C26D195FC54C077DEF7A3F6F79B36E046C1A83CE9674BA1983EC2FB58947DE616DD797D6499B0385D5E8A213DB9AD5078A8E0C940FF0CB6BF92357EA5609F778C3D1FB1E7E36C35DB873361E2BE5C125EA7148EFF4A035B0CCE880A41190B2E22924AD9D1B82433D9C023924F2311315F07B88BFD42850047BF3BE785C4CE11C09D7E02065D30F6324365F93C5E7E423A07D754EB314B5FE9DB4614275BE4BE26AF017ABDC9C338D01368226FE9AF1FB1F815E7317BDBB30A0F36DC69"
|
||||
toMatch := "4E9E79AB7434F6C7401FB3305D55052EE829B9E46D5D05D43B59FEFB32E9A619"
|
||||
toMatchBytes, err := hex.DecodeString(toMatch)
|
||||
require.NoError(t, err)
|
||||
dataBytes, err := hex.DecodeString(data)
|
||||
require.NoError(t, err)
|
||||
k256 := sha3.NewLegacyKeccak256()
|
||||
_, err = k256.Write(dataBytes)
|
||||
require.NoError(t, err)
|
||||
var hashed []byte
|
||||
hashed = k256.Sum(hashed)
|
||||
require.Equal(t, hashed, toMatchBytes)
|
||||
}
|
||||
|
||||
// Test that the pubkey can get derived correctly from privkey
|
||||
func TestPrivToPubkey(t *testing.T) {
|
||||
testVectors := GetPrivToPubkeyTestCases()
|
||||
|
||||
for _, pair := range testVectors {
|
||||
privkeyBytes, err := hex.DecodeString(pair.Privkey)
|
||||
require.NoError(t, err)
|
||||
pubkeyBytes, err := hex.DecodeString(pair.Pubkey)
|
||||
require.NoError(t, err)
|
||||
|
||||
privKeyCalced, err := NewKeyFromSeed(privkeyBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
pubKeyCalced := privKeyCalced.Public().(PublicKey)
|
||||
|
||||
require.Equal(t, pubKeyCalced.Bytes(), pubkeyBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we can:
|
||||
// Get pubkey from privkey
|
||||
// Obtain the correct signature
|
||||
// Verify the test vector provided signature
|
||||
func TestSigs(t *testing.T) {
|
||||
testVectors := GetSigTestCases()
|
||||
|
||||
for _, ts := range testVectors {
|
||||
// Test priv -> pubkey again
|
||||
privkeyBytes, err := hex.DecodeString(ts.Privkey)
|
||||
require.NoError(t, err)
|
||||
pubkeyBytes, err := hex.DecodeString(ts.Pubkey)
|
||||
require.NoError(t, err)
|
||||
|
||||
privKeyCalced, err := NewKeyFromSeed(privkeyBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
pubKeyCalced := privKeyCalced.Public().(PublicKey)
|
||||
|
||||
require.True(t, bytes.Equal(pubKeyCalced.Bytes(), pubkeyBytes))
|
||||
|
||||
dataBytes, err := hex.DecodeString(ts.Data)
|
||||
require.NoError(t, err)
|
||||
sigBytes, err := hex.DecodeString(ts.Sig)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test sign
|
||||
sigCalced, err := Sign(privKeyCalced, dataBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, bytes.Equal(sigCalced, sigBytes))
|
||||
|
||||
// Test verify
|
||||
verified, err := Verify(pubKeyCalced, dataBytes, sigBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, verified)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Test cases were obtained from NEM
|
||||
// See link: https://github.com/symbol/test-vectors
|
||||
// Pulled 5 test vectors for each test case, at time of writing confirmed that all 10000 vectors passed
|
||||
func GetPrivToPubkeyTestCases() []KeyPair {
|
||||
var toReturn []KeyPair
|
||||
|
||||
kp1 := KeyPair{
|
||||
Privkey: "575DBB3062267EFF57C970A336EBBC8FBCFE12C5BD3ED7BC11EB0481D7704CED",
|
||||
Pubkey: "C5F54BA980FCBB657DBAAA42700539B207873E134D2375EFEAB5F1AB52F87844",
|
||||
}
|
||||
|
||||
kp2 := KeyPair{
|
||||
Privkey: "5B0E3FA5D3B49A79022D7C1E121BA1CBBF4DB5821F47AB8C708EF88DEFC29BFE",
|
||||
Pubkey: "96EB2A145211B1B7AB5F0D4B14F8ABC8D695C7AEE31A3CFC2D4881313C68EEA3",
|
||||
}
|
||||
|
||||
kp3 := KeyPair{
|
||||
Privkey: "738BA9BB9110AEA8F15CAA353ACA5653B4BDFCA1DB9F34D0EFED2CE1325AEEDA",
|
||||
Pubkey: "2D8425E4CA2D8926346C7A7CA39826ACD881A8639E81BD68820409C6E30D142A",
|
||||
}
|
||||
|
||||
kp4 := KeyPair{
|
||||
Privkey: "E8BF9BC0F35C12D8C8BF94DD3A8B5B4034F1063948E3CC5304E55E31AA4B95A6",
|
||||
Pubkey: "4FEED486777ED38E44C489C7C4E93A830E4C4A907FA19A174E630EF0F6ED0409",
|
||||
}
|
||||
|
||||
kp5 := KeyPair{
|
||||
Privkey: "C325EA529674396DB5675939E7988883D59A5FC17A28CA977E3BA85370232A83",
|
||||
Pubkey: "83EE32E4E145024D29BCA54F71FA335A98B3E68283F1A3099C4D4AE113B53E54",
|
||||
}
|
||||
|
||||
toReturn = append(toReturn, kp1, kp2, kp3, kp4, kp5)
|
||||
|
||||
return toReturn
|
||||
}
|
||||
|
||||
func GetSigTestCases() []TestSig {
|
||||
var toReturn []TestSig
|
||||
|
||||
t1 := TestSig{
|
||||
Privkey: "ABF4CF55A2B3F742D7543D9CC17F50447B969E6E06F5EA9195D428AB12B7318D",
|
||||
Pubkey: "8A558C728C21C126181E5E654B404A45B4F0137CE88177435A69978CC6BEC1F4",
|
||||
Data: "8CE03CD60514233B86789729102EA09E867FC6D964DEA8C2018EF7D0A2E0E24BF7E348E917116690B9",
|
||||
Length: 41,
|
||||
Sig: "D9CEC0CC0E3465FAB229F8E1D6DB68AB9CC99A18CB0435F70DEB6100948576CD5C0AA1FEB550BDD8693EF81EB10A556A622DB1F9301986827B96716A7134230C",
|
||||
}
|
||||
|
||||
t2 := TestSig{
|
||||
Privkey: "6AA6DAD25D3ACB3385D5643293133936CDDDD7F7E11818771DB1FF2F9D3F9215",
|
||||
Pubkey: "BBC8CBB43DDA3ECF70A555981A351A064493F09658FFFE884C6FAB2A69C845C6",
|
||||
Data: "E4A92208A6FC52282B620699191EE6FB9CF04DAF48B48FD542C5E43DAA9897763A199AAA4B6F10546109F47AC3564FADE0",
|
||||
Length: 49,
|
||||
Sig: "98BCA58B075D1748F1C3A7AE18F9341BC18E90D1BEB8499E8A654C65D8A0B4FBD2E084661088D1E5069187A2811996AE31F59463668EF0F8CB0AC46A726E7902",
|
||||
}
|
||||
|
||||
t3 := TestSig{
|
||||
Privkey: "8E32BC030A4C53DE782EC75BA7D5E25E64A2A072A56E5170B77A4924EF3C32A9",
|
||||
Pubkey: "72D0E65F1EDE79C4AF0BA7EC14204E10F0F7EA09F2BC43259CD60EA8C3A087E2",
|
||||
Data: "13ED795344C4448A3B256F23665336645A853C5C44DBFF6DB1B9224B5303B6447FBF8240A2249C55",
|
||||
Length: 40,
|
||||
Sig: "EF257D6E73706BB04878875C58AA385385BF439F7040EA8297F7798A0EA30C1C5EFF5DDC05443F801849C68E98111AE65D088E726D1D9B7EECA2EB93B677860C",
|
||||
}
|
||||
|
||||
t4 := TestSig{
|
||||
Privkey: "C83CE30FCB5B81A51BA58FF827CCBC0142D61C13E2ED39E78E876605DA16D8D7",
|
||||
Pubkey: "3EC8923F9EA5EA14F8AAA7E7C2784653ED8C7DE44E352EF9FC1DEE81FC3FA1A3",
|
||||
Data: "A2704638434E9F7340F22D08019C4C8E3DBEE0DF8DD4454A1D70844DE11694F4C8CA67FDCB08FED0CEC9ABB2112B5E5F89",
|
||||
Length: 49,
|
||||
Sig: "0C684E71B35FED4D92B222FC60561DB34E0D8AFE44BDD958AAF4EE965911BEF5991236F3E1BCED59FC44030693BCAC37F34D29E5AE946669DC326E706E81B804",
|
||||
}
|
||||
|
||||
t5 := TestSig{
|
||||
Privkey: "2DA2A0AAE0F37235957B51D15843EDDE348A559692D8FA87B94848459899FC27",
|
||||
Pubkey: "D73D0B14A9754EEC825FCB25EF1CFA9AE3B1370074EDA53FC64C22334A26C254",
|
||||
Data: "D2488E854DBCDFDB2C9D16C8C0B2FDBC0ABB6BAC991BFE2B14D359A6BC99D66C00FD60D731AE06D0",
|
||||
Length: 40,
|
||||
Sig: "6F17F7B21EF9D6907A7AB104559F77D5A2532B557D95EDFFD6D88C073D87AC00FC838FC0D05282A0280368092A4BD67E95C20F3E14580BE28D8B351968C65E03",
|
||||
}
|
||||
|
||||
toReturn = append(toReturn, t1, t2, t3, t4, t5)
|
||||
|
||||
return toReturn
|
||||
}
|
||||
Reference in New Issue
Block a user