github.com/Finschia/finschia-sdk@v0.48.1/crypto/armor_test.go (about) 1 package crypto_test 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "testing" 9 10 ostcrypto "github.com/Finschia/ostracon/crypto" 11 "github.com/Finschia/ostracon/crypto/armor" 12 "github.com/Finschia/ostracon/crypto/xsalsa20symmetric" 13 "github.com/stretchr/testify/require" 14 "github.com/tendermint/crypto/bcrypt" 15 16 "github.com/Finschia/finschia-sdk/codec/legacy" 17 "github.com/Finschia/finschia-sdk/crypto" 18 "github.com/Finschia/finschia-sdk/crypto/hd" 19 "github.com/Finschia/finschia-sdk/crypto/keyring" 20 "github.com/Finschia/finschia-sdk/crypto/keys/secp256k1" 21 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 22 "github.com/Finschia/finschia-sdk/types" 23 ) 24 25 func TestArmorUnarmorPrivKey(t *testing.T) { 26 priv := secp256k1.GenPrivKey() 27 armored := crypto.EncryptArmorPrivKey(priv, "passphrase", "") 28 _, _, err := crypto.UnarmorDecryptPrivKey(armored, "wrongpassphrase") 29 require.Error(t, err) 30 decrypted, algo, err := crypto.UnarmorDecryptPrivKey(armored, "passphrase") 31 require.NoError(t, err) 32 require.Equal(t, string(hd.Secp256k1Type), algo) 33 require.True(t, priv.Equals(decrypted)) 34 35 // empty string 36 decrypted, algo, err = crypto.UnarmorDecryptPrivKey("", "passphrase") 37 require.Error(t, err) 38 require.True(t, errors.Is(io.EOF, err)) 39 require.Nil(t, decrypted) 40 require.Empty(t, algo) 41 42 // wrong key type 43 armored = crypto.ArmorPubKeyBytes(priv.PubKey().Bytes(), "") 44 _, _, err = crypto.UnarmorDecryptPrivKey(armored, "passphrase") 45 require.Error(t, err) 46 require.Contains(t, err.Error(), "unrecognized armor type") 47 48 // armor key manually 49 encryptPrivKeyFn := func(privKey cryptotypes.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) { 50 saltBytes = ostcrypto.CRandBytes(16) 51 key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), crypto.BcryptSecurityParameter) 52 require.NoError(t, err) 53 key = ostcrypto.Sha256(key) // get 32 bytes 54 privKeyBytes := legacy.Cdc.Amino.MustMarshalBinaryBare(privKey) 55 return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key) 56 } 57 saltBytes, encBytes := encryptPrivKeyFn(priv, "passphrase") 58 59 // wrong kdf header 60 headerWrongKdf := map[string]string{ 61 "kdf": "wrong", 62 "salt": fmt.Sprintf("%X", saltBytes), 63 "type": "secp256k", 64 } 65 armored = armor.EncodeArmor("OSTRACON PRIVATE KEY", headerWrongKdf, encBytes) 66 _, _, err = crypto.UnarmorDecryptPrivKey(armored, "passphrase") 67 require.Error(t, err) 68 require.Equal(t, "unrecognized KDF type: wrong", err.Error()) 69 } 70 71 func TestArmorUnarmorPubKey(t *testing.T) { 72 // Select the encryption and storage for your cryptostore 73 cstore := keyring.NewInMemory() 74 75 // Add keys and see they return in alphabetical order 76 info, _, err := cstore.NewMnemonic("Bob", keyring.English, types.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 77 require.NoError(t, err) 78 armored := crypto.ArmorPubKeyBytes(legacy.Cdc.Amino.MustMarshalBinaryBare(info.GetPubKey()), "") 79 pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armored) 80 require.NoError(t, err) 81 pub, err := legacy.PubKeyFromBytes(pubBytes) 82 require.NoError(t, err) 83 require.Equal(t, string(hd.Secp256k1Type), algo) 84 require.True(t, pub.Equals(info.GetPubKey())) 85 86 armored = crypto.ArmorPubKeyBytes(legacy.Cdc.Amino.MustMarshalBinaryBare(info.GetPubKey()), "unknown") 87 pubBytes, algo, err = crypto.UnarmorPubKeyBytes(armored) 88 require.NoError(t, err) 89 pub, err = legacy.PubKeyFromBytes(pubBytes) 90 require.NoError(t, err) 91 require.Equal(t, "unknown", algo) 92 require.True(t, pub.Equals(info.GetPubKey())) 93 94 armored, err = cstore.ExportPrivKeyArmor("Bob", "passphrase") 95 require.NoError(t, err) 96 _, _, err = crypto.UnarmorPubKeyBytes(armored) 97 require.Error(t, err) 98 require.Equal(t, `couldn't unarmor bytes: unrecognized armor type "OSTRACON PRIVATE KEY", expected: "OSTRACON PUBLIC KEY"`, err.Error()) 99 100 // armor pubkey manually 101 header := map[string]string{ 102 "version": "0.0.0", 103 "type": "unknown", 104 } 105 armored = armor.EncodeArmor("OSTRACON PUBLIC KEY", header, pubBytes) 106 _, algo, err = crypto.UnarmorPubKeyBytes(armored) 107 require.NoError(t, err) 108 // return secp256k1 if version is 0.0.0 109 require.Equal(t, "secp256k1", algo) 110 111 // missing version header 112 header = map[string]string{ 113 "type": "unknown", 114 } 115 armored = armor.EncodeArmor("OSTRACON PUBLIC KEY", header, pubBytes) 116 bz, algo, err := crypto.UnarmorPubKeyBytes(armored) 117 require.Nil(t, bz) 118 require.Empty(t, algo) 119 require.Error(t, err) 120 require.Equal(t, "header's version field is empty", err.Error()) 121 122 // unknown version header 123 header = map[string]string{ 124 "type": "unknown", 125 "version": "unknown", 126 } 127 armored = armor.EncodeArmor("OSTRACON PUBLIC KEY", header, pubBytes) 128 bz, algo, err = crypto.UnarmorPubKeyBytes(armored) 129 require.Nil(t, bz) 130 require.Empty(t, algo) 131 require.Error(t, err) 132 require.Equal(t, "unrecognized version: unknown", err.Error()) 133 } 134 135 func TestArmorInfoBytes(t *testing.T) { 136 bs := []byte("test") 137 armoredString := crypto.ArmorInfoBytes(bs) 138 unarmoredBytes, err := crypto.UnarmorInfoBytes(armoredString) 139 require.NoError(t, err) 140 require.True(t, bytes.Equal(bs, unarmoredBytes)) 141 } 142 143 func TestUnarmorInfoBytesErrors(t *testing.T) { 144 unarmoredBytes, err := crypto.UnarmorInfoBytes("") 145 require.Error(t, err) 146 require.True(t, errors.Is(io.EOF, err)) 147 require.Nil(t, unarmoredBytes) 148 149 header := map[string]string{ 150 "type": "Info", 151 "version": "0.0.1", 152 } 153 unarmoredBytes, err = crypto.UnarmorInfoBytes(armor.EncodeArmor( 154 "OSTRACON KEY INFO", header, []byte("plain-text"))) 155 require.Error(t, err) 156 require.Equal(t, "unrecognized version: 0.0.1", err.Error()) 157 require.Nil(t, unarmoredBytes) 158 } 159 160 func BenchmarkBcryptGenerateFromPassword(b *testing.B) { 161 passphrase := []byte("passphrase") 162 for securityParam := 9; securityParam < 16; securityParam++ { 163 param := securityParam 164 b.Run(fmt.Sprintf("benchmark-security-param-%d", param), func(b *testing.B) { 165 b.ReportAllocs() 166 saltBytes := ostcrypto.CRandBytes(16) 167 b.ResetTimer() 168 for i := 0; i < b.N; i++ { 169 _, err := bcrypt.GenerateFromPassword(saltBytes, passphrase, param) 170 require.Nil(b, err) 171 } 172 }) 173 } 174 }