Skip to content

Commit

Permalink
Merge pull request #436 from LF-Decentralized-Trust-labs/seed-keyhandle
Browse files Browse the repository at this point in the history
Allow pre-resolved seeds
  • Loading branch information
dwertent authored Nov 19, 2024
2 parents e2347b0 + d5a2142 commit dc3f1b8
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 17 deletions.
15 changes: 8 additions & 7 deletions config/pkg/pldconf/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,26 @@ type ConfigKeyPathEntry struct {
Index uint64 `json:"index"`
}

type SigningKeyConfigEntry struct {
type StaticKeyReference struct {
KeyHandle string `json:"keyHandle,omitempty"` // causes resolution to be bypassed, similarly to if a key-mapping already exists in the DB for runtime resolution
Name string `json:"name"`
Index uint64 `json:"index"`
Attributes map[string]string `json:"attributes"`
Path []ConfigKeyPathEntry `json:"path"`
}

type KeyDerivationConfig struct {
Type KeyDerivationType `json:"type"`
SeedKeyPath SigningKeyConfigEntry `json:"seedKey"`
BIP44DirectResolution bool `json:"bip44DirectResolution"`
BIP44Prefix *string `json:"bip44Prefix"`
BIP44HardenedSegments *int `json:"bip44HardenedSegments"`
Type KeyDerivationType `json:"type"`
SeedKeyPath StaticKeyReference `json:"seedKey"`
BIP44DirectResolution bool `json:"bip44DirectResolution"`
BIP44Prefix *string `json:"bip44Prefix"`
BIP44HardenedSegments *int `json:"bip44HardenedSegments"`
}

var KeyDerivationDefaults = &KeyDerivationConfig{
BIP44Prefix: confutil.P("m/44'/60'"),
BIP44HardenedSegments: confutil.P(1), // in addition to the prefix, so `m/44'/60'/0'/0/0` for example with 3 segments, on top of the prefix
SeedKeyPath: SigningKeyConfigEntry{Name: "seed", Index: 0},
SeedKeyPath: StaticKeyReference{Name: "seed", Index: 0},
}

type StaticKeyEntryEncoding string
Expand Down
2 changes: 1 addition & 1 deletion operator/internal/controller/paladin_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ func (r *PaladinReconciler) generatePaladinSigners(ctx context.Context, node *co
// Upsert a secret if we've been asked to. We use a mnemonic in this case (rather than directly generating a 32byte seed)
if s.Type == corev1alpha1.SignerType_AutoHDWallet {
wallet.Signer.KeyDerivation.Type = pldconf.KeyDerivationTypeBIP32
wallet.Signer.KeyDerivation.SeedKeyPath = pldconf.SigningKeyConfigEntry{Name: "seed"}
wallet.Signer.KeyDerivation.SeedKeyPath = pldconf.StaticKeyReference{Name: "seed"}
if err := r.generateBIP39SeedSecretIfNotExist(ctx, node, s.Secret); err != nil {
return err
}
Expand Down
19 changes: 15 additions & 4 deletions toolkit/go/pkg/signer/hd_key_derivation.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ type hdWalletPathEntry struct {
Index uint64
}

func configToKeyResolutionRequest(k *pldconf.SigningKeyConfigEntry) *signerapi.ResolveKeyRequest {
func configToKeyResolutionRequest(k *pldconf.StaticKeyReference) (string, *signerapi.ResolveKeyRequest) {
if k.KeyHandle != "" {
return k.KeyHandle, nil
}
keyReq := &signerapi.ResolveKeyRequest{
Name: k.Name,
Index: k.Index,
Expand All @@ -54,7 +57,7 @@ func configToKeyResolutionRequest(k *pldconf.SigningKeyConfigEntry) *signerapi.R
Index: p.Index,
})
}
return keyReq
return "", keyReq
}

func (sm *signingModule[C]) initHDWallet(ctx context.Context, conf *pldconf.KeyDerivationConfig) (err error) {
Expand All @@ -67,11 +70,19 @@ func (sm *signingModule[C]) initHDWallet(ctx context.Context, conf *pldconf.KeyD
bip44HardenedSegments: confutil.IntMin(conf.BIP44HardenedSegments, 0, *pldconf.KeyDerivationDefaults.BIP44HardenedSegments),
}
seedKeyPath := pldconf.KeyDerivationDefaults.SeedKeyPath
if conf.SeedKeyPath.Name != "" {
if conf.SeedKeyPath.Name != "" || conf.SeedKeyPath.KeyHandle != "" || len(conf.SeedKeyPath.Attributes) > 0 {
seedKeyPath = conf.SeedKeyPath
}
// Note we don't have any way to store the resolved keyHandle, so we resolve it every time we start
seed, _, err := sm.keyStore.FindOrCreateLoadableKey(ctx, configToKeyResolutionRequest(&seedKeyPath), sm.new32ByteRandomSeed)
var seed []byte
keyHandle, seedResolve := configToKeyResolutionRequest(&seedKeyPath)
if keyHandle != "" {
// We have been provided a pre-resolved key handle
seed, err = sm.keyStore.LoadKeyMaterial(ctx, keyHandle)
} else {
// We need to call resolve to resolve the key material
seed, _, err = sm.keyStore.FindOrCreateLoadableKey(ctx, seedResolve, sm.new32ByteRandomSeed)
}
if err != nil {
return err
}
Expand Down
49 changes: 46 additions & 3 deletions toolkit/go/pkg/signer/hd_key_derivation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,49 @@ func TestHDSigningStaticExample(t *testing.T) {

}

func TestHDSigningStaticExamplePreResolved(t *testing.T) {

ctx := context.Background()
sm, err := NewSigningModule(ctx, &signerapi.ConfigNoExt{
KeyDerivation: pldconf.KeyDerivationConfig{
Type: pldconf.KeyDerivationTypeBIP32,
SeedKeyPath: pldconf.StaticKeyReference{
KeyHandle: "directly.resolved",
},
},
KeyStore: pldconf.KeyStoreConfig{
Type: pldconf.KeyStoreTypeStatic,
Static: pldconf.StaticKeyStoreConfig{
Keys: map[string]pldconf.StaticKeyEntryConfig{
"directly.resolved": {
Encoding: "hex",
Inline: tktypes.RandHex(32),
},
},
},
},
})
require.NoError(t, err)

res, err := sm.Resolve(ctx, &signerapi.ResolveKeyRequest{
RequiredIdentifiers: []*signerapi.PublicKeyIdentifierType{{Algorithm: algorithms.ECDSA_SECP256K1, VerifierType: verifiers.ETH_ADDRESS}},
Name: "key1",
Index: 0,
})
require.NoError(t, err)
assert.Equal(t, "m/44'/60'/0'", res.KeyHandle)

resSign, err := sm.Sign(ctx, &signerapi.SignRequest{
KeyHandle: res.KeyHandle,
Algorithm: algorithms.ECDSA_SECP256K1,
PayloadType: signpayloads.OPAQUE_TO_RSV,
Payload: ([]byte)("some data"),
})
require.NoError(t, err)
assert.NotEmpty(t, resSign.Payload)

}

func TestHDSigningDirectResNoPrefix(t *testing.T) {

ctx := context.Background()
Expand Down Expand Up @@ -157,7 +200,7 @@ func TestHDSigningDefaultBehaviorOK(t *testing.T) {
sm, err := NewSigningModule(ctx, &signerapi.ConfigNoExt{
KeyDerivation: pldconf.KeyDerivationConfig{
Type: pldconf.KeyDerivationTypeBIP32,
SeedKeyPath: pldconf.SigningKeyConfigEntry{
SeedKeyPath: pldconf.StaticKeyReference{
Name: "seed",
Index: 0,
Path: []pldconf.ConfigKeyPathEntry{
Expand Down Expand Up @@ -292,7 +335,7 @@ func TestHDInitBadSeed(t *testing.T) {
_, err = NewSigningModule(ctx, &signerapi.ConfigNoExt{
KeyDerivation: pldconf.KeyDerivationConfig{
Type: pldconf.KeyDerivationTypeBIP32,
SeedKeyPath: pldconf.SigningKeyConfigEntry{
SeedKeyPath: pldconf.StaticKeyReference{
Name: "missing",
},
},
Expand All @@ -319,7 +362,7 @@ func TestHDInitGenSeed(t *testing.T) {
sm, err := NewSigningModule(ctx, &signerapi.ConfigNoExt{
KeyDerivation: pldconf.KeyDerivationConfig{
Type: pldconf.KeyDerivationTypeBIP32,
SeedKeyPath: pldconf.SigningKeyConfigEntry{
SeedKeyPath: pldconf.StaticKeyReference{
Name: "seed",
Path: []pldconf.ConfigKeyPathEntry{{Name: "generate"}},
},
Expand Down
4 changes: 2 additions & 2 deletions toolkit/go/pkg/signerapi/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ type KeyStoreFactory[C ExtensibleConfig] interface {
}

// All cryptographic storage needs to support master key encryption, by which the bytes
// can be decrypted an loaded into volatile memory for use, and then discarded.
// can be decrypted and loaded into volatile memory for use, and then discarded.
//
// The implementation is not required to know how to generate or validate such data, just now
// The implementation is not required to know how to generate or validate such data, just how
// to securely store and retrieve it using only the information contained in the returned
// keyHandle. If the implementation finds it does not exist, it can invoke the callback function to generate
// a new suitable random string to encrypt and store.
Expand Down

0 comments on commit dc3f1b8

Please sign in to comment.