Skip to content

Commit

Permalink
feat(SPV-1316) type42 derivation (#821)
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-4chain authored Dec 19, 2024
1 parent 9e4f534 commit e28768f
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 4 deletions.
21 changes: 21 additions & 0 deletions engine/keys/type42/derivation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package type42

import (
primitives "github.com/bitcoin-sv/go-sdk/primitives/ec"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
)

var (
anyonePriv, _ = primitives.PrivateKeyFromBytes([]byte{1})
)

func derive(pubKey *primitives.PublicKey, derivationKey string) (*primitives.PublicKey, error) {
if pubKey == nil {
return nil, ErrDeriveKey.Wrap(spverrors.Newf("public key is nil"))
}
derivedPubByRef, err := pubKey.DeriveChild(anyonePriv, derivationKey)
if err != nil {
return nil, ErrDeriveKey.Wrap(err)
}
return derivedPubByRef, nil
}
21 changes: 21 additions & 0 deletions engine/keys/type42/destination.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package type42

import (
"fmt"

primitives "github.com/bitcoin-sv/go-sdk/primitives/ec"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
)

// Destination derives a public key using a reference ID.
// It is intended to be used to derive a public key for paymail destinations.
func Destination(pubKey *primitives.PublicKey, referenceID string) (*primitives.PublicKey, error) {
if referenceID == "" {
return nil, ErrDeriveKey.Wrap(spverrors.Newf("reference ID is empty"))
}
derivedPubByRef, err := derive(pubKey, fmt.Sprintf("1-destination-%s", referenceID))
if err != nil {
return nil, err
}
return derivedPubByRef, nil
}
46 changes: 46 additions & 0 deletions engine/keys/type42/destination_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package type42

import (
"testing"

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

func TestDestination(t *testing.T) {
t.Run("generate destination", func(t *testing.T) {
// given:
pubKey := makePubKey(t, "033014c226b8fe8260e21e75479a47a654e7b631b3bd13484d85c484f7791aa75b")
referenceID := "4c7bc22854691fda2d643f9c5cf6d218"

// when:
destination, err := Destination(pubKey, referenceID)

// then:
assert.NoError(t, err)
assert.Equal(t, "03d34d33cb9cf83ad5bea49c7ebb1adafe0c85ceda0e256a0c5db3b6cb28e3ec99", destination.ToDERHex())
})

t.Run("try to generate destination on nil", func(t *testing.T) {
// given:
referenceID := "4c7bc22854691fda2d643f9c5cf6d218"

// when:
destination, err := Destination(nil, referenceID)

// then:
assert.ErrorIs(t, err, ErrDeriveKey)
assert.Nil(t, destination)
})

t.Run("try to generate destination on empty referenceID", func(t *testing.T) {
// given:
pubKey := makePubKey(t, "033014c226b8fe8260e21e75479a47a654e7b631b3bd13484d85c484f7791aa75b")

// when:
destination, err := Destination(pubKey, "")

// then:
assert.ErrorIs(t, err, ErrDeriveKey)
assert.Nil(t, destination)
})
}
6 changes: 6 additions & 0 deletions engine/keys/type42/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package type42

import "github.com/bitcoin-sv/spv-wallet/models"

// ErrDeriveKey is an error that occurs when a child key cannot be derived from a public key.
var ErrDeriveKey = models.SPVError{Message: "Failed to derive a child key for provided public key", StatusCode: 500, Code: "error-derive-key"}
14 changes: 14 additions & 0 deletions engine/keys/type42/pki.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package type42

import primitives "github.com/bitcoin-sv/go-sdk/primitives/ec"

const pkiDerivationKey = "1-pki-0"

// PKI (Public Key Infrastructure) derives a public key using a constant derivation key.
func PKI(pubKey *primitives.PublicKey) (*primitives.PublicKey, error) {
derivedPubByRef, err := derive(pubKey, pkiDerivationKey)
if err != nil {
return nil, ErrDeriveKey.Wrap(err)
}
return derivedPubByRef, nil
}
40 changes: 40 additions & 0 deletions engine/keys/type42/pki_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package type42

import (
"testing"

primitives "github.com/bitcoin-sv/go-sdk/primitives/ec"
"github.com/stretchr/testify/assert"
)

func TestPKI(t *testing.T) {
t.Run("generate PKI", func(t *testing.T) {
// given:
pubKey := makePubKey(t, "033014c226b8fe8260e21e75479a47a654e7b631b3bd13484d85c484f7791aa75b")

// when:
pki, err := PKI(pubKey)

// then:
assert.NoError(t, err)
assert.Equal(t, "02b9a822f2db22649e14eedf75ba140cf5dacc6b2690cfae9da55b551069461705", pki.ToDERHex())
})

t.Run("try to generate PKI on nil", func(t *testing.T) {
// when:
pki, err := PKI(nil)

// then:
assert.ErrorIs(t, err, ErrDeriveKey)
assert.Nil(t, pki)
})
}

func makePubKey(t *testing.T, pubDERHex string) *primitives.PublicKey {
t.Helper()
pk, err := primitives.PublicKeyFromString(pubDERHex)
if err != nil {
t.Fatalf("failed to create public key: %s", err)
}
return pk
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package type42
package type84

import (
"crypto/hmac"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package type42
package type84

import (
"encoding/hex"
Expand Down
4 changes: 2 additions & 2 deletions engine/pike/pike.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import (

ec "github.com/bitcoin-sv/go-sdk/primitives/ec"
"github.com/bitcoin-sv/go-sdk/script"
"github.com/bitcoin-sv/spv-wallet/engine/keys/type84"
"github.com/bitcoin-sv/spv-wallet/engine/script/template"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
"github.com/bitcoin-sv/spv-wallet/engine/types/type42"
)

// GenerateOutputsTemplate creates a Pike output template
Expand All @@ -43,7 +43,7 @@ func GenerateLockingScriptsFromTemplates(outputsTemplate []*template.OutputTempl
return nil, spverrors.Wrapf(err, "error creating script from hex string")
}

dPK, err := type42.DeriveLinkedKey(senderPubKey, receiverPubKey, fmt.Sprintf("%s-%d", reference, idx))
dPK, err := type84.DeriveLinkedKey(senderPubKey, receiverPubKey, fmt.Sprintf("%s-%d", reference, idx))
if err != nil {
return nil, spverrors.Wrapf(err, "error deriving linked key")
}
Expand Down

0 comments on commit e28768f

Please sign in to comment.