123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package packet
- import (
- "crypto/cipher"
- "crypto/sha1"
- "crypto/subtle"
- "golang.org/x/crypto/openpgp/errors"
- "hash"
- "io"
- "strconv"
- )
- // SymmetricallyEncrypted represents a symmetrically encrypted byte string. The
- // encrypted contents will consist of more OpenPGP packets. See RFC 4880,
- // sections 5.7 and 5.13.
- type SymmetricallyEncrypted struct {
- MDC bool // true iff this is a type 18 packet and thus has an embedded MAC.
- contents io.Reader
- prefix []byte
- }
- const symmetricallyEncryptedVersion = 1
- func (se *SymmetricallyEncrypted) parse(r io.Reader) error {
- if se.MDC {
- // See RFC 4880, section 5.13.
- var buf [1]byte
- _, err := readFull(r, buf[:])
- if err != nil {
- return err
- }
- if buf[0] != symmetricallyEncryptedVersion {
- return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
- }
- }
- se.contents = r
- return nil
- }
- // Decrypt returns a ReadCloser, from which the decrypted contents of the
- // packet can be read. An incorrect key can, with high probability, be detected
- // immediately and this will result in a KeyIncorrect error being returned.
- func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) {
- keySize := c.KeySize()
- if keySize == 0 {
- return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c)))
- }
- if len(key) != keySize {
- return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
- }
- if se.prefix == nil {
- se.prefix = make([]byte, c.blockSize()+2)
- _, err := readFull(se.contents, se.prefix)
- if err != nil {
- return nil, err
- }
- } else if len(se.prefix) != c.blockSize()+2 {
- return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths")
- }
- ocfbResync := OCFBResync
- if se.MDC {
- // MDC packets use a different form of OCFB mode.
- ocfbResync = OCFBNoResync
- }
- s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync)
- if s == nil {
- return nil, errors.ErrKeyIncorrect
- }
- plaintext := cipher.StreamReader{S: s, R: se.contents}
- if se.MDC {
- // MDC packets have an embedded hash that we need to check.
- h := sha1.New()
- h.Write(se.prefix)
- return &seMDCReader{in: plaintext, h: h}, nil
- }
- // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser.
- return seReader{plaintext}, nil
- }
- // seReader wraps an io.Reader with a no-op Close method.
- type seReader struct {
- in io.Reader
- }
- func (ser seReader) Read(buf []byte) (int, error) {
- return ser.in.Read(buf)
- }
- func (ser seReader) Close() error {
- return nil
- }
- const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size
- // An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold
- // of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an
- // MDC packet containing a hash of the previous contents which is checked
- // against the running hash. See RFC 4880, section 5.13.
- type seMDCReader struct {
- in io.Reader
- h hash.Hash
- trailer [mdcTrailerSize]byte
- scratch [mdcTrailerSize]byte
- trailerUsed int
- error bool
- eof bool
- }
- func (ser *seMDCReader) Read(buf []byte) (n int, err error) {
- if ser.error {
- err = io.ErrUnexpectedEOF
- return
- }
- if ser.eof {
- err = io.EOF
- return
- }
- // If we haven't yet filled the trailer buffer then we must do that
- // first.
- for ser.trailerUsed < mdcTrailerSize {
- n, err = ser.in.Read(ser.trailer[ser.trailerUsed:])
- ser.trailerUsed += n
- if err == io.EOF {
- if ser.trailerUsed != mdcTrailerSize {
- n = 0
- err = io.ErrUnexpectedEOF
- ser.error = true
- return
- }
- ser.eof = true
- n = 0
- return
- }
- if err != nil {
- n = 0
- return
- }
- }
- // If it's a short read then we read into a temporary buffer and shift
- // the data into the caller's buffer.
- if len(buf) <= mdcTrailerSize {
- n, err = readFull(ser.in, ser.scratch[:len(buf)])
- copy(buf, ser.trailer[:n])
- ser.h.Write(buf[:n])
- copy(ser.trailer[:], ser.trailer[n:])
- copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:])
- if n < len(buf) {
- ser.eof = true
- err = io.EOF
- }
- return
- }
- n, err = ser.in.Read(buf[mdcTrailerSize:])
- copy(buf, ser.trailer[:])
- ser.h.Write(buf[:n])
- copy(ser.trailer[:], buf[n:])
- if err == io.EOF {
- ser.eof = true
- }
- return
- }
- // This is a new-format packet tag byte for a type 19 (MDC) packet.
- const mdcPacketTagByte = byte(0x80) | 0x40 | 19
- func (ser *seMDCReader) Close() error {
- if ser.error {
- return errors.SignatureError("error during reading")
- }
- for !ser.eof {
- // We haven't seen EOF so we need to read to the end
- var buf [1024]byte
- _, err := ser.Read(buf[:])
- if err == io.EOF {
- break
- }
- if err != nil {
- return errors.SignatureError("error during reading")
- }
- }
- if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
- return errors.SignatureError("MDC packet not found")
- }
- ser.h.Write(ser.trailer[:2])
- final := ser.h.Sum(nil)
- if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
- return errors.SignatureError("hash mismatch")
- }
- return nil
- }
- // An seMDCWriter writes through to an io.WriteCloser while maintains a running
- // hash of the data written. On close, it emits an MDC packet containing the
- // running hash.
- type seMDCWriter struct {
- w io.WriteCloser
- h hash.Hash
- }
- func (w *seMDCWriter) Write(buf []byte) (n int, err error) {
- w.h.Write(buf)
- return w.w.Write(buf)
- }
- func (w *seMDCWriter) Close() (err error) {
- var buf [mdcTrailerSize]byte
- buf[0] = mdcPacketTagByte
- buf[1] = sha1.Size
- w.h.Write(buf[:2])
- digest := w.h.Sum(nil)
- copy(buf[2:], digest)
- _, err = w.w.Write(buf[:])
- if err != nil {
- return
- }
- return w.w.Close()
- }
- // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
- type noOpCloser struct {
- w io.Writer
- }
- func (c noOpCloser) Write(data []byte) (n int, err error) {
- return c.w.Write(data)
- }
- func (c noOpCloser) Close() error {
- return nil
- }
- // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
- // to w and returns a WriteCloser to which the to-be-encrypted packets can be
- // written.
- // If config is nil, sensible defaults will be used.
- func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) {
- if c.KeySize() != len(key) {
- return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length")
- }
- writeCloser := noOpCloser{w}
- ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC)
- if err != nil {
- return
- }
- _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion})
- if err != nil {
- return
- }
- block := c.new(key)
- blockSize := block.BlockSize()
- iv := make([]byte, blockSize)
- _, err = config.Random().Read(iv)
- if err != nil {
- return
- }
- s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
- _, err = ciphertext.Write(prefix)
- if err != nil {
- return
- }
- plaintext := cipher.StreamWriter{S: s, W: ciphertext}
- h := sha1.New()
- h.Write(iv)
- h.Write(iv[blockSize-2:])
- contents = &seMDCWriter{w: plaintext, h: h}
- return
- }
|