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 }