github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/armor/armor.go (about) 1 package armor 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 7 "github.com/gnolang/gno/tm2/pkg/crypto" 8 "github.com/gnolang/gno/tm2/pkg/crypto/armor" 9 "github.com/gnolang/gno/tm2/pkg/crypto/bcrypt" 10 "github.com/gnolang/gno/tm2/pkg/crypto/keys/keyerror" 11 "github.com/gnolang/gno/tm2/pkg/crypto/xsalsa20symmetric" 12 "github.com/gnolang/gno/tm2/pkg/os" 13 ) 14 15 const ( 16 blockTypePrivKey = "TENDERMINT PRIVATE KEY" 17 blockTypeKeyInfo = "TENDERMINT KEY INFO" 18 blockTypePubKey = "TENDERMINT PUBLIC KEY" 19 bcryptSecurityParameter = 12 20 ) 21 22 // ----------------------------------------------------------------- 23 // add armor 24 25 // Armor the InfoBytes 26 func ArmorInfoBytes(bz []byte) string { 27 return armorBytes(bz, blockTypeKeyInfo) 28 } 29 30 // Armor the PubKeyBytes 31 func ArmorPubKeyBytes(bz []byte) string { 32 return armorBytes(bz, blockTypePubKey) 33 } 34 35 func armorBytes(bz []byte, blockType string) string { 36 header := map[string]string{ 37 "type": "Info", 38 "version": "0.0.0", 39 } 40 return armor.EncodeArmor(blockType, header, bz) 41 } 42 43 // ----------------------------------------------------------------- 44 // remove armor 45 46 // Unarmor the InfoBytes 47 func UnarmorInfoBytes(armorStr string) (bz []byte, err error) { 48 return unarmorBytes(armorStr, blockTypeKeyInfo) 49 } 50 51 // Unarmor the PubKeyBytes 52 func UnarmorPubKeyBytes(armorStr string) (bz []byte, err error) { 53 return unarmorBytes(armorStr, blockTypePubKey) 54 } 55 56 func unarmorBytes(armorStr, blockType string) (bz []byte, err error) { 57 bType, header, bz, err := armor.DecodeArmor(armorStr) 58 if err != nil { 59 return 60 } 61 if bType != blockType { 62 err = fmt.Errorf("unrecognized armor type %q, expected: %q", bType, blockType) 63 return 64 } 65 if header["version"] != "0.0.0" { 66 err = fmt.Errorf("unrecognized version: %v", header["version"]) 67 return 68 } 69 return 70 } 71 72 // ----------------------------------------------------------------- 73 // encrypt/decrypt with armor 74 75 // Encrypt and armor the private key. 76 func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string { 77 saltBytes, encBytes := encryptPrivKey(privKey, passphrase) 78 header := map[string]string{ 79 "kdf": "bcrypt", 80 "salt": fmt.Sprintf("%X", saltBytes), 81 } 82 armorStr := armor.EncodeArmor(blockTypePrivKey, header, encBytes) 83 return armorStr 84 } 85 86 // encrypt the given privKey with the passphrase using a randomly 87 // generated salt and the xsalsa20 cipher. returns the salt and the 88 // encrypted priv key. 89 func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) { 90 saltBytes = crypto.CRandBytes(16) 91 key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), bcryptSecurityParameter) 92 if err != nil { 93 os.Exit("Error generating bcrypt key from passphrase: " + err.Error()) 94 } 95 key = crypto.Sha256(key) // get 32 bytes 96 privKeyBytes := privKey.Bytes() 97 return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key) 98 } 99 100 // Unarmor and decrypt the private key. 101 func UnarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) { 102 var privKey crypto.PrivKey 103 blockType, header, encBytes, err := armor.DecodeArmor(armorStr) 104 if err != nil { 105 return privKey, err 106 } 107 if blockType != blockTypePrivKey { 108 return privKey, fmt.Errorf("unrecognized armor type: %v", blockType) 109 } 110 if header["kdf"] != "bcrypt" { 111 return privKey, fmt.Errorf("unrecognized KDF type: %v", header["KDF"]) 112 } 113 if header["salt"] == "" { 114 return privKey, fmt.Errorf("missing salt bytes") 115 } 116 saltBytes, err := hex.DecodeString(header["salt"]) 117 if err != nil { 118 return privKey, fmt.Errorf("error decoding salt: %w", err) 119 } 120 privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) 121 return privKey, err 122 } 123 124 func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) { 125 key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), bcryptSecurityParameter) 126 if err != nil { 127 os.Exit("Error generating bcrypt key from passphrase: " + err.Error()) 128 } 129 key = crypto.Sha256(key) // Get 32 bytes 130 privKeyBytes, err := xsalsa20symmetric.DecryptSymmetric(encBytes, key) 131 if err != nil && err.Error() == "ciphertext decryption failed" { 132 return privKey, keyerror.NewErrWrongPassword() 133 } else if err != nil { 134 return privKey, err 135 } 136 privKey, err = crypto.PrivKeyFromBytes(privKeyBytes) 137 return privKey, err 138 }