age

Simple, secure encryption with UNIX-style composability.
git clone git://git.sgregoratto.me/age
Log | Files | Refs | README | LICENSE

age.go (2937B)


      1 // Copyright 2019 Google LLC
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file or at
      5 // https://developers.google.com/open-source/licenses/bsd
      6 
      7 package age
      8 
      9 import (
     10 	"crypto/hmac"
     11 	"crypto/rand"
     12 	"errors"
     13 	"fmt"
     14 	"io"
     15 
     16 	"github.com/FiloSottile/age/internal/format"
     17 	"github.com/FiloSottile/age/internal/stream"
     18 )
     19 
     20 type Identity interface {
     21 	Type() string
     22 	Unwrap(block *format.Recipient) (fileKey []byte, err error)
     23 }
     24 
     25 type Recipient interface {
     26 	Type() string
     27 	Wrap(fileKey []byte) (*format.Recipient, error)
     28 }
     29 
     30 func Encrypt(dst io.Writer, recipients ...Recipient) (io.WriteCloser, error) {
     31 	if len(recipients) == 0 {
     32 		return nil, errors.New("no recipients specified")
     33 	}
     34 
     35 	fileKey := make([]byte, 16)
     36 	if _, err := rand.Read(fileKey); err != nil {
     37 		return nil, err
     38 	}
     39 
     40 	hdr := &format.Header{}
     41 	for i, r := range recipients {
     42 		if r.Type() == "scrypt" && len(recipients) != 1 {
     43 			return nil, errors.New("an scrypt recipient must be the only one")
     44 		}
     45 
     46 		block, err := r.Wrap(fileKey)
     47 		if err != nil {
     48 			return nil, fmt.Errorf("failed to wrap key for recipient #%d: %v", i, err)
     49 		}
     50 		hdr.Recipients = append(hdr.Recipients, block)
     51 	}
     52 	if mac, err := headerMAC(fileKey, hdr); err != nil {
     53 		return nil, fmt.Errorf("failed to compute header MAC: %v", err)
     54 	} else {
     55 		hdr.MAC = mac
     56 	}
     57 	if err := hdr.Marshal(dst); err != nil {
     58 		return nil, fmt.Errorf("failed to write header: %v", err)
     59 	}
     60 
     61 	nonce := make([]byte, 16)
     62 	if _, err := rand.Read(nonce); err != nil {
     63 		return nil, err
     64 	}
     65 	if _, err := dst.Write(nonce); err != nil {
     66 		return nil, fmt.Errorf("failed to write nonce: %v", err)
     67 	}
     68 
     69 	return stream.NewWriter(streamKey(fileKey, nonce), dst)
     70 }
     71 
     72 func Decrypt(src io.Reader, identities ...Identity) (io.Reader, error) {
     73 	if len(identities) == 0 {
     74 		return nil, errors.New("no identities specified")
     75 	}
     76 
     77 	hdr, payload, err := format.Parse(src)
     78 	if err != nil {
     79 		return nil, fmt.Errorf("failed to read header: %v", err)
     80 	}
     81 	if len(hdr.Recipients) > 20 {
     82 		return nil, errors.New("too many recipients")
     83 	}
     84 
     85 	var fileKey []byte
     86 RecipientsLoop:
     87 	for _, r := range hdr.Recipients {
     88 		if r.Type == "scrypt" && len(hdr.Recipients) != 1 {
     89 			return nil, errors.New("an scrypt recipient must be the only one")
     90 		}
     91 		for _, i := range identities {
     92 
     93 			if i.Type() != r.Type {
     94 				continue
     95 			}
     96 
     97 			fileKey, err = i.Unwrap(r)
     98 			if err == nil {
     99 				break RecipientsLoop
    100 			}
    101 		}
    102 	}
    103 	if fileKey == nil {
    104 		return nil, errors.New("no identity matched a recipient")
    105 	}
    106 
    107 	if mac, err := headerMAC(fileKey, hdr); err != nil {
    108 		return nil, fmt.Errorf("failed to compute header MAC: %v", err)
    109 	} else if !hmac.Equal(mac, hdr.MAC) {
    110 		return nil, errors.New("bad header MAC")
    111 	}
    112 
    113 	nonce := make([]byte, 16)
    114 	if _, err := io.ReadFull(payload, nonce); err != nil {
    115 		return nil, fmt.Errorf("failed to read nonce: %v", err)
    116 	}
    117 
    118 	return stream.NewReader(streamKey(fileKey, nonce), payload)
    119 }