From faeb4aba91b5fe33cf9a78f88d75a6c23f2a21e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Thu, 18 May 2023 13:24:36 +0200 Subject: [PATCH] feat(wallet): sr25519-raw and sr25519-adr8 for Ledger --- wallet/file/file.go | 36 +++++++++++++++++++++++++++++++----- wallet/ledger/ledger.go | 24 +++++++++++++++++++----- wallet/ledger/signer.go | 2 ++ wallet/wallet.go | 6 +++++- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/wallet/file/file.go b/wallet/file/file.go index 61bc2d5c..4dfd1b2e 100644 --- a/wallet/file/file.go +++ b/wallet/file/file.go @@ -20,10 +20,10 @@ import ( "github.com/oasisprotocol/deoxysii" "github.com/oasisprotocol/oasis-core/go/common/crypto/sakg" coreSignature "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" - "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/ed25519" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/secp256k1" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/sr25519" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" "github.com/oasisprotocol/cli/config" @@ -45,14 +45,14 @@ const ( // SupportedAlgorithmsForImport returns the algorithms supported by the given import kind. func SupportedAlgorithmsForImport(kind *wallet.ImportKind) []string { if kind == nil { - return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw} + return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Raw} } switch *kind { case wallet.ImportKindMnemonic: return []string{wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44} case wallet.ImportKindPrivateKey: - return []string{wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Raw} + return []string{wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Raw} default: return []string{} } @@ -258,6 +258,8 @@ func (af *fileAccountFactory) DataPrompt(kind wallet.ImportKind, rawCfg map[stri return &survey.Multiline{Message: "Private key (base64-encoded):"} case wallet.AlgorithmSecp256k1Raw: return &survey.Multiline{Message: "Private key (hex-encoded):"} + case wallet.AlgorithmSr25519Raw: + return &survey.Multiline{Message: "Private key (base64-encoded):"} default: return nil } @@ -288,6 +290,12 @@ func (af *fileAccountFactory) DataValidator(kind wallet.ImportKind, rawCfg map[s if err != nil { return fmt.Errorf("private key must be hex-encoded (without leading 0x): %w", err) } + case wallet.AlgorithmSr25519Raw: + // Ensure the private key is base64 encoded. + _, err := base64.StdEncoding.DecodeString(ans.(string)) + if err != nil { + return fmt.Errorf("private key must be base64-encoded: %w", err) + } default: return fmt.Errorf("unsupported algorithm for %s: %s", wallet.ImportKindPrivateKey, cfg.Algorithm) } @@ -427,7 +435,7 @@ func (af *fileAccountFactory) Import(name string, passphrase string, rawCfg map[ } case wallet.ImportKindPrivateKey: switch cfg.Algorithm { - case wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Raw: + case wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Raw: default: return nil, fmt.Errorf("algorithm '%s' does not support import from private key", cfg.Algorithm) } @@ -512,6 +520,22 @@ func newAccount(state *secretState, cfg *accountConfig) (wallet.Account, error) return nil, fmt.Errorf("failed to initialize signer: %w", err) } + return &fileAccount{ + cfg: cfg, + state: state, + signer: signer, + }, nil + case wallet.AlgorithmSr25519Raw: + // For Sr25519-Raw use the raw private key. + dataRaw, err := base64.StdEncoding.DecodeString(state.Data) + if err != nil { + return nil, err + } + signer, err := sr25519.NewSigner(dataRaw) + if err != nil { + return nil, err + } + return &fileAccount{ cfg: cfg, state: state, @@ -561,6 +585,8 @@ func (a *fileAccount) SignatureAddressSpec() types.SignatureAddressSpec { return types.NewSignatureAddressSpecEd25519(a.Signer().Public().(ed25519.PublicKey)) case wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw: return types.NewSignatureAddressSpecSecp256k1Eth(a.Signer().Public().(secp256k1.PublicKey)) + case wallet.AlgorithmSr25519Adr8, wallet.AlgorithmSr25519Raw: + return types.NewSignatureAddressSpecSr25519(a.Signer().Public().(sr25519.PublicKey)) default: return types.SignatureAddressSpec{} } @@ -572,7 +598,7 @@ func (a *fileAccount) UnsafeExport() string { func init() { flags := flag.NewFlagSet("", flag.ContinueOnError) - flags.String(cfgAlgorithm, wallet.AlgorithmEd25519Adr8, fmt.Sprintf("Cryptographic algorithm to use for this account [%s, %s]", wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44)) + flags.String(cfgAlgorithm, wallet.AlgorithmEd25519Adr8, fmt.Sprintf("Cryptographic algorithm to use for this account [%s, %s, %s]", wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSr25519Adr8)) flags.Uint32(cfgNumber, 0, "Key number to use in the key derivation scheme") wallet.Register(&fileAccountFactory{ diff --git a/wallet/ledger/ledger.go b/wallet/ledger/ledger.go index 6cadf08a..a5886dcc 100644 --- a/wallet/ledger/ledger.go +++ b/wallet/ledger/ledger.go @@ -13,6 +13,7 @@ import ( "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/ed25519" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/secp256k1" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/sr25519" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" "github.com/oasisprotocol/cli/wallet" @@ -216,6 +217,18 @@ func newAccount(cfg *accountConfig) (wallet.Account, error) { return nil, err } pk = secp256k1pk + case wallet.AlgorithmSr25519Adr8: + path = getAdr0008Path(cfg.Number) + rawPk, err := dev.GetPublicKeySr25519(path, false) + if err != nil { + _ = dev.Close() + return nil, err + } + var sr25519pk sr25519.PublicKey + if err := sr25519pk.UnmarshalBinary(rawPk); err != nil { + return nil, err + } + pk = sr25519pk default: return nil, fmt.Errorf("unsupported algorithm %s", cfg.Algorithm) } @@ -253,8 +266,7 @@ func (a *ledgerAccount) Address() types.Address { } func (a *ledgerAccount) EthAddress() *ethCommon.Address { - switch a.cfg.Algorithm { - case wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw: + if a.cfg.Algorithm == wallet.AlgorithmSecp256k1Bip44 { h := sha3.NewLegacyKeccak256() untaggedPk, _ := a.Signer().Public().(secp256k1.PublicKey).MarshalBinaryUncompressedUntagged() h.Write(untaggedPk) @@ -268,10 +280,12 @@ func (a *ledgerAccount) EthAddress() *ethCommon.Address { func (a *ledgerAccount) SignatureAddressSpec() types.SignatureAddressSpec { switch a.cfg.Algorithm { - case "", wallet.AlgorithmEd25519Legacy, wallet.AlgorithmEd25519Adr8, wallet.AlgorithmEd25519Raw: + case "", wallet.AlgorithmEd25519Legacy, wallet.AlgorithmEd25519Adr8: return types.NewSignatureAddressSpecEd25519(a.Signer().Public().(ed25519.PublicKey)) - case wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSecp256k1Raw: + case wallet.AlgorithmSecp256k1Bip44: return types.NewSignatureAddressSpecSecp256k1Eth(a.Signer().Public().(secp256k1.PublicKey)) + case wallet.AlgorithmSr25519Adr8: + return types.NewSignatureAddressSpecSr25519(a.Signer().Public().(sr25519.PublicKey)) } return types.SignatureAddressSpec{} } @@ -283,7 +297,7 @@ func (a *ledgerAccount) UnsafeExport() string { func init() { flags := flag.NewFlagSet("", flag.ContinueOnError) - flags.String(cfgAlgorithm, wallet.AlgorithmEd25519Legacy, fmt.Sprintf("Cryptographic algorithm to use for this account [%s, %s, %s]", wallet.AlgorithmEd25519Legacy, wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44)) + flags.String(cfgAlgorithm, wallet.AlgorithmEd25519Legacy, fmt.Sprintf("Cryptographic algorithm to use for this account [%s, %s, %s, %s]", wallet.AlgorithmEd25519Legacy, wallet.AlgorithmEd25519Adr8, wallet.AlgorithmSecp256k1Bip44, wallet.AlgorithmSr25519Adr8)) flags.Uint32(cfgNumber, 0, "Key number to use in the derivation scheme") wallet.Register(&ledgerAccountFactory{ diff --git a/wallet/ledger/signer.go b/wallet/ledger/signer.go index 9040d3dc..412b41f4 100644 --- a/wallet/ledger/signer.go +++ b/wallet/ledger/signer.go @@ -58,6 +58,8 @@ func (ls *ledgerSigner) ContextSign(metadata signature.Context, message []byte) return ls.dev.SignRtEd25519(ls.path, metadata, message) case wallet.AlgorithmSecp256k1Bip44: return ls.dev.SignRtSecp256k1(ls.path, metadata, message) + case wallet.AlgorithmSr25519Adr8: + return ls.dev.SignRtSr25519(ls.path, metadata, message) } return nil, fmt.Errorf("ledger: algorithm %s not supported", ls.algorithm) diff --git a/wallet/wallet.go b/wallet/wallet.go index eb1270d0..b8576970 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -16,7 +16,7 @@ import ( var registeredFactories sync.Map const ( - // AlgorithmEd25519Adr8 is the Ed25519 algorithm using the ADR-8 derivation path (ROSE coin type). + // AlgorithmEd25519Adr8 is the Ed25519 algorithm using the ADR-8 derivation path. AlgorithmEd25519Adr8 = "ed25519-adr8" // AlgorithmEd25519Raw is the Ed25519 algorithm using raw private keys. AlgorithmEd25519Raw = "ed25519-raw" @@ -26,6 +26,10 @@ const ( AlgorithmSecp256k1Bip44 = "secp256k1-bip44" // AlgorithmSecp256k1Raw is the Secp256k1 algorithm using raw private keys. AlgorithmSecp256k1Raw = "secp256k1-raw" + // AlgorithmSr25519Adr8 is the Sr25519 algorithm using the Ledger-compatible derivation path defined in ADR-8. + AlgorithmSr25519Adr8 = "sr25519-adr8" + // AlgorithmSr25519Raw is the Sr25519 algorithm using raw private keys. + AlgorithmSr25519Raw = "sr25519-raw" ) // Factory is a factory that supports accounts of a specific kind.