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 (
	
	
	
	
	
	
	
)
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 ( *SymmetricallyEncrypted) ( io.Reader) error {
See RFC 4880, section 5.13.
		var  [1]byte
		,  := readFull(, [:])
		if  != nil {
			return 
		}
		if [0] != symmetricallyEncryptedVersion {
			return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
		}
	}
	.contents = 
	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 ( *SymmetricallyEncrypted) ( CipherFunction,  []byte) (io.ReadCloser, error) {
	 := .KeySize()
	if  == 0 {
		return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int()))
	}
	if len() !=  {
		return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
	}

	if .prefix == nil {
		.prefix = make([]byte, .blockSize()+2)
		,  := readFull(.contents, .prefix)
		if  != nil {
			return nil, 
		}
	} else if len(.prefix) != .blockSize()+2 {
		return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths")
	}

	 := OCFBResync
MDC packets use a different form of OCFB mode.
		 = OCFBNoResync
	}

	 := NewOCFBDecrypter(.new(), .prefix, )
	if  == nil {
		return nil, errors.ErrKeyIncorrect
	}

	 := cipher.StreamReader{S: , R: .contents}

MDC packets have an embedded hash that we need to check.
		 := sha1.New()
		.Write(.prefix)
		return &seMDCReader{in: , h: }, nil
	}
Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser.
	return seReader{}, nil
}
seReader wraps an io.Reader with a no-op Close method.
type seReader struct {
	in io.Reader
}

func ( seReader) ( []byte) (int, error) {
	return .in.Read()
}

func ( seReader) () 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 ( *seMDCReader) ( []byte) ( int,  error) {
	if .error {
		 = io.ErrUnexpectedEOF
		return
	}
	if .eof {
		 = io.EOF
		return
	}
If we haven't yet filled the trailer buffer then we must do that first.
	for .trailerUsed < mdcTrailerSize {
		,  = .in.Read(.trailer[.trailerUsed:])
		.trailerUsed += 
		if  == io.EOF {
			if .trailerUsed != mdcTrailerSize {
				 = 0
				 = io.ErrUnexpectedEOF
				.error = true
				return
			}
			.eof = true
			 = 0
			return
		}

		if  != nil {
			 = 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() <= mdcTrailerSize {
		,  = readFull(.in, .scratch[:len()])
		copy(, .trailer[:])
		.h.Write([:])
		copy(.trailer[:], .trailer[:])
		copy(.trailer[mdcTrailerSize-:], .scratch[:])
		if  < len() {
			.eof = true
			 = io.EOF
		}
		return
	}

	,  = .in.Read([mdcTrailerSize:])
	copy(, .trailer[:])
	.h.Write([:])
	copy(.trailer[:], [:])

	if  == io.EOF {
		.eof = true
	}
	return
}
This is a new-format packet tag byte for a type 19 (MDC) packet.
const mdcPacketTagByte = byte(0x80) | 0x40 | 19

func ( *seMDCReader) () error {
	if .error {
		return errors.SignatureError("error during reading")
	}

We haven't seen EOF so we need to read to the end
		var  [1024]byte
		,  := .Read([:])
		if  == io.EOF {
			break
		}
		if  != nil {
			return errors.SignatureError("error during reading")
		}
	}

	if .trailer[0] != mdcPacketTagByte || .trailer[1] != sha1.Size {
		return errors.SignatureError("MDC packet not found")
	}
	.h.Write(.trailer[:2])

	 := .h.Sum(nil)
	if subtle.ConstantTimeCompare(, .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 ( *seMDCWriter) ( []byte) ( int,  error) {
	.h.Write()
	return .w.Write()
}

func ( *seMDCWriter) () ( error) {
	var  [mdcTrailerSize]byte

	[0] = mdcPacketTagByte
	[1] = sha1.Size
	.h.Write([:2])
	 := .h.Sum(nil)
	copy([2:], )

	_,  = .w.Write([:])
	if  != nil {
		return
	}
	return .w.Close()
}
noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
type noOpCloser struct {
	w io.Writer
}

func ( noOpCloser) ( []byte) ( int,  error) {
	return .w.Write()
}

func ( noOpCloser) () 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 ( io.Writer,  CipherFunction,  []byte,  *Config) ( io.WriteCloser,  error) {
	if .KeySize() != len() {
		return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length")
	}
	 := noOpCloser{}
	,  := serializeStreamHeader(, packetTypeSymmetricallyEncryptedMDC)
	if  != nil {
		return
	}

	_,  = .Write([]byte{symmetricallyEncryptedVersion})
	if  != nil {
		return
	}

	 := .new()
	 := .BlockSize()
	 := make([]byte, )
	_,  = .Random().Read()
	if  != nil {
		return
	}
	,  := NewOCFBEncrypter(, , OCFBNoResync)
	_,  = .Write()
	if  != nil {
		return
	}
	 := cipher.StreamWriter{S: , W: }

	 := sha1.New()
	.Write()
	.Write([-2:])
	 = &seMDCWriter{w: , h: }
	return