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