github.com/chain5j/chain5j-pkg@v1.0.7/crypto/scrypt/passphrase.go (about)

     1  // Package scrypt
     2  //
     3  // @author: xwc1125
     4  package scrypt
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/aes"
     9  	"crypto/rand"
    10  	"crypto/sha256"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"fmt"
    14  	"io"
    15  
    16  	"github.com/chain5j/chain5j-pkg/crypto/hashalg/sha3"
    17  	"github.com/pborman/uuid"
    18  	"golang.org/x/crypto/pbkdf2"
    19  	"golang.org/x/crypto/scrypt"
    20  )
    21  
    22  const (
    23  	keyHeaderKDF = "scrypt"
    24  
    25  	// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
    26  	// memory and taking approximately 1s CPU time on a modern processor.
    27  	StandardScryptN = 1 << 18
    28  
    29  	// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
    30  	// memory and taking approximately 1s CPU time on a modern processor.
    31  	StandardScryptP = 1
    32  
    33  	// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
    34  	// memory and taking approximately 100ms CPU time on a modern processor.
    35  	LightScryptN = 1 << 12
    36  
    37  	// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
    38  	// memory and taking approximately 100ms CPU time on a modern processor.
    39  	LightScryptP = 6
    40  
    41  	scryptR     = 8
    42  	scryptDKLen = 32
    43  )
    44  
    45  // EncryptDataV3 encrypts the data given as 'data' with the password 'auth'.
    46  func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
    47  
    48  	salt := make([]byte, 32)
    49  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
    50  		panic("reading from crypto/rand failed: " + err.Error())
    51  	}
    52  	derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
    53  	if err != nil {
    54  		return CryptoJSON{}, err
    55  	}
    56  	encryptKey := derivedKey[:16]
    57  
    58  	iv := make([]byte, aes.BlockSize) // 16
    59  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    60  		panic("reading from crypto/rand failed: " + err.Error())
    61  	}
    62  	cipherText, err := aesCTRXOR(encryptKey, data, iv)
    63  	if err != nil {
    64  		return CryptoJSON{}, err
    65  	}
    66  	mac := sha3.Keccak256(derivedKey[16:32], cipherText)
    67  
    68  	scryptParamsJSON := make(map[string]interface{}, 5)
    69  	scryptParamsJSON["n"] = scryptN
    70  	scryptParamsJSON["r"] = scryptR
    71  	scryptParamsJSON["p"] = scryptP
    72  	scryptParamsJSON["dklen"] = scryptDKLen
    73  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
    74  	cipherParamsJSON := cipherparamsJSON{
    75  		IV: hex.EncodeToString(iv),
    76  	}
    77  
    78  	cryptoStruct := CryptoJSON{
    79  		Cipher:       "aes-128-ctr",
    80  		CipherText:   hex.EncodeToString(cipherText),
    81  		CipherParams: cipherParamsJSON,
    82  		KDF:          keyHeaderKDF,
    83  		KDFParams:    scryptParamsJSON,
    84  		MAC:          hex.EncodeToString(mac),
    85  	}
    86  	return cryptoStruct, nil
    87  }
    88  
    89  // EncryptKey encrypts a key using the specified scrypt parameters into a json
    90  // blob that can be decrypted later on.
    91  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
    92  	keyBytes := key.PrivateKey
    93  	cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
    98  		cryptoStruct,
    99  		key.Id.String(),
   100  		version,
   101  	}
   102  	return json.Marshal(encryptedKeyJSONV3)
   103  }
   104  
   105  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   106  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   107  	// Parse the json into a simple map to fetch the key version
   108  	m := make(map[string]interface{})
   109  	if err := json.Unmarshal(keyjson, &m); err != nil {
   110  		return nil, err
   111  	}
   112  	// Depending on the version try to parse one way or another
   113  	var (
   114  		keyBytes, keyId []byte
   115  		err             error
   116  	)
   117  	if version, ok := m["version"].(string); ok && version == "1" {
   118  		k := new(encryptedKeyJSONV1)
   119  		if err := json.Unmarshal(keyjson, k); err != nil {
   120  			return nil, err
   121  		}
   122  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   123  	} else {
   124  		k := new(encryptedKeyJSONV3)
   125  		if err := json.Unmarshal(keyjson, k); err != nil {
   126  			return nil, err
   127  		}
   128  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   129  	}
   130  	// Handle any decryption errors and return the key
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return &Key{
   136  		Id:         uuid.UUID(keyId),
   137  		PrivateKey: keyBytes,
   138  	}, nil
   139  }
   140  
   141  func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
   142  	if cryptoJson.Cipher != "aes-128-ctr" {
   143  		return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
   144  	}
   145  	mac, err := hex.DecodeString(cryptoJson.MAC)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	derivedKey, err := getKDFKey(cryptoJson, auth)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	calculatedMAC := sha3.Keccak256(derivedKey[16:32], cipherText)
   166  	if !bytes.Equal(calculatedMAC, mac) {
   167  		return nil, ErrDecrypt
   168  	}
   169  
   170  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	return plainText, err
   175  }
   176  
   177  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   178  	if keyProtected.Version != version {
   179  		return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
   180  	}
   181  	keyId = uuid.Parse(keyProtected.Id)
   182  	plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
   183  	if err != nil {
   184  		return nil, nil, err
   185  	}
   186  	return plainText, keyId, err
   187  }
   188  
   189  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   190  	keyId = uuid.Parse(keyProtected.Id)
   191  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   192  	if err != nil {
   193  		return nil, nil, err
   194  	}
   195  
   196  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   197  	if err != nil {
   198  		return nil, nil, err
   199  	}
   200  
   201  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   202  	if err != nil {
   203  		return nil, nil, err
   204  	}
   205  
   206  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   207  	if err != nil {
   208  		return nil, nil, err
   209  	}
   210  
   211  	calculatedMAC := sha3.Keccak256(derivedKey[16:32], cipherText)
   212  	if !bytes.Equal(calculatedMAC, mac) {
   213  		return nil, nil, ErrDecrypt
   214  	}
   215  
   216  	plainText, err := aesCBCDecrypt(sha3.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   217  	if err != nil {
   218  		return nil, nil, err
   219  	}
   220  	return plainText, keyId, err
   221  }
   222  
   223  func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
   224  	authArray := []byte(auth)
   225  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   230  
   231  	if cryptoJSON.KDF == keyHeaderKDF {
   232  		n := ensureInt(cryptoJSON.KDFParams["n"])
   233  		r := ensureInt(cryptoJSON.KDFParams["r"])
   234  		p := ensureInt(cryptoJSON.KDFParams["p"])
   235  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   236  
   237  	} else if cryptoJSON.KDF == "pbkdf2" {
   238  		c := ensureInt(cryptoJSON.KDFParams["c"])
   239  		prf := cryptoJSON.KDFParams["prf"].(string)
   240  		if prf != "hmac-sha256" {
   241  			return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
   242  		}
   243  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   244  		return key, nil
   245  	}
   246  
   247  	return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
   248  }
   249  
   250  // TODO: can we do without this when unmarshalling dynamic JSON?
   251  // why do integers in KDF params end up as float64 and not int after
   252  // unmarshal?
   253  func ensureInt(x interface{}) int {
   254  	res, ok := x.(int)
   255  	if !ok {
   256  		res = int(x.(float64))
   257  	}
   258  	return res
   259  }