You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

115 lines
3.3 KiB

package keys
import (
"encoding/hex"
"fmt"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-crypto/keys/bcrypt"
)
const (
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
blockTypeKeyInfo = "TENDERMINT KEY INFO"
blockTypePubKey = "TENDERMINT PUBLIC KEY"
)
func armorInfoBytes(bz []byte) string {
return armorBytes(bz, blockTypeKeyInfo)
}
func armorPubKeyBytes(bz []byte) string {
return armorBytes(bz, blockTypePubKey)
}
func armorBytes(bz []byte, blockType string) string {
header := map[string]string{
"type": "Info",
"version": "0.0.0",
}
return crypto.EncodeArmor(blockType, header, bz)
}
func unarmorInfoBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypeKeyInfo)
}
func unarmorPubKeyBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypePubKey)
}
func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
bType, header, bz, err := crypto.DecodeArmor(armorStr)
if err != nil {
return
}
if bType != blockType {
err = fmt.Errorf("Unrecognized armor type %q, expected: %q", bType, blockType)
return
}
if header["version"] != "0.0.0" {
err = fmt.Errorf("Unrecognized version: %v", header["version"])
return
}
return
}
func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
header := map[string]string{
"kdf": "bcrypt",
"salt": fmt.Sprintf("%X", saltBytes),
}
armorStr := crypto.EncodeArmor(blockTypePrivKey, header, encBytes)
return armorStr
}
func unarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) {
var privKey crypto.PrivKey
blockType, header, encBytes, err := crypto.DecodeArmor(armorStr)
if err != nil {
return privKey, err
}
if blockType != blockTypePrivKey {
return privKey, fmt.Errorf("Unrecognized armor type: %v", blockType)
}
if header["kdf"] != "bcrypt" {
return privKey, fmt.Errorf("Unrecognized KDF type: %v", header["KDF"])
}
if header["salt"] == "" {
return privKey, fmt.Errorf("Missing salt bytes")
}
saltBytes, err := hex.DecodeString(header["salt"])
if err != nil {
return privKey, fmt.Errorf("Error decoding salt: %v", err.Error())
}
privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase)
return privKey, err
}
func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) {
saltBytes = crypto.CRandBytes(16)
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016)
if err != nil {
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes := privKey.Bytes()
return saltBytes, crypto.EncryptSymmetric(privKeyBytes, key)
}
func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016)
if err != nil {
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
}
key = crypto.Sha256(key) // Get 32 bytes
privKeyBytes, err := crypto.DecryptSymmetric(encBytes, key)
if err != nil {
return privKey, err
}
privKey, err = crypto.PrivKeyFromBytes(privKeyBytes)
return privKey, err
}