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  }