From b7c53433c672070a8b6a2d3999cfc32d702f8d5c Mon Sep 17 00:00:00 2001 From: jon4hz Date: Mon, 14 Mar 2022 20:10:25 +0100 Subject: [PATCH 1/4] feat: add support for all language supported by tyler-smith/go-bip39 --- cmd/melt/main.go | 48 +++++++++++++++++++++++++++++++++++++++++-- cmd/melt/main_test.go | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/cmd/melt/main.go b/cmd/melt/main.go index 59e90ae..89a2044 100644 --- a/cmd/melt/main.go +++ b/cmd/melt/main.go @@ -18,6 +18,8 @@ import ( "github.com/muesli/reflow/wordwrap" "github.com/muesli/roff" "github.com/muesli/termenv" + "github.com/tyler-smith/go-bip39" + "github.com/tyler-smith/go-bip39/wordlists" "golang.org/x/crypto/ssh" "golang.org/x/term" ) @@ -39,6 +41,9 @@ var ( Padding(1, 2) keyPathStyle = lipgloss.NewStyle().Foreground(violet) + mnemonic string + language string + rootCmd = &coral.Command{ Use: "melt", Example: ` melt ~/.ssh/id_ed25519 @@ -51,6 +56,10 @@ be used to rebuild your public and private keys.`, Args: coral.ExactArgs(1), SilenceUsage: true, RunE: func(cmd *coral.Command, args []string) error { + if err := setLanguage(language); err != nil { + return err + } + mnemonic, err := backup(args[0], nil) if err != nil { return err @@ -67,8 +76,12 @@ be used to rebuild your public and private keys.`, // Build formatted restore command const cmdEOL = " \\" + var lang string + if language != "en" { + lang = fmt.Sprintf(" -l %s", language) + } cmd := wordwrap.String( - os.Args[0]+` restore ./my-key --seed "`+mnemonic+`"`, + os.Args[0]+` restore`+lang+` ./my-key --seed "`+mnemonic+`"`, w-lipgloss.Width(cmdEOL)-baseStyle.GetHorizontalFrameSize()*2, ) leftPad := strings.Repeat(" ", baseStyle.GetMarginLeft()) @@ -91,7 +104,6 @@ be used to rebuild your public and private keys.`, }, } - mnemonic string restoreCmd = &coral.Command{ Use: "restore", Short: "Recreate a key using the given seed phrase", @@ -100,6 +112,10 @@ be used to rebuild your public and private keys.`, Aliases: []string{"res", "r"}, Args: coral.ExactArgs(1), RunE: func(cmd *coral.Command, args []string) error { + if err := setLanguage(language); err != nil { + return err + } + if err := restore(maybeFile(mnemonic), args[0]); err != nil { return err } @@ -131,6 +147,7 @@ be used to rebuild your public and private keys.`, ) func init() { + rootCmd.PersistentFlags().StringVarP(&language, "language", "l", "en", "Language") rootCmd.AddCommand(restoreCmd, manCmd) restoreCmd.PersistentFlags().StringVarP(&mnemonic, "seed", "s", "-", "Seed phrase") @@ -238,3 +255,30 @@ func completeColor(truecolor, ansi256, ansi string) string { } return ansi } + +// setLanguage sets the language of the big39 mnemonic seed. +func setLanguage(language string) error { + switch strings.ToLower(language) { + case "chinese-simplified": + bip39.SetWordList(wordlists.ChineseSimplified) + case "chinese-traditional": + bip39.SetWordList(wordlists.ChineseTraditional) + case "czech", "cs": + bip39.SetWordList(wordlists.Czech) + case "english", "en": + bip39.SetWordList(wordlists.English) + case "french", "fr": + bip39.SetWordList(wordlists.French) + case "italian", "it": + bip39.SetWordList(wordlists.Italian) + case "japanese", "ja": + bip39.SetWordList(wordlists.Japanese) + case "korean", "ko": + bip39.SetWordList(wordlists.Korean) + case "spanish", "es": + bip39.SetWordList(wordlists.Spanish) + default: + return fmt.Errorf("this language is not supported") + } + return nil +} diff --git a/cmd/melt/main_test.go b/cmd/melt/main_test.go index 6801afd..ce5175d 100644 --- a/cmd/melt/main_test.go +++ b/cmd/melt/main_test.go @@ -76,6 +76,45 @@ func TestBackupRestoreKnownKey(t *testing.T) { }) } +func TestBackupRestoreKnownKeyInJapanse(t *testing.T) { + const expectedMnemonic = ` + いきおい ざるそば えもの せんめんじょ てあみ ていねい はったつ + ろこつ すあし のぞく かまう ほくろ らくご けぶかい たおす よゆう + ひめじし くたびれる ぐんたい なわばり にかい えほん せなか + そいとげる + ` + const expectedSum = "ba34175ef608633b29f046b40cce596dd221347b77abba40763eef2e7ae51fe9" + const expectedFingerprint = "SHA256:tX0ZrsNLIB/ZlRK3vy/HsWIIkyBNhYhCSGmtqtxJcWo" + + // set language to Japanse + setLanguage("japanese") + + t.Run("backup", func(t *testing.T) { + mnemonic, err := backup("testdata/id_ed25519", nil) + is := is.New(t) + is.NoErr(err) + is.Equal(mnemonic, strings.Join(strings.Fields(expectedMnemonic), " ")) + }) + + t.Run("restore", func(t *testing.T) { + is := is.New(t) + path := filepath.Join(t.TempDir(), "key") + is.NoErr(restore(expectedMnemonic, path)) + is.Equal(expectedSum, sha256sum(t, path+".pub")) + + bts, err := os.ReadFile(path) + is.NoErr(err) + + k, err := ssh.ParsePrivateKey(bts) + is.NoErr(err) + + is.Equal(expectedFingerprint, ssh.FingerprintSHA256(k.PublicKey())) + }) + + // set language back to English + setLanguage("english") +} + func TestMaybeFile(t *testing.T) { t.Run("is a file", func(t *testing.T) { is := is.New(t) From 3fbaf963566b2dbda2378eafa024cafd52ef71ff Mon Sep 17 00:00:00 2001 From: jon4hz Date: Mon, 14 Mar 2022 20:23:06 +0100 Subject: [PATCH 2/4] fix: use full language flag in cmd --- cmd/melt/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/melt/main.go b/cmd/melt/main.go index 89a2044..71a1f8f 100644 --- a/cmd/melt/main.go +++ b/cmd/melt/main.go @@ -78,7 +78,7 @@ be used to rebuild your public and private keys.`, const cmdEOL = " \\" var lang string if language != "en" { - lang = fmt.Sprintf(" -l %s", language) + lang = fmt.Sprintf(" --language %s", language) } cmd := wordwrap.String( os.Args[0]+` restore`+lang+` ./my-key --seed "`+mnemonic+`"`, From 0fddd3fb95023b2bf23b0280373fc3b41bba9b8a Mon Sep 17 00:00:00 2001 From: jon4hz Date: Mon, 14 Mar 2022 20:26:51 +0100 Subject: [PATCH 3/4] fix: use t.Cleanup to reset language --- cmd/melt/main_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/melt/main_test.go b/cmd/melt/main_test.go index ce5175d..210e419 100644 --- a/cmd/melt/main_test.go +++ b/cmd/melt/main_test.go @@ -89,6 +89,11 @@ func TestBackupRestoreKnownKeyInJapanse(t *testing.T) { // set language to Japanse setLanguage("japanese") + // set language back to English + t.Cleanup(func() { + setLanguage("english") + }) + t.Run("backup", func(t *testing.T) { mnemonic, err := backup("testdata/id_ed25519", nil) is := is.New(t) @@ -110,9 +115,6 @@ func TestBackupRestoreKnownKeyInJapanse(t *testing.T) { is.Equal(expectedFingerprint, ssh.FingerprintSHA256(k.PublicKey())) }) - - // set language back to English - setLanguage("english") } func TestMaybeFile(t *testing.T) { From 0d68ea3815c8c97b3d8fc7d1e8163065874c9c2f Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 15 Mar 2022 09:05:44 +0100 Subject: [PATCH 4/4] fix: add more language identifier for Chinese --- cmd/melt/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/melt/main.go b/cmd/melt/main.go index 71a1f8f..4bf53cc 100644 --- a/cmd/melt/main.go +++ b/cmd/melt/main.go @@ -259,9 +259,9 @@ func completeColor(truecolor, ansi256, ansi string) string { // setLanguage sets the language of the big39 mnemonic seed. func setLanguage(language string) error { switch strings.ToLower(language) { - case "chinese-simplified": + case "chinese-simplified", "zh", "zh_HANS": bip39.SetWordList(wordlists.ChineseSimplified) - case "chinese-traditional": + case "chinese-traditional", "zh_HANT": bip39.SetWordList(wordlists.ChineseTraditional) case "czech", "cs": bip39.SetWordList(wordlists.Czech)