summaryrefslogtreecommitdiff
path: root/src/pkg/crypto/block/eax.go
blob: 3f3b96431e70140c477caca8ac40a0735306b971 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// 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
}