| // 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 = copy(sum) | |
| tag = copy(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 = copy(x.tag) | |
| e.Read = copy(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 | |
| } |