...

Source file src/github.com/theupdateframework/go-tuf/encrypted/encrypted.go

Documentation: github.com/theupdateframework/go-tuf/encrypted

     1  // Package encrypted provides a simple, secure system for encrypting data
     2  // symmetrically with a passphrase.
     3  //
     4  // It uses scrypt derive a key from the passphrase and the NaCl secret box
     5  // cipher for authenticated encryption.
     6  //
     7  // Deprecated: The encrypted package from go-tuf is already moved to
     8  // https://github.com/secure-systems-lab/go-securesystemslib and will be deprecated here.
     9  // Use github.com/secure-systems-lab/go-securesystemslib/encrypted instead.
    10  package encrypted
    11  
    12  import (
    13  	"crypto/rand"
    14  	"encoding/json"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  
    19  	"golang.org/x/crypto/nacl/secretbox"
    20  	"golang.org/x/crypto/scrypt"
    21  )
    22  
    23  const saltSize = 32
    24  
    25  const (
    26  	boxKeySize   = 32
    27  	boxNonceSize = 24
    28  )
    29  
    30  // KDFParameterStrength defines the KDF parameter strength level to be used for
    31  // encryption key derivation.
    32  type KDFParameterStrength uint8
    33  
    34  const (
    35  	// Legacy defines legacy scrypt parameters (N:2^15, r:8, p:1)
    36  	Legacy KDFParameterStrength = iota + 1
    37  	// Standard defines standard scrypt parameters which is focusing 100ms of computation (N:2^16, r:8, p:1)
    38  	Standard
    39  	// OWASP defines OWASP recommended scrypt parameters (N:2^17, r:8, p:1)
    40  	OWASP
    41  )
    42  
    43  var (
    44  	// legacyParams represents old scrypt derivation parameters for backward
    45  	// compatibility.
    46  	legacyParams = scryptParams{
    47  		N: 32768, // 2^15
    48  		R: 8,
    49  		P: 1,
    50  	}
    51  
    52  	// standardParams defines scrypt parameters based on the scrypt creator
    53  	// recommendation to limit key derivation in time boxed to 100ms.
    54  	standardParams = scryptParams{
    55  		N: 65536, // 2^16
    56  		R: 8,
    57  		P: 1,
    58  	}
    59  
    60  	// owaspParams defines scrypt parameters recommended by OWASP
    61  	owaspParams = scryptParams{
    62  		N: 131072, // 2^17
    63  		R: 8,
    64  		P: 1,
    65  	}
    66  
    67  	// defaultParams defines scrypt parameters which will be used to generate a
    68  	// new key.
    69  	defaultParams = standardParams
    70  )
    71  
    72  const (
    73  	nameScrypt    = "scrypt"
    74  	nameSecretBox = "nacl/secretbox"
    75  )
    76  
    77  type data struct {
    78  	KDF        scryptKDF       `json:"kdf"`
    79  	Cipher     secretBoxCipher `json:"cipher"`
    80  	Ciphertext []byte          `json:"ciphertext"`
    81  }
    82  
    83  type scryptParams struct {
    84  	N int `json:"N"`
    85  	R int `json:"r"`
    86  	P int `json:"p"`
    87  }
    88  
    89  func (sp *scryptParams) Equal(in *scryptParams) bool {
    90  	return in != nil && sp.N == in.N && sp.P == in.P && sp.R == in.R
    91  }
    92  
    93  func newScryptKDF(level KDFParameterStrength) (scryptKDF, error) {
    94  	salt := make([]byte, saltSize)
    95  	if err := fillRandom(salt); err != nil {
    96  		return scryptKDF{}, fmt.Errorf("unable to generate a random salt: %w", err)
    97  	}
    98  
    99  	var params scryptParams
   100  	switch level {
   101  	case Legacy:
   102  		params = legacyParams
   103  	case Standard:
   104  		params = standardParams
   105  	case OWASP:
   106  		params = owaspParams
   107  	default:
   108  		// Fallback to default parameters
   109  		params = defaultParams
   110  	}
   111  
   112  	return scryptKDF{
   113  		Name:   nameScrypt,
   114  		Params: params,
   115  		Salt:   salt,
   116  	}, nil
   117  }
   118  
   119  type scryptKDF struct {
   120  	Name   string       `json:"name"`
   121  	Params scryptParams `json:"params"`
   122  	Salt   []byte       `json:"salt"`
   123  }
   124  
   125  func (s *scryptKDF) Key(passphrase []byte) ([]byte, error) {
   126  	return scrypt.Key(passphrase, s.Salt, s.Params.N, s.Params.R, s.Params.P, boxKeySize)
   127  }
   128  
   129  // CheckParams checks that the encoded KDF parameters are what we expect them to
   130  // be. If we do not do this, an attacker could cause a DoS by tampering with
   131  // them.
   132  func (s *scryptKDF) CheckParams() error {
   133  	switch {
   134  	case legacyParams.Equal(&s.Params):
   135  	case standardParams.Equal(&s.Params):
   136  	case owaspParams.Equal(&s.Params):
   137  	default:
   138  		return errors.New("unsupported scrypt parameters")
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  func newSecretBoxCipher() (secretBoxCipher, error) {
   145  	nonce := make([]byte, boxNonceSize)
   146  	if err := fillRandom(nonce); err != nil {
   147  		return secretBoxCipher{}, err
   148  	}
   149  	return secretBoxCipher{
   150  		Name:  nameSecretBox,
   151  		Nonce: nonce,
   152  	}, nil
   153  }
   154  
   155  type secretBoxCipher struct {
   156  	Name  string `json:"name"`
   157  	Nonce []byte `json:"nonce"`
   158  
   159  	encrypted bool
   160  }
   161  
   162  func (s *secretBoxCipher) Encrypt(plaintext, key []byte) []byte {
   163  	var keyBytes [boxKeySize]byte
   164  	var nonceBytes [boxNonceSize]byte
   165  
   166  	if len(key) != len(keyBytes) {
   167  		panic("incorrect key size")
   168  	}
   169  	if len(s.Nonce) != len(nonceBytes) {
   170  		panic("incorrect nonce size")
   171  	}
   172  
   173  	copy(keyBytes[:], key)
   174  	copy(nonceBytes[:], s.Nonce)
   175  
   176  	// ensure that we don't re-use nonces
   177  	if s.encrypted {
   178  		panic("Encrypt must only be called once for each cipher instance")
   179  	}
   180  	s.encrypted = true
   181  
   182  	return secretbox.Seal(nil, plaintext, &nonceBytes, &keyBytes)
   183  }
   184  
   185  func (s *secretBoxCipher) Decrypt(ciphertext, key []byte) ([]byte, error) {
   186  	var keyBytes [boxKeySize]byte
   187  	var nonceBytes [boxNonceSize]byte
   188  
   189  	if len(key) != len(keyBytes) {
   190  		panic("incorrect key size")
   191  	}
   192  	if len(s.Nonce) != len(nonceBytes) {
   193  		// return an error instead of panicking since the nonce is user input
   194  		return nil, errors.New("encrypted: incorrect nonce size")
   195  	}
   196  
   197  	copy(keyBytes[:], key)
   198  	copy(nonceBytes[:], s.Nonce)
   199  
   200  	res, ok := secretbox.Open(nil, ciphertext, &nonceBytes, &keyBytes)
   201  	if !ok {
   202  		return nil, errors.New("encrypted: decryption failed")
   203  	}
   204  	return res, nil
   205  }
   206  
   207  // Encrypt takes a passphrase and plaintext, and returns a JSON object
   208  // containing ciphertext and the details necessary to decrypt it.
   209  func Encrypt(plaintext, passphrase []byte) ([]byte, error) {
   210  	return EncryptWithCustomKDFParameters(plaintext, passphrase, Standard)
   211  }
   212  
   213  // EncryptWithCustomKDFParameters takes a passphrase, the plaintext and a KDF
   214  // parameter level (Legacy, Standard, or OWASP), and returns a JSON object
   215  // containing ciphertext and the details necessary to decrypt it.
   216  func EncryptWithCustomKDFParameters(plaintext, passphrase []byte, kdfLevel KDFParameterStrength) ([]byte, error) {
   217  	k, err := newScryptKDF(kdfLevel)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	key, err := k.Key(passphrase)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	c, err := newSecretBoxCipher()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	data := &data{
   232  		KDF:    k,
   233  		Cipher: c,
   234  	}
   235  	data.Ciphertext = c.Encrypt(plaintext, key)
   236  
   237  	return json.Marshal(data)
   238  }
   239  
   240  // Marshal encrypts the JSON encoding of v using passphrase.
   241  func Marshal(v interface{}, passphrase []byte) ([]byte, error) {
   242  	return MarshalWithCustomKDFParameters(v, passphrase, Standard)
   243  }
   244  
   245  // MarshalWithCustomKDFParameters encrypts the JSON encoding of v using passphrase.
   246  func MarshalWithCustomKDFParameters(v interface{}, passphrase []byte, kdfLevel KDFParameterStrength) ([]byte, error) {
   247  	data, err := json.MarshalIndent(v, "", "\t")
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	return EncryptWithCustomKDFParameters(data, passphrase, kdfLevel)
   252  }
   253  
   254  // Decrypt takes a JSON-encoded ciphertext object encrypted using Encrypt and
   255  // tries to decrypt it using passphrase. If successful, it returns the
   256  // plaintext.
   257  func Decrypt(ciphertext, passphrase []byte) ([]byte, error) {
   258  	data := &data{}
   259  	if err := json.Unmarshal(ciphertext, data); err != nil {
   260  		return nil, err
   261  	}
   262  
   263  	if data.KDF.Name != nameScrypt {
   264  		return nil, fmt.Errorf("encrypted: unknown kdf name %q", data.KDF.Name)
   265  	}
   266  	if data.Cipher.Name != nameSecretBox {
   267  		return nil, fmt.Errorf("encrypted: unknown cipher name %q", data.Cipher.Name)
   268  	}
   269  	if err := data.KDF.CheckParams(); err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	key, err := data.KDF.Key(passphrase)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  
   278  	return data.Cipher.Decrypt(data.Ciphertext, key)
   279  }
   280  
   281  // Unmarshal decrypts the data using passphrase and unmarshals the resulting
   282  // plaintext into the value pointed to by v.
   283  func Unmarshal(data []byte, v interface{}, passphrase []byte) error {
   284  	decrypted, err := Decrypt(data, passphrase)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	return json.Unmarshal(decrypted, v)
   289  }
   290  
   291  func fillRandom(b []byte) error {
   292  	_, err := io.ReadFull(rand.Reader, b)
   293  	return err
   294  }
   295  

View as plain text