github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/helpers/crypto/box.go (about)

     1  package crypto
     2  
     3  import (
     4  	"crypto/rand"
     5  	"errors"
     6  
     7  	"code.google.com/p/go.crypto/nacl/secretbox"
     8  )
     9  
    10  const (
    11  	// EncryptionKeySize is the keySize which is used to encrypt data.
    12  	EncryptionKeySize = 32
    13  
    14  	// secretbox nonceSize
    15  	nonceSize = 24
    16  
    17  	// pre-computed minimal length of ciphertext; anything less cannot be valid
    18  	// and will be rejected before attempting any other operations.
    19  	encryptedContentMinSize = 2*(nonceSize+secretbox.Overhead) + EncryptionKeySize
    20  )
    21  
    22  // Box encapsulates the encryption and decryption of files with a given
    23  // Encryption key.
    24  type Box struct {
    25  	privateKey [EncryptionKeySize]byte
    26  }
    27  
    28  // NewBox initializes a Box with the passed encryption key, which can be used
    29  // to encrypt and decrypt data.
    30  func NewBox(privateKey [EncryptionKeySize]byte) *Box {
    31  	return &Box{
    32  		privateKey: privateKey,
    33  	}
    34  }
    35  
    36  // EncryptWithRandomKey takes a piece of data, encrypts it with a random
    37  // key and returns the result, prefixed by the random key encrypted by
    38  // the repository encryption key.
    39  func (b *Box) EncryptWithRandomKey(data []byte) ([]byte, error) {
    40  	// first generate and encrypt the per-file key and append it to
    41  	// the result buffer:
    42  	var nonce1 [nonceSize]byte
    43  	_, err := rand.Read(nonce1[:])
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	var fileKey [32]byte
    49  	_, err = rand.Read(fileKey[:])
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	encryptionKey := b.privateKey
    55  	out := nonce1[:]
    56  	out = secretbox.Seal(out, fileKey[:], &nonce1, &encryptionKey)
    57  
    58  	// then append the actual encrypted contents
    59  	var nonce2 [nonceSize]byte
    60  	_, err = rand.Read(nonce2[:])
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	out = append(out, nonce2[:]...)
    65  	out = secretbox.Seal(out, data, &nonce2, &fileKey)
    66  	return out, nil
    67  }
    68  
    69  // DecryptContent is the counter-part of EncryptWithRandomKey, i.e.
    70  // it returns the plain text again.
    71  func (b *Box) DecryptContent(enc []byte) ([]byte, error) {
    72  	if len(enc) < encryptedContentMinSize {
    73  		return nil, errors.New("truncated ciphertext")
    74  	}
    75  
    76  	// first decrypt the file-specific key using the master key
    77  	var nonce [nonceSize]byte
    78  	readNonce := func() {
    79  		copy(nonce[:], enc[:nonceSize])
    80  		enc = enc[nonceSize:]
    81  	}
    82  	readNonce()
    83  	encryptionKey := b.privateKey
    84  
    85  	l := EncryptionKeySize + secretbox.Overhead
    86  	encryptedFileKey := enc[:l]
    87  	enc = enc[l:]
    88  	var fileKey []byte
    89  	fileKey, success := secretbox.Open(fileKey, encryptedFileKey, &nonce, &encryptionKey)
    90  	if !success {
    91  		return nil, errors.New("file key decryption failed")
    92  	}
    93  
    94  	var arrFileKey [EncryptionKeySize]byte
    95  	copy(arrFileKey[:], fileKey)
    96  
    97  	readNonce()
    98  	var content []byte
    99  	content, success = secretbox.Open(content, enc, &nonce, &arrFileKey)
   100  	if !success {
   101  		return nil, errors.New("content decryption failed")
   102  	}
   103  
   104  	return content, nil
   105  }