github.com/igggame/nebulas-go@v2.1.0+incompatible/crypto/cipher/scrypt.go (about)

     1  // Copyright (C) 2017 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package cipher
    20  
    21  import (
    22  	"bytes"
    23  	"crypto/aes"
    24  	"crypto/cipher"
    25  	"encoding/hex"
    26  	"encoding/json"
    27  	"errors"
    28  
    29  	"github.com/nebulasio/go-nebulas/crypto/hash"
    30  	"github.com/nebulasio/go-nebulas/crypto/utils"
    31  	uuid "github.com/satori/go.uuid"
    32  	"golang.org/x/crypto/scrypt"
    33  )
    34  
    35  const (
    36  	// ScryptKDF name
    37  	ScryptKDF = "scrypt"
    38  
    39  	// StandardScryptN N parameter of Scrypt encryption algorithm
    40  	StandardScryptN = 1 << 12 //TODO: check 1<<12 vs 1<<18
    41  
    42  	// StandardScryptR r parameter of Scrypt encryption algorithm
    43  	StandardScryptR = 8
    44  
    45  	// StandardScryptP p parameter of Scrypt encryption algorithm
    46  	StandardScryptP = 1
    47  
    48  	// ScryptDKLen get derived key length
    49  	ScryptDKLen = 32
    50  
    51  	// cipher the name of cipher
    52  	cipherName = "aes-128-ctr"
    53  
    54  	// version compatible with ethereum, the version start with 3
    55  	version3       = 3
    56  	currentVersion = 4
    57  
    58  	// mac calculate hash type
    59  	macHash = "sha3256"
    60  )
    61  
    62  var (
    63  	// ErrVersionInvalid version not supported
    64  	ErrVersionInvalid = errors.New("version not supported")
    65  
    66  	// ErrKDFInvalid cipher not supported
    67  	ErrKDFInvalid = errors.New("kdf not supported")
    68  
    69  	// ErrCipherInvalid cipher not supported
    70  	ErrCipherInvalid = errors.New("cipher not supported")
    71  
    72  	// ErrDecrypt decrypt failed
    73  	ErrDecrypt = errors.New("could not decrypt key with given passphrase")
    74  )
    75  
    76  type cipherparamsJSON struct {
    77  	IV string `json:"iv"`
    78  }
    79  
    80  type cryptoJSON struct {
    81  	Cipher       string                 `json:"cipher"`
    82  	CipherText   string                 `json:"ciphertext"`
    83  	CipherParams cipherparamsJSON       `json:"cipherparams"`
    84  	KDF          string                 `json:"kdf"`
    85  	KDFParams    map[string]interface{} `json:"kdfparams"`
    86  	MAC          string                 `json:"mac"`
    87  	MACHash      string                 `json:"machash"`
    88  }
    89  
    90  type encryptedKeyJSON struct {
    91  	Address string     `json:"address"`
    92  	Crypto  cryptoJSON `json:"crypto"`
    93  	ID      string     `json:"id"`
    94  	Version int        `json:"version"`
    95  }
    96  
    97  // Scrypt scrypt encrypt
    98  type Scrypt struct {
    99  }
   100  
   101  // EncryptKey encrypt key with address
   102  func (s *Scrypt) EncryptKey(address string, data []byte, passphrase []byte) ([]byte, error) {
   103  	crypto, err := s.scryptEncrypt(data, passphrase, StandardScryptN, StandardScryptR, StandardScryptP)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	encryptedKeyJSON := encryptedKeyJSON{
   108  		string(address),
   109  		*crypto,
   110  		uuid.NewV4().String(),
   111  		currentVersion,
   112  	}
   113  	return json.Marshal(encryptedKeyJSON)
   114  }
   115  
   116  // Encrypt scrypt encrypt
   117  func (s *Scrypt) Encrypt(data []byte, passphrase []byte) ([]byte, error) {
   118  	return s.ScryptEncrypt(data, passphrase, StandardScryptN, StandardScryptR, StandardScryptP)
   119  }
   120  
   121  // ScryptEncrypt encrypts a key using the specified scrypt parameters into a json
   122  // blob that can be decrypted later on.
   123  // N is a CPU/memory cost parameter, which must be a power of two greater than 1.
   124  // r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the
   125  // limits, the function returns a nil byte slice and an error.
   126  func (s *Scrypt) ScryptEncrypt(data []byte, passphrase []byte, N, r, p int) ([]byte, error) {
   127  	crypto, err := s.scryptEncrypt(data, passphrase, N, r, p)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	return json.Marshal(crypto)
   132  }
   133  
   134  func (s *Scrypt) scryptEncrypt(data []byte, passphrase []byte, N, r, p int) (*cryptoJSON, error) {
   135  	salt := utils.RandomCSPRNG(ScryptDKLen)
   136  	derivedKey, err := scrypt.Key(passphrase, salt, N, r, p, ScryptDKLen)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	encryptKey := derivedKey[:16]
   141  
   142  	iv := utils.RandomCSPRNG(aes.BlockSize) // 16
   143  	cipherText, err := s.aesCTRXOR(encryptKey, data, iv)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	//mac := hash.Sha3256(derivedKey[16:32], cipherText) // version3: deprecated
   149  	mac := hash.Sha3256(derivedKey[16:32], cipherText, iv, []byte(cipherName))
   150  
   151  	scryptParamsJSON := make(map[string]interface{}, 5)
   152  	scryptParamsJSON["n"] = N
   153  	scryptParamsJSON["r"] = r
   154  	scryptParamsJSON["p"] = p
   155  	scryptParamsJSON["dklen"] = ScryptDKLen
   156  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   157  
   158  	cipherParamsJSON := cipherparamsJSON{
   159  		IV: hex.EncodeToString(iv),
   160  	}
   161  
   162  	crypto := &cryptoJSON{
   163  		Cipher:       cipherName,
   164  		CipherText:   hex.EncodeToString(cipherText),
   165  		CipherParams: cipherParamsJSON,
   166  		KDF:          ScryptKDF,
   167  		KDFParams:    scryptParamsJSON,
   168  		MAC:          hex.EncodeToString(mac),
   169  		MACHash:      macHash,
   170  	}
   171  	return crypto, nil
   172  }
   173  
   174  func (s *Scrypt) aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
   175  	aesBlock, err := aes.NewCipher(key)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	stream := cipher.NewCTR(aesBlock, iv)
   180  	outText := make([]byte, len(inText))
   181  	stream.XORKeyStream(outText, inText)
   182  	return outText, err
   183  }
   184  
   185  // Decrypt decrypts data from a json blob, returning the origin data
   186  func (s *Scrypt) Decrypt(data []byte, passphrase []byte) ([]byte, error) {
   187  	crypto := new(cryptoJSON)
   188  	if err := json.Unmarshal(data, crypto); err != nil {
   189  		return nil, err
   190  	}
   191  	return s.scryptDecrypt(crypto, passphrase, currentVersion)
   192  }
   193  
   194  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   195  func (s *Scrypt) DecryptKey(keyjson []byte, passphrase []byte) ([]byte, error) {
   196  	keyJSON := new(encryptedKeyJSON)
   197  	if err := json.Unmarshal(keyjson, keyJSON); err != nil {
   198  		return nil, err
   199  	}
   200  	version := keyJSON.Version
   201  	if version != currentVersion && version != version3 {
   202  		return nil, ErrVersionInvalid
   203  	}
   204  	return s.scryptDecrypt(&keyJSON.Crypto, passphrase, version)
   205  }
   206  
   207  func (s *Scrypt) scryptDecrypt(crypto *cryptoJSON, passphrase []byte, version int) ([]byte, error) {
   208  
   209  	if crypto.Cipher != cipherName {
   210  		return nil, ErrCipherInvalid
   211  	}
   212  
   213  	mac, err := hex.DecodeString(crypto.MAC)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  
   218  	iv, err := hex.DecodeString(crypto.CipherParams.IV)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	cipherText, err := hex.DecodeString(crypto.CipherText)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	salt, err := hex.DecodeString(crypto.KDFParams["salt"].(string))
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	dklen := ensureInt(crypto.KDFParams["dklen"])
   234  	var derivedKey = []byte{}
   235  	if crypto.KDF == ScryptKDF {
   236  		n := ensureInt(crypto.KDFParams["n"])
   237  		r := ensureInt(crypto.KDFParams["r"])
   238  		p := ensureInt(crypto.KDFParams["p"])
   239  		derivedKey, err = scrypt.Key(passphrase, salt, n, r, p, dklen)
   240  		if err != nil {
   241  			return nil, err
   242  		}
   243  	} else {
   244  		return nil, ErrKDFInvalid
   245  	}
   246  
   247  	var calculatedMAC []byte
   248  
   249  	if version == currentVersion {
   250  		calculatedMAC = hash.Sha3256(derivedKey[16:32], cipherText, iv, []byte(crypto.Cipher))
   251  	} else if version == version3 {
   252  		calculatedMAC = hash.Sha3256(derivedKey[16:32], cipherText)
   253  		if crypto.MACHash != macHash {
   254  			// compatible ethereum keystore file,
   255  			calculatedMAC = hash.Keccak256(derivedKey[16:32], cipherText)
   256  		}
   257  	} else {
   258  		return nil, ErrVersionInvalid
   259  	}
   260  
   261  	if !bytes.Equal(calculatedMAC, mac) {
   262  		return nil, ErrDecrypt
   263  	}
   264  
   265  	key, err := s.aesCTRXOR(derivedKey[:16], cipherText, iv)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  	return key, nil
   270  }
   271  
   272  // because json.Unmarshal change int to float64, convert to int
   273  func ensureInt(x interface{}) int {
   274  	res, ok := x.(int)
   275  	if !ok {
   276  		res = int(x.(float64))
   277  	}
   278  	return res
   279  }