Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 13 additions & 20 deletions internal/contracts/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,7 @@ type JSONContract struct {
StateNonce uint64
}

/*
version field comes from version parameter
sender public key comes from sender private key
signature comes from calling sign contract
signature length comes from signature
recipient pk hash comes from sha-256 hash of rpk
value is value parameter
returns contract struct
*/
//Creates an unsigned contract
func New(version uint16, sender *ecdsa.PrivateKey, recipient []byte, value uint64, nextStateNonce uint64) (*Contract, error) {

if version == 0 {
Expand All @@ -75,18 +67,19 @@ func New(version uint16, sender *ecdsa.PrivateKey, recipient []byte, value uint6
return &c, nil
}

// // Serialize all fields of the contract
func (c *Contract) Serialize() ([]byte, error) {
/*
0-2 version
2-180 spubkey
180-181 siglen
181 - 181+c.siglen signature
181+c.siglen - (181+c.siglen + 32) rpkh
(181+c.siglen + 32) - (181+c.siglen + 32+ 8) value

*/
/*
Serialize will serialize the contract into an slice of bytes

Byte breakdown
0-2 version
3: The first byte of the encoded public key will indicate the length of the encoded public key.
If this byte is 0, then there is no sender public key (usefully for mining contracts)
After the encoded public key, the next byte will be the signature length
The next bytes are the signatures
The next 32 bytes are the recipient public key hash
The next 8 bytes indicates the value of the contract
*/
func (c *Contract) Serialize() ([]byte, error) {
// if contract's sender pubkey is nil, make 178 zeros in its place instead
var spubkey []byte
var err error
Expand Down
183 changes: 120 additions & 63 deletions internal/contracts/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"fmt"
"math/big"
Expand Down Expand Up @@ -100,73 +101,129 @@ func TestNew(t *testing.T) {
}
}

func TestContract_Serialize(t *testing.T) {
func TestMiningContractSerialize(t *testing.T) {
recipientPrivateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
encodedPublicKey, _ := publickey.Encode(&recipientPrivateKey.PublicKey)
recipientPubKeyHash := hashing.New(encodedPublicKey)

expectedVersion := uint16(1)
expectedValue := uint64(10004)
expectedStateNonce := uint64(43)

nullSenderContract, _ := New(expectedVersion, nil, recipientPubKeyHash, expectedValue, expectedStateNonce)
serializedContract, _ := nullSenderContract.Serialize()

actualVersion := binary.LittleEndian.Uint16(serializedContract[0:2])
sigLen := uint8(serializedContract[3])
actualRecipPubKeyHash := serializedContract[4:36]

if actualVersion != expectedVersion {
t.Errorf("Versions do not match. Expected: %d, actual: %d\n", expectedVersion, actualVersion)
}
if serializedContract[2] != byte(0x0) {
t.Errorf("Encoded sender public key is not 0")
}
if sigLen != 0 {
t.Errorf("Mining contract should be unsigned. Got signature length of %d, when it should be 0", sigLen)
}
if !bytes.Equal(recipientPubKeyHash, actualRecipPubKeyHash) {
t.Errorf("Recipient Public Key hashes do not match.\nExpected: %v\nActual:%v\n", recipientPubKeyHash, actualRecipPubKeyHash)
}
}

func TestUnsignedContractSerialize(t *testing.T) {
recipientPrivateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
senderPrivateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
encodedRecipientPublicKey, _ := publickey.Encode(&recipientPrivateKey.PublicKey)
encodedSenderPublicKey, _ := publickey.Encode(&senderPrivateKey.PublicKey)
recipientPubKeyHash := hashing.New(encodedRecipientPublicKey)

expectedVersion := uint16(3)
expectedValue := uint64(12004)
expectedStateNonce := uint64(873)
contract, _ := New(expectedVersion, senderPrivateKey, recipientPubKeyHash, expectedValue, expectedStateNonce)
serializedContract, _ := contract.Serialize()

actualVersion := binary.LittleEndian.Uint16(serializedContract[0:2])

firstByte := serializedContract[2]
pubKeyLength := publickey.Info[firstByte].Length
actualPubKey := serializedContract[2 : 2+pubKeyLength]
actualSigLen := serializedContract[2+pubKeyLength]
actualRecipientHash := serializedContract[3+pubKeyLength : 35+pubKeyLength]
actualValue := binary.LittleEndian.Uint64(serializedContract[35+pubKeyLength : 43+pubKeyLength])
actualNonce := binary.LittleEndian.Uint64(serializedContract[43+pubKeyLength : 51+pubKeyLength])

if actualVersion != expectedVersion {
t.Errorf("Versions do not match. Expected: %d, actual: %d\n", expectedVersion, actualVersion)
}
if serializedContract[2] == byte(0x0) {
t.Errorf("Encoded sender public key is 0\n")
}
if !bytes.Equal(actualPubKey, encodedSenderPublicKey) {
t.Errorf("Sender public key not properly encoded\nexpected: %v\nactual %v\n", encodedSenderPublicKey, actualPubKey)
}
if actualSigLen != 0 {
t.Errorf("Signature length is not 0. Expected: 0, actual: %d", actualSigLen)
}
if !bytes.Equal(actualRecipientHash, recipientPubKeyHash) {
t.Errorf("Recipeint public key hash not matching.\nExpected: %v\nActual: %v\n", recipientPubKeyHash, actualRecipientHash)
}
if actualValue != expectedValue {
t.Errorf("Values do not match\nExpected: %v\nActual: %v\n", expectedValue, actualValue)
}
if actualNonce != expectedStateNonce {
t.Errorf("State nonces do not match\nExpected: %v\nActual: %v\n", expectedStateNonce, actualNonce)
}

}

func TestSignedContractSerialize(t *testing.T) {
recipientPrivateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
encodedPublicKey, _ := publickey.Encode(&senderPrivateKey.PublicKey)
nullSenderContract, _ := New(1, nil, hashing.New(encodedPublicKey), 1000, 0)
encodedRecipientKey, _ := publickey.Encode(&recipientPrivateKey.PublicKey)
unsignedContract, _ := New(1, senderPrivateKey, hashing.New(encodedRecipientKey), 1000, 0)
signedContract, _ := New(1, senderPrivateKey, hashing.New(encodedRecipientKey), 1000, 0)
signedContract.Sign(senderPrivateKey)
tests := []struct {
name string
c *Contract
}{
{
name: "Minting contract",
c: nullSenderContract,
},
{
name: "Unsigned contract",
c: unsignedContract,
},
{
name: "Signed contract",
c: signedContract,
},
senderPrivateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
encodedRecipientPublicKey, _ := publickey.Encode(&recipientPrivateKey.PublicKey)
encodedSenderPublicKey, _ := publickey.Encode(&senderPrivateKey.PublicKey)
recipientPubKeyHash := hashing.New(encodedRecipientPublicKey)

expectedVersion := uint16(3)
expectedValue := uint64(12004)
expectedStateNonce := uint64(873)
contract, _ := New(expectedVersion, senderPrivateKey, recipientPubKeyHash, expectedValue, expectedStateNonce)
contract.Sign(senderPrivateKey)
serializedContract, _ := contract.Serialize()

actualVersion := binary.LittleEndian.Uint16(serializedContract[0:2])

firstByte := serializedContract[2]
pubKeyLength := publickey.Info[firstByte].Length
actualPubKey := serializedContract[2 : 2+pubKeyLength]
actualSigLen := uint(serializedContract[2+pubKeyLength])
actualRecipientHash := serializedContract[3+pubKeyLength+actualSigLen : 35+pubKeyLength+actualSigLen]
actualValue := binary.LittleEndian.Uint64(serializedContract[35+pubKeyLength+actualSigLen : 43+pubKeyLength+actualSigLen])
actualNonce := binary.LittleEndian.Uint64(serializedContract[43+pubKeyLength+actualSigLen : 51+pubKeyLength+actualSigLen])

if actualVersion != expectedVersion {
t.Errorf("Versions do not match. Expected: %d, actual: %d\n", expectedVersion, actualVersion)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, _ := tt.c.Serialize()
sigLen := got[180]
testSendPublicKey := tt.c.SenderPubKey
testEncodeSenderPubKey, _ := publickey.Encode(testSendPublicKey)
switch tt.name {
case "Minting contract":
if !bytes.Equal(got[2:180], make([]byte, 178)) {
t.Errorf("Non null sender public key for minting contract")
}
if sigLen != 0 {
t.Errorf("Non-zero signature length in minting contract: %v", sigLen)
}
if !bytes.Equal(got[181:213], tt.c.RecipPubKeyHash) {
t.Errorf("Invalid recipient public key hash in minting contract")
}
break
case "Unsigned contract":
if sigLen != 0 {
t.Errorf("Non-zero signature length in unsigned contract: %v", sigLen)
}
if !bytes.Equal(got[2:180], testEncodeSenderPubKey) {
t.Errorf("Invalid encoded public key for unsigned contract")
}
if !bytes.Equal(got[181:213], tt.c.RecipPubKeyHash) {
t.Errorf("Invalid recipient public key hash in unsigned contract")
}
case "Signed Contract":
if sigLen == 0 {
t.Errorf("Zero length signature in signed contract: %v", sigLen)
}
if !bytes.Equal(got[2:180], testEncodeSenderPubKey) {
t.Errorf("Invalid encoded public key for signed contract")
}
if !bytes.Equal(got[(181+int(sigLen)):(181+int(sigLen)+32)], tt.c.RecipPubKeyHash) {
t.Errorf("Invalid recipient public key hash in signed contract")
}
}
})
if serializedContract[2] == byte(0x0) {
t.Errorf("Encoded sender public key is 0\n")
}
if !bytes.Equal(actualPubKey, encodedSenderPublicKey) {
t.Errorf("Sender public key not properly encoded\nexpected: %v\nactual %v\n", encodedSenderPublicKey, actualPubKey)
}
if actualSigLen == 0 {
t.Errorf("Signature length is 0")
}
if !bytes.Equal(actualRecipientHash, recipientPubKeyHash) {
t.Errorf("Recipeint public key hash not matching.\nExpected: %v\nActual: %v\n", recipientPubKeyHash, actualRecipientHash)
}
if actualValue != expectedValue {
t.Errorf("Values do not match\nExpected: %v\nActual: %v\n", expectedValue, actualValue)
}
if actualNonce != expectedStateNonce {
t.Errorf("State nonces do not match\nExpected: %v\nActual: %v\n", expectedStateNonce, actualNonce)
}

}

func TestContract_Deserialize(t *testing.T) {
Expand Down
9 changes: 9 additions & 0 deletions internal/publickey/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package publickey

var (
Info = map[byte]publicKeyInfo{0x01: {Length: 65}}
)

type publicKeyInfo struct {
Length uint
}
43 changes: 27 additions & 16 deletions internal/publickey/publickey.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package publickey

import (
"crypto/ecdsa"
"crypto/x509"
"crypto/elliptic"
"encoding/hex"
"encoding/pem"
"errors"
"math/big"


"github.com/SIGBlockchain/project_aurum/internal/hashing"
)
Expand Down Expand Up @@ -42,30 +43,40 @@ func Encode(key *ecdsa.PublicKey) ([]byte, error) {
if key == nil {
return nil, errors.New("Could not return the encoded public key - the key value is nil")
}
x509EncodedPub, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: x509EncodedPub}), nil
b := make([]byte, 1+len(key.X.Bytes())+len(key.Y.Bytes()))
b[0] = byte(1)
copy(b[1:33], key.X.Bytes())
copy(b[33:65], key.Y.Bytes())

return b, nil
}

// Decode returns the public key from a given PEM-Encoded byte slice representation of the public key or a non-nil error if fail
func Decode(key []byte) (*ecdsa.PublicKey, error) {
if key == nil {
return nil, errors.New("Could not return the decoded public key - the key value is nil")
}
blockPub, _ := pem.Decode(key)
// pem.Decode will return nil for the first value if no PEM data is found. This would be bad
if blockPub == nil {
return nil, errors.New("Could not return the public key - failed to PEM decode in preparation x509 encode")
if(key[0]!=1){
return nil, errors.New("Could not return the decoded public key - the key is not uncompressed public key")
}

x509EncodedPub := blockPub.Bytes
genericPublicKey, err := x509.ParsePKIXPublicKey(x509EncodedPub)
if err != nil {
return nil, err
n := new(big.Int)
m := new(big.Int)


xByte, err := n.SetString(hex.EncodeToString(key[1:33]), 16)

if !err {
return nil, errors.New("Could not convert string to int")
}

yByte, ok := m.SetString(hex.EncodeToString(key[33:65]), 16)
if !ok {
return nil, errors.New("Could not convert string to int")
}
return genericPublicKey.(*ecdsa.PublicKey), nil

priv := ecdsa.PublicKey{Curve: elliptic.P256(), X: xByte, Y: yByte}
return &priv, nil
}

// Equals returns true if the given two *ecdsa.PublicKey are equal
Expand Down
38 changes: 18 additions & 20 deletions internal/publickey/publickey_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package publickey

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"reflect"
"testing"

Expand Down Expand Up @@ -54,21 +53,22 @@ func TestNew(t *testing.T) {
func TestEncoding(t *testing.T) {
private, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
public := private.PublicKey
x509EncodedPub, err := x509.MarshalPKIXPublicKey(nil)
// test that Encoding returns an error for bad input
if err == nil {
t.Errorf("Expected err to not be nil, but it was...")
}
encoded, err := Encode(&public)
// test that Encoding does not receive an error for valid input
encodedPublic, err := Encode(&public)

if err != nil {
t.Errorf("Received an error for valid input")
t.Errorf("Got error: %s\n", err.Error())
}
x509EncodedPub, _ = x509.MarshalPKIXPublicKey(&public)
x509EncodedPub = pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: x509EncodedPub})
// test that Encoding results match
if !reflect.DeepEqual(x509EncodedPub, encoded) {
t.Errorf("Encoding does not match")
if encodedPublic[0] != byte(1) {
t.Errorf("Encoding is missing a byte prefix of 1, instead got: %v\n", encodedPublic[0])
}
if bytes.Compare(encodedPublic[1:33], public.X.Bytes()) != 0 {
t.Errorf("Encoding of X from public key is mssing")
}
if bytes.Compare(encodedPublic[33:65], public.Y.Bytes()) != 0 {
t.Errorf("Encoding of Y from public key is mssing")
}
if len(encodedPublic) != 65 {
t.Errorf("Expected legnth of 65, instead got: %d\n", len(encodedPublic))
}
}

Expand All @@ -87,12 +87,10 @@ func TestDecoding(t *testing.T) {
if err != nil {
t.Errorf("Expected err to not be nil, but it was...")
}
localDecoded, _ := pem.Decode(encoded)
x509EncodedPub := localDecoded.Bytes
genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub)

// test that decodings match
if !reflect.DeepEqual(genericPublicKey, decoded) {
t.Errorf("Keys do not match after decode")
if !reflect.DeepEqual(public, *decoded) {
t.Errorf("Keys do not match after decode\n. %v != %v\n", public, decoded)
}
}

Expand Down