Skip to content

Commit

Permalink
[crypto] Refactor crypto to be generic / pull ed25519 out of transact…
Browse files Browse the repository at this point in the history
…ion logic (#9)

* [crypto] Refactor crypto to be generic / pull ed25519 out of transaction logic

* [crypto] Fix go client with bytes function
  • Loading branch information
gregnazario authored May 2, 2024
1 parent f5af0f8 commit 42dee9c
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 32 deletions.
54 changes: 44 additions & 10 deletions account.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package aptos

import (
"crypto"
"crypto/ed25519"
"crypto/rand"
"encoding/hex"
"errors"
Expand All @@ -12,6 +10,17 @@ import (
"golang.org/x/crypto/sha3"
)

// Seeds for deriving addresses from addresses
const (
Ed25519Scheme = uint8(0)
MultiEd25519Scheme = uint8(1)
SingleKeyScheme = uint8(2)
MultiKeyScheme = uint8(3)
deriveObjectScheme = uint8(252)
namedObjectScheme = uint8(254)
resourceAccountScheme = uint8(255)
)

type AccountAddress [32]byte

// TODO: find nicer naming for this? Move account to a package so this can be account.ONE ? Wrap in a singleton struct for Account.One ?
Expand Down Expand Up @@ -46,33 +55,58 @@ func (aa AccountAddress) String() string {
func (aa AccountAddress) MarshalBCS(bcs *Serializer) {
bcs.FixedBytes(aa[:])
}

func (aa *AccountAddress) UnmarshalBCS(bcs *Deserializer) {
bcs.ReadFixedBytesInto((*aa)[:])
}

func (aa *AccountAddress) Random() {
rand.Read((*aa)[:])
}
func (aa *AccountAddress) FromEd25519PubKey(pubkey ed25519.PublicKey) {
// TODO: Other SDK implementations have an internal AuthenticationKey type to wrap this. Maybe follow that pattern later?

func (aa *AccountAddress) FromPublicKey(pubkey PublicKey) {
hasher := sha3.New256()
hasher.Write(pubkey[:])
hasher.Write([]byte{0})
hasher.Write(pubkey.Bytes())
hasher.Write([]byte{pubkey.Scheme()})
hasher.Sum((*aa)[:0])
}

func (aa *AccountAddress) NamedObjectAddress(seed []byte) (accountAddress AccountAddress) {
return aa.DerivedAddress(seed, namedObjectScheme)
}

func (aa *AccountAddress) ObjectAddressFromObject(objectAddress *AccountAddress) (accountAddress AccountAddress) {
return aa.DerivedAddress(objectAddress[:], deriveObjectScheme)
}

func (aa *AccountAddress) ResourceAccount(seed []byte) (accountAddress AccountAddress) {
return aa.DerivedAddress(seed, resourceAccountScheme)
}

// DerivedAddress addresses are derived by the address, the seed, then the type byte
func (aa *AccountAddress) DerivedAddress(seed []byte, type_byte uint8) (accountAddress AccountAddress) {
accountAddress = AccountAddress{}
hasher := sha3.New256()
hasher.Write(aa[:])
hasher.Write(seed[:])
hasher.Write([]byte{type_byte})
hasher.Sum(accountAddress[:])
return
}

type Account struct {
Address AccountAddress
PrivateKey crypto.PrivateKey
PrivateKey PrivateKey
}

func NewAccount() (*Account, error) {
pubkey, privkey, err := ed25519.GenerateKey(nil)
privkey, pubkey, err := GenerateEd5519Keys()
if err != nil {
return nil, err
}
out := &Account{}
out.Address.FromEd25519PubKey(pubkey)
out.PrivateKey = privkey
out.Address.FromPublicKey(pubkey)
out.PrivateKey = &privkey
return out, nil
}

Expand Down
5 changes: 2 additions & 3 deletions cmd/goclient/goclient.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"crypto/ed25519"
"encoding/binary"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -213,14 +212,14 @@ func main() {
amount := uint64(200_000_000)
err = client.Fund(alice.Address, amount)
maybefail(err, "faucet err: %s", err)
fmt.Fprintf(os.Stdout, "new account %s funded for %d, privkey = %s\n", alice.Address.String(), amount, hex.EncodeToString(alice.PrivateKey.(ed25519.PrivateKey)[:]))
fmt.Fprintf(os.Stdout, "new account %s funded for %d, privkey = %s\n", alice.Address.String(), amount, hex.EncodeToString(alice.PrivateKey.Bytes()))

bob, err := aptos.NewAccount()
maybefail(err, "new account: %s", err)
//amount = uint64(10_000_000)
err = client.Fund(bob.Address, amount)
maybefail(err, "faucet err: %s", err)
fmt.Fprintf(os.Stdout, "new account %s funded for %d, privkey = %s\n", bob.Address.String(), amount, hex.EncodeToString(bob.PrivateKey.(ed25519.PrivateKey)[:]))
fmt.Fprintf(os.Stdout, "new account %s funded for %d, privkey = %s\n", bob.Address.String(), amount, hex.EncodeToString(bob.PrivateKey.Bytes()))

time.Sleep(2 * time.Second)
stxn, err := aptos.APTTransferTransaction(client, alice, bob.Address, 42)
Expand Down
29 changes: 29 additions & 0 deletions crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package aptos

// Signer a generic interface for any kind of signing
type Signer interface {
Sign(msg []byte) (authenticator Authenticator, err error)
}

// PrivateKey a generic interface for a signing private key
type PrivateKey interface {
Signer

/// PubKey Retrieve the public key for signature verification
PubKey() PublicKey

Bytes() []byte
}

// PublicKey a generic interface for a public key associated with the private key
type PublicKey interface {
// BCSStruct The public key must be serializable or it will not be used in transactions
BCSStruct

// Bytes the raw bytes for an authenticator
Bytes() []byte
// Scheme The scheme used for address derivation
Scheme() uint8

// TODO: add verify
}
68 changes: 68 additions & 0 deletions ed25519.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package aptos

import (
"crypto/ed25519"
)

type Ed25519PrivateKey struct {
inner ed25519.PrivateKey
}

func GenerateEd5519Keys() (privkey Ed25519PrivateKey, pubkey Ed25519PublicKey, err error) {
pub, priv, err := ed25519.GenerateKey(nil)
if err != nil {
return
}
privkey = Ed25519PrivateKey{priv}
pubkey = Ed25519PublicKey{pub}
return
}

func (key *Ed25519PrivateKey) PubKey() PublicKey {
pubkey := key.inner.Public()
return Ed25519PublicKey{
pubkey.(ed25519.PublicKey),
}
}

func (key *Ed25519PrivateKey) Bytes() []byte {
return key.inner[:]
}

func (key *Ed25519PrivateKey) Sign(msg []byte) (authenticator Authenticator, err error) {
publicKeyBytes := key.PubKey().Bytes()
signature := ed25519.Sign(key.inner, msg)

auth := &Ed25519Authenticator{}
copy(auth.PublicKey[:], publicKeyBytes[:])
copy(auth.Signature[:], signature[:]) // TODO: Signature type?
authenticator = Authenticator{
AuthenticatorEd25519,
auth,
}
return
}

type Ed25519PublicKey struct {
inner ed25519.PublicKey
}

func (key Ed25519PublicKey) Bytes() []byte {
return key.inner[:]
}
func (key Ed25519PublicKey) Scheme() uint8 {
return Ed25519Scheme
}

func (key Ed25519PublicKey) MarshalBCS(bcs *Serializer) {
bcs.FixedBytes(key.inner[:])
}

func (key Ed25519PublicKey) UnmarshalBCS(bcs *Deserializer) {
key = Ed25519PublicKey{}
bytes := bcs.ReadFixedBytes(32)
if bcs.Error() != nil {
return
}
copy(key.inner[:], bytes)
}
24 changes: 9 additions & 15 deletions transactions.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package aptos

import (
"crypto/ed25519"
"errors"
"fmt"
"math/big"
Expand Down Expand Up @@ -31,6 +30,7 @@ func (txn *RawTransaction) MarshalBCS(bcs *Serializer) {
bcs.U64(txn.ExpirationTimetampSeconds)
bcs.U8(txn.ChainId)
}

func (txn *RawTransaction) UnmarshalBCS(bcs *Deserializer) {
txn.Sender.UnmarshalBCS(bcs)
txn.SequenceNumber = bcs.U64()
Expand All @@ -40,6 +40,7 @@ func (txn *RawTransaction) UnmarshalBCS(bcs *Deserializer) {
txn.ExpirationTimetampSeconds = bcs.U64()
txn.ChainId = bcs.U8()
}

func (txn *RawTransaction) SignableBytes() (signableBytes []byte, err error) {
ser := Serializer{}
txn.MarshalBCS(&ser)
Expand All @@ -54,27 +55,20 @@ func (txn *RawTransaction) SignableBytes() (signableBytes []byte, err error) {
copy(signableBytes[len(prehash):], txnbytes)
return signableBytes, nil
}
func (txn *RawTransaction) SignEd25519(privateKey ed25519.PrivateKey) (stxn *SignedTransaction, err error) {

func (txn *RawTransaction) Sign(privateKey PrivateKey) (stxn *SignedTransaction, err error) {
signableBytes, err := txn.SignableBytes()
if err != nil {
return
}
signature := ed25519.Sign(privateKey, signableBytes)
eauth := &Ed25519Authenticator{}
pubkey := privateKey.Public()
if pkb, ok := pubkey.(ed25519.PublicKey); ok {
copy(eauth.PublicKey[:], pkb[:])
} else {
panic(fmt.Sprintf("could not get bytes from pubkey: %T %#v", pubkey, pubkey))
}
copy(eauth.Signature[:], signature)
aa := Authenticator{
Kind: AuthenticatorEd25519,
Auth: eauth,
authenticator, err := privateKey.Sign(signableBytes)
if err != nil {
return
}

stxn = &SignedTransaction{
Transaction: *txn,
Authenticator: aa,
Authenticator: authenticator,
}
return
}
Expand Down
3 changes: 1 addition & 2 deletions transactions_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package aptos

import (
"crypto/ed25519"
"encoding/binary"
"testing"

Expand Down Expand Up @@ -40,7 +39,7 @@ func TestRawTransactionSign(t *testing.T) {
ChainId: 4,
}

stxn, err := txn.SignEd25519(sender.PrivateKey.(ed25519.PrivateKey))
stxn, err := txn.Sign(sender.PrivateKey)
assert.NoError(t, err)

_, ok := stxn.Authenticator.Auth.(*Ed25519Authenticator)
Expand Down
3 changes: 1 addition & 2 deletions util.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package aptos

import (
"crypto/ed25519"
"encoding/binary"
"time"
)
Expand Down Expand Up @@ -51,6 +50,6 @@ func APTTransferTransaction(client *Client, sender *Account, dest AccountAddress
ChainId: chainId,
}

stxn, err = txn.SignEd25519(sender.PrivateKey.(ed25519.PrivateKey))
stxn, err = txn.Sign(sender.PrivateKey)
return stxn, err
}

0 comments on commit 42dee9c

Please sign in to comment.