// Copyright 2009 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. // EAX mode, not a NIST standard (yet). // EAX provides encryption and authentication. // EAX targets the same uses as NIST's CCM mode, // but EAX adds the ability to run in streaming mode. // See // http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf // http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf // What those papers call OMAC is now called CMAC. package block import ( "fmt" "hash" "io" "os" ) // An EAXTagError is returned when the message has failed to authenticate, // because the tag at the end of the message stream (Read) does not match // the tag computed from the message itself (Computed). type EAXTagError struct { Read []byte Computed []byte } func (e *EAXTagError) String() string { return fmt.Sprintf("crypto/block: EAX tag mismatch: read %x but computed %x", e.Read, e.Computed) } func setupEAX(c Cipher, iv, hdr []byte, tagBytes int) (ctrIV, tag []byte, cmac hash.Hash) { n := len(iv) if n != c.BlockSize() { panic(fmt.Sprintln("crypto/block: EAX: iv length", n, "!=", c.BlockSize())) } buf := make([]byte, n) // zeroed // tag = CMAC(0 + iv) ^ CMAC(1 + hdr) ^ CMAC(2 + data) cmac = NewCMAC(c) cmac.Write(buf) // 0 cmac.Write(iv) sum := cmac.Sum() ctrIV = dup(sum) tag = dup(sum[0:tagBytes]) cmac.Reset() buf[n-1] = 1 cmac.Write(buf) // 1 cmac.Write(hdr) sum = cmac.Sum() for i := 0; i < tagBytes; i++ { tag[i] ^= sum[i] } cmac.Reset() buf[n-1] = 2 // 2 cmac.Write(buf) return } func finishEAX(tag []byte, cmac hash.Hash) { // Finish CMAC #2 and xor into tag. sum := cmac.Sum() for i := range tag { tag[i] ^= sum[i] } } // Writer adapter. Tees writes into both w and cmac. // Knows that cmac never returns write errors. type cmacWriter struct { w io.Writer cmac hash.Hash } func (cw *cmacWriter) Write(p []byte) (n int, err os.Error) { n, err = cw.w.Write(p) cw.cmac.Write(p[0:n]) return } // An eaxEncrypter implements the EAX encryption mode. type eaxEncrypter struct { ctr io.Writer // CTR encrypter cw cmacWriter // CTR's output stream tag []byte } // NewEAXEncrypter creates and returns a new EAX encrypter // using the given cipher c, initialization vector iv, associated data hdr, // and tag length tagBytes. The encrypter's Write method encrypts // the data it receives and writes that data to w. // The encrypter's Close method writes a final authenticating tag to w. func NewEAXEncrypter(c Cipher, iv []byte, hdr []byte, tagBytes int, w io.Writer) io.WriteCloser { x := new(eaxEncrypter) // Create new CTR instance writing to both // w for encrypted output and cmac for digesting. x.cw.w = w var ctrIV []byte ctrIV, x.tag, x.cw.cmac = setupEAX(c, iv, hdr, tagBytes) x.ctr = NewCTRWriter(c, ctrIV, &x.cw) return x } func (x *eaxEncrypter) Write(p []byte) (n int, err os.Error) { return x.ctr.Write(p) } func (x *eaxEncrypter) Close() os.Error { x.ctr = nil // crash if Write is called again // Write tag. finishEAX(x.tag, x.cw.cmac) n, err := x.cw.w.Write(x.tag) if n != len(x.tag) && err == nil { err = io.ErrShortWrite } return err } // Reader adapter. Returns data read from r but hangs // on to the last len(tag) bytes for itself (returns EOF len(tag) // bytes early). Also tees all data returned from Read into // the cmac digest. The "don't return the last t bytes" // and the "tee into digest" functionality could be separated, // but the latter half is trivial. type cmacReader struct { r io.Reader cmac hash.Hash tag []byte tmp []byte } func (cr *cmacReader) Read(p []byte) (n int, err os.Error) { // TODO(rsc): Maybe fall back to simpler code if // we recognize the underlying r as a ByteBuffer // or ByteReader. Then we can just take the last piece // off at the start. // First, read a tag-sized chunk. // It's probably not the tag (unless there's no data). tag := cr.tag if len(tag) < cap(tag) { nt := len(tag) nn, err1 := io.ReadFull(cr.r, tag[nt:cap(tag)]) tag = tag[0 : nt+nn] cr.tag = tag if err1 != nil { return 0, err1 } } tagBytes := len(tag) if len(p) > 4*tagBytes { // If p is big, try to read directly into p to avoid a copy. n, err = cr.r.Read(p[tagBytes:]) if n == 0 { goto out } // copy old tag into p for i := 0; i < tagBytes; i++ { p[i] = tag[i] } // copy new tag out of p for i := 0; i < tagBytes; i++ { tag[i] = p[n+i] } goto out } // Otherwise, read into p and then slide data n, err = cr.r.Read(p) if n == 0 { goto out } // copy tag+p into p+tmp and then swap tmp, tag tmp := cr.tmp for i := n + tagBytes - 1; i >= 0; i-- { var c byte if i < tagBytes { c = tag[i] } else { c = p[i-tagBytes] } if i < n { p[i] = c } else { tmp[i] = c } } cr.tmp, cr.tag = tag, tmp out: cr.cmac.Write(p[0:n]) return } type eaxDecrypter struct { ctr io.Reader cr cmacReader tag []byte } // NewEAXDecrypter creates and returns a new EAX decrypter // using the given cipher c, initialization vector iv, associated data hdr, // and tag length tagBytes. The encrypter's Read method decrypts and // returns data read from r. At r's EOF, the encrypter checks the final // authenticating tag and returns an EAXTagError if the tag is invalid. // In that case, the message should be discarded. // Note that the data stream returned from Read cannot be // assumed to be valid, authenticated data until Read returns // 0, nil to signal the end of the data. func NewEAXDecrypter(c Cipher, iv []byte, hdr []byte, tagBytes int, r io.Reader) io.Reader { x := new(eaxDecrypter) x.cr.r = r x.cr.tag = make([]byte, 0, tagBytes) x.cr.tmp = make([]byte, 0, tagBytes) var ctrIV []byte ctrIV, x.tag, x.cr.cmac = setupEAX(c, iv, hdr, tagBytes) x.ctr = NewCTRReader(c, ctrIV, &x.cr) return x } func (x *eaxDecrypter) checkTag() os.Error { x.ctr = nil // crash if Read is called again finishEAX(x.tag, x.cr.cmac) if !same(x.tag, x.cr.tag) { e := new(EAXTagError) e.Computed = dup(x.tag) e.Read = dup(x.cr.tag) return e } return nil } func (x *eaxDecrypter) Read(p []byte) (n int, err os.Error) { n, err = x.ctr.Read(p) if n == 0 && err == nil { err = x.checkTag() } return n, err }