1
2
3
4
5
6
7
8 package eax
9
10 import (
11 "crypto/cipher"
12 "crypto/subtle"
13 "errors"
14 "github.com/ProtonMail/go-crypto/internal/byteutil"
15 )
16
17 const (
18 defaultTagSize = 16
19 defaultNonceSize = 16
20 )
21
22 type eax struct {
23 block cipher.Block
24 tagSize int
25 nonceSize int
26 }
27
28 func (e *eax) NonceSize() int {
29 return e.nonceSize
30 }
31
32 func (e *eax) Overhead() int {
33 return e.tagSize
34 }
35
36
37
38 func NewEAX(block cipher.Block) (cipher.AEAD, error) {
39 return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
40 }
41
42
43
44
45
46
47
48
49
50
51 func NewEAXWithNonceAndTagSize(
52 block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
53 if nonceSize < 1 {
54 return nil, eaxError("Cannot initialize EAX with nonceSize = 0")
55 }
56 if tagSize > block.BlockSize() {
57 return nil, eaxError("Custom tag length exceeds blocksize")
58 }
59 return &eax{
60 block: block,
61 tagSize: tagSize,
62 nonceSize: nonceSize,
63 }, nil
64 }
65
66 func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte {
67 if len(nonce) > e.nonceSize {
68 panic("crypto/eax: Nonce too long for this instance")
69 }
70 ret, out := byteutil.SliceForAppend(dst, len(plaintext)+e.tagSize)
71 omacNonce := e.omacT(0, nonce)
72 omacAdata := e.omacT(1, adata)
73
74
75 ctr := cipher.NewCTR(e.block, omacNonce)
76 ciphertextData := out[:len(plaintext)]
77 ctr.XORKeyStream(ciphertextData, plaintext)
78
79 omacCiphertext := e.omacT(2, ciphertextData)
80
81 tag := out[len(plaintext):]
82 for i := 0; i < e.tagSize; i++ {
83 tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
84 }
85 return ret
86 }
87
88 func (e *eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
89 if len(nonce) > e.nonceSize {
90 panic("crypto/eax: Nonce too long for this instance")
91 }
92 if len(ciphertext) < e.tagSize {
93 return nil, eaxError("Ciphertext shorter than tag length")
94 }
95 sep := len(ciphertext) - e.tagSize
96
97
98 omacNonce := e.omacT(0, nonce)
99 omacAdata := e.omacT(1, adata)
100 omacCiphertext := e.omacT(2, ciphertext[:sep])
101
102 tag := make([]byte, e.tagSize)
103 for i := 0; i < e.tagSize; i++ {
104 tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
105 }
106
107
108 if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 {
109 return nil, eaxError("Tag authentication failed")
110 }
111
112
113 ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
114 ctr := cipher.NewCTR(e.block, omacNonce)
115 ctr.XORKeyStream(out, ciphertext[:sep])
116
117 return ret[:sep], nil
118 }
119
120
121 func (e *eax) omacT(t byte, plaintext []byte) []byte {
122 blockSize := e.block.BlockSize()
123 byteT := make([]byte, blockSize)
124 byteT[blockSize-1] = t
125 concat := append(byteT, plaintext...)
126 return e.omac(concat)
127 }
128
129 func (e *eax) omac(plaintext []byte) []byte {
130 blockSize := e.block.BlockSize()
131
132 L := make([]byte, blockSize)
133 e.block.Encrypt(L, L)
134 B := byteutil.GfnDouble(L)
135 P := byteutil.GfnDouble(B)
136
137
138 cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize))
139 padded := e.pad(plaintext, B, P)
140 cbcCiphertext := make([]byte, len(padded))
141 cbc.CryptBlocks(cbcCiphertext, padded)
142
143 return cbcCiphertext[len(cbcCiphertext)-blockSize:]
144 }
145
146 func (e *eax) pad(plaintext, B, P []byte) []byte {
147
148 blockSize := e.block.BlockSize()
149 if len(plaintext) != 0 && len(plaintext)%blockSize == 0 {
150 return byteutil.RightXor(plaintext, B)
151 }
152
153
154 ending := make([]byte, blockSize-len(plaintext)%blockSize)
155 ending[0] = 0x80
156 padded := append(plaintext, ending...)
157 return byteutil.RightXor(padded, P)
158 }
159
160 func eaxError(err string) error {
161 return errors.New("crypto/eax: " + err)
162 }
163
View as plain text