]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / golang.org / x / crypto / openpgp / packet / symmetrically_encrypted.go
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package packet
6
7 import (
8 "crypto/cipher"
9 "crypto/sha1"
10 "crypto/subtle"
11 "golang.org/x/crypto/openpgp/errors"
12 "hash"
13 "io"
14 "strconv"
15 )
16
17 // SymmetricallyEncrypted represents a symmetrically encrypted byte string. The
18 // encrypted contents will consist of more OpenPGP packets. See RFC 4880,
19 // sections 5.7 and 5.13.
20 type SymmetricallyEncrypted struct {
21 MDC bool // true iff this is a type 18 packet and thus has an embedded MAC.
22 contents io.Reader
23 prefix []byte
24 }
25
26 const symmetricallyEncryptedVersion = 1
27
28 func (se *SymmetricallyEncrypted) parse(r io.Reader) error {
29 if se.MDC {
30 // See RFC 4880, section 5.13.
31 var buf [1]byte
32 _, err := readFull(r, buf[:])
33 if err != nil {
34 return err
35 }
36 if buf[0] != symmetricallyEncryptedVersion {
37 return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
38 }
39 }
40 se.contents = r
41 return nil
42 }
43
44 // Decrypt returns a ReadCloser, from which the decrypted contents of the
45 // packet can be read. An incorrect key can, with high probability, be detected
46 // immediately and this will result in a KeyIncorrect error being returned.
47 func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) {
48 keySize := c.KeySize()
49 if keySize == 0 {
50 return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c)))
51 }
52 if len(key) != keySize {
53 return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
54 }
55
56 if se.prefix == nil {
57 se.prefix = make([]byte, c.blockSize()+2)
58 _, err := readFull(se.contents, se.prefix)
59 if err != nil {
60 return nil, err
61 }
62 } else if len(se.prefix) != c.blockSize()+2 {
63 return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths")
64 }
65
66 ocfbResync := OCFBResync
67 if se.MDC {
68 // MDC packets use a different form of OCFB mode.
69 ocfbResync = OCFBNoResync
70 }
71
72 s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync)
73 if s == nil {
74 return nil, errors.ErrKeyIncorrect
75 }
76
77 plaintext := cipher.StreamReader{S: s, R: se.contents}
78
79 if se.MDC {
80 // MDC packets have an embedded hash that we need to check.
81 h := sha1.New()
82 h.Write(se.prefix)
83 return &seMDCReader{in: plaintext, h: h}, nil
84 }
85
86 // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser.
87 return seReader{plaintext}, nil
88 }
89
90 // seReader wraps an io.Reader with a no-op Close method.
91 type seReader struct {
92 in io.Reader
93 }
94
95 func (ser seReader) Read(buf []byte) (int, error) {
96 return ser.in.Read(buf)
97 }
98
99 func (ser seReader) Close() error {
100 return nil
101 }
102
103 const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size
104
105 // An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold
106 // of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an
107 // MDC packet containing a hash of the previous contents which is checked
108 // against the running hash. See RFC 4880, section 5.13.
109 type seMDCReader struct {
110 in io.Reader
111 h hash.Hash
112 trailer [mdcTrailerSize]byte
113 scratch [mdcTrailerSize]byte
114 trailerUsed int
115 error bool
116 eof bool
117 }
118
119 func (ser *seMDCReader) Read(buf []byte) (n int, err error) {
120 if ser.error {
121 err = io.ErrUnexpectedEOF
122 return
123 }
124 if ser.eof {
125 err = io.EOF
126 return
127 }
128
129 // If we haven't yet filled the trailer buffer then we must do that
130 // first.
131 for ser.trailerUsed < mdcTrailerSize {
132 n, err = ser.in.Read(ser.trailer[ser.trailerUsed:])
133 ser.trailerUsed += n
134 if err == io.EOF {
135 if ser.trailerUsed != mdcTrailerSize {
136 n = 0
137 err = io.ErrUnexpectedEOF
138 ser.error = true
139 return
140 }
141 ser.eof = true
142 n = 0
143 return
144 }
145
146 if err != nil {
147 n = 0
148 return
149 }
150 }
151
152 // If it's a short read then we read into a temporary buffer and shift
153 // the data into the caller's buffer.
154 if len(buf) <= mdcTrailerSize {
155 n, err = readFull(ser.in, ser.scratch[:len(buf)])
156 copy(buf, ser.trailer[:n])
157 ser.h.Write(buf[:n])
158 copy(ser.trailer[:], ser.trailer[n:])
159 copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:])
160 if n < len(buf) {
161 ser.eof = true
162 err = io.EOF
163 }
164 return
165 }
166
167 n, err = ser.in.Read(buf[mdcTrailerSize:])
168 copy(buf, ser.trailer[:])
169 ser.h.Write(buf[:n])
170 copy(ser.trailer[:], buf[n:])
171
172 if err == io.EOF {
173 ser.eof = true
174 }
175 return
176 }
177
178 // This is a new-format packet tag byte for a type 19 (MDC) packet.
179 const mdcPacketTagByte = byte(0x80) | 0x40 | 19
180
181 func (ser *seMDCReader) Close() error {
182 if ser.error {
183 return errors.SignatureError("error during reading")
184 }
185
186 for !ser.eof {
187 // We haven't seen EOF so we need to read to the end
188 var buf [1024]byte
189 _, err := ser.Read(buf[:])
190 if err == io.EOF {
191 break
192 }
193 if err != nil {
194 return errors.SignatureError("error during reading")
195 }
196 }
197
198 if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
199 return errors.SignatureError("MDC packet not found")
200 }
201 ser.h.Write(ser.trailer[:2])
202
203 final := ser.h.Sum(nil)
204 if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
205 return errors.SignatureError("hash mismatch")
206 }
207 return nil
208 }
209
210 // An seMDCWriter writes through to an io.WriteCloser while maintains a running
211 // hash of the data written. On close, it emits an MDC packet containing the
212 // running hash.
213 type seMDCWriter struct {
214 w io.WriteCloser
215 h hash.Hash
216 }
217
218 func (w *seMDCWriter) Write(buf []byte) (n int, err error) {
219 w.h.Write(buf)
220 return w.w.Write(buf)
221 }
222
223 func (w *seMDCWriter) Close() (err error) {
224 var buf [mdcTrailerSize]byte
225
226 buf[0] = mdcPacketTagByte
227 buf[1] = sha1.Size
228 w.h.Write(buf[:2])
229 digest := w.h.Sum(nil)
230 copy(buf[2:], digest)
231
232 _, err = w.w.Write(buf[:])
233 if err != nil {
234 return
235 }
236 return w.w.Close()
237 }
238
239 // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
240 type noOpCloser struct {
241 w io.Writer
242 }
243
244 func (c noOpCloser) Write(data []byte) (n int, err error) {
245 return c.w.Write(data)
246 }
247
248 func (c noOpCloser) Close() error {
249 return nil
250 }
251
252 // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
253 // to w and returns a WriteCloser to which the to-be-encrypted packets can be
254 // written.
255 // If config is nil, sensible defaults will be used.
256 func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) {
257 if c.KeySize() != len(key) {
258 return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length")
259 }
260 writeCloser := noOpCloser{w}
261 ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC)
262 if err != nil {
263 return
264 }
265
266 _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion})
267 if err != nil {
268 return
269 }
270
271 block := c.new(key)
272 blockSize := block.BlockSize()
273 iv := make([]byte, blockSize)
274 _, err = config.Random().Read(iv)
275 if err != nil {
276 return
277 }
278 s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
279 _, err = ciphertext.Write(prefix)
280 if err != nil {
281 return
282 }
283 plaintext := cipher.StreamWriter{S: s, W: ciphertext}
284
285 h := sha1.New()
286 h.Write(iv)
287 h.Write(iv[blockSize-2:])
288 contents = &seMDCWriter{w: plaintext, h: h}
289 return
290 }