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.
11 "golang.org/x/crypto/openpgp/errors"
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.
26 const symmetricallyEncryptedVersion = 1
28 func (se *SymmetricallyEncrypted) parse(r io.Reader) error {
30 // See RFC 4880, section 5.13.
32 _, err := readFull(r, buf[:])
36 if buf[0] != symmetricallyEncryptedVersion {
37 return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
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()
50 return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c)))
52 if len(key) != keySize {
53 return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
57 se.prefix = make([]byte, c.blockSize()+2)
58 _, err := readFull(se.contents, se.prefix)
62 } else if len(se.prefix) != c.blockSize()+2 {
63 return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths")
66 ocfbResync := OCFBResync
68 // MDC packets use a different form of OCFB mode.
69 ocfbResync = OCFBNoResync
72 s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync)
74 return nil, errors.ErrKeyIncorrect
77 plaintext := cipher.StreamReader{S: s, R: se.contents}
80 // MDC packets have an embedded hash that we need to check.
83 return &seMDCReader{in: plaintext, h: h}, nil
86 // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser.
87 return seReader{plaintext}, nil
90 // seReader wraps an io.Reader with a no-op Close method.
91 type seReader struct {
95 func (ser seReader) Read(buf []byte) (int, error) {
96 return ser.in.Read(buf)
99 func (ser seReader) Close() error {
103 const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size
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 {
112 trailer [mdcTrailerSize]byte
113 scratch [mdcTrailerSize]byte
119 func (ser *seMDCReader) Read(buf []byte) (n int, err error) {
121 err = io.ErrUnexpectedEOF
129 // If we haven't yet filled the trailer buffer then we must do that
131 for ser.trailerUsed < mdcTrailerSize {
132 n, err = ser.in.Read(ser.trailer[ser.trailerUsed:])
135 if ser.trailerUsed != mdcTrailerSize {
137 err = io.ErrUnexpectedEOF
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])
158 copy(ser.trailer[:], ser.trailer[n:])
159 copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:])
167 n, err = ser.in.Read(buf[mdcTrailerSize:])
168 copy(buf, ser.trailer[:])
170 copy(ser.trailer[:], buf[n:])
178 // This is a new-format packet tag byte for a type 19 (MDC) packet.
179 const mdcPacketTagByte = byte(0x80) | 0x40 | 19
181 func (ser *seMDCReader) Close() error {
183 return errors.SignatureError("error during reading")
187 // We haven't seen EOF so we need to read to the end
189 _, err := ser.Read(buf[:])
194 return errors.SignatureError("error during reading")
198 if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
199 return errors.SignatureError("MDC packet not found")
201 ser.h.Write(ser.trailer[:2])
203 final := ser.h.Sum(nil)
204 if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
205 return errors.SignatureError("hash mismatch")
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
213 type seMDCWriter struct {
218 func (w *seMDCWriter) Write(buf []byte) (n int, err error) {
220 return w.w.Write(buf)
223 func (w *seMDCWriter) Close() (err error) {
224 var buf [mdcTrailerSize]byte
226 buf[0] = mdcPacketTagByte
229 digest := w.h.Sum(nil)
230 copy(buf[2:], digest)
232 _, err = w.w.Write(buf[:])
239 // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
240 type noOpCloser struct {
244 func (c noOpCloser) Write(data []byte) (n int, err error) {
245 return c.w.Write(data)
248 func (c noOpCloser) Close() error {
252 // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
253 // to w and returns a WriteCloser to which the to-be-encrypted packets can be
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")
260 writeCloser := noOpCloser{w}
261 ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC)
266 _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion})
272 blockSize := block.BlockSize()
273 iv := make([]byte, blockSize)
274 _, err = config.Random().Read(iv)
278 s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
279 _, err = ciphertext.Write(prefix)
283 plaintext := cipher.StreamWriter{S: s, W: ciphertext}
287 h.Write(iv[blockSize-2:])
288 contents = &seMDCWriter{w: plaintext, h: h}