| // 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"; |
| "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 Digest) { |
| n := len(iv); |
| if n != c.BlockSize() { |
| panicln("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 Digest) { |
| // 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 Digest; |
| } |
| |
| 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 Digest; |
| 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:len(p)]); |
| 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; |
| } |
| |