Commit | Line | Data |
---|---|---|
55b6b722 MW |
1 | /* -*-c-*- |
2 | * | |
3 | * CCM common code | |
4 | * | |
5 | * (c) 2018 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Catacomb. | |
11 | * | |
12 | * Catacomb is free software: you can redistribute it and/or modify it | |
13 | * under the terms of the GNU Library General Public License as published | |
14 | * by the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * Catacomb is distributed in the hope that it will be useful, but | |
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 | * Library General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU Library General Public | |
23 | * License along with Catacomb. If not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | /*----- Header files ------------------------------------------------------*/ | |
29 | ||
30 | #include "ccm.h" | |
31 | #include "ccm-def.h" | |
32 | ||
33 | /*----- MAC header and counter formatting ---------------------------------* | |
34 | * | |
35 | * CCM's most endearing feature is its complex formatting of the MAC | |
36 | * initialization vector and counter blocks. This is only specified for | |
37 | * 128-bit blocks, so I've made up formats for 64-, 192-, and 256-bit blocks. | |
38 | * For completeness and ease of comparison, all of the encodings are defined | |
39 | * here. Encoding of the rest of the MAC input data is common. | |
40 | * | |
41 | * The notation is taken from the definition of CCM in NIST SP800-38C. | |
42 | * | |
43 | * * [x]_w = encoding of x, as w bits | |
44 | * * P = plaintext/ciphertext | |
45 | * * p = length of P in octets | |
46 | * * Q = [p]_{8q} = encoding of p | |
47 | * * q = length of Q in octets | |
48 | * * N = nonce | |
49 | * * H = B_0 = MAC header block | |
50 | * * C_i = the i-th counter block | |
51 | * * t = length of tag in octets | |
52 | * | |
53 | * 128-bit blocks (SP800-38C, appendix A): | |
54 | * | |
55 | * Let F be an octet: | |
56 | * | |
57 | * Bits 7 6 6:3 3:0 | |
58 | * Meaning 0 AAD? [t/2 - 1]_3 [q - 1]_3 | |
59 | * | |
60 | * Then H = F || N || Q and C_i = 0^5 || [q - 1]_3 || N || [i]_{8q}. | |
61 | * | |
62 | * 64-bit blocks (new): | |
63 | * | |
64 | * Let F be an octet: | |
65 | * | |
66 | * Bits 7 6 6:3 3:0 | |
67 | * Meaning 0 AAD? [t - 1]_3 [q - 1]_3 | |
68 | * | |
69 | * Then H = F || N || Q and C_i = 0^5 || [q - 1]_3 || N || [i]_{8q}. | |
70 | * | |
71 | * (i.e., almost exactly the same, except that the tag no longer needs to be | |
72 | * an even number of octets). | |
73 | * | |
74 | * n-bit blocks, for n > 128 | |
75 | * | |
76 | * Let F be a 16-bit flags word: | |
77 | * | |
78 | * Bits 15 15:8 7 7:0 | |
79 | * Meaning 0 [t]_7 AAD? [q]_7 | |
80 | * | |
81 | * Then H = F || N || Q and C_i = 0^9 || [q - 1]_7 || N || [i]_{8q}. | |
82 | */ | |
83 | ||
84 | /* --- @ccm_paramsok@ --- * | |
85 | * | |
86 | * Arguments: @const ccm_params *p@ = pointer to parameters | |
87 | * | |
88 | * Returns: True (nonzero) if the parameters are OK; false (zero) if | |
89 | * there's a problem. | |
90 | * | |
91 | * Use: Verify that the CCM parameters are acceptable. | |
92 | */ | |
93 | ||
94 | int ccm_check(const ccm_params *p) | |
95 | { | |
96 | unsigned fsz = p->bsz <= 16 ? 1 : 2, q = p->bsz - p->nsz - fsz, i; | |
97 | size_t msz; | |
98 | ||
99 | /* Check that the block size hasn't been bungled. */ | |
100 | if (p->bsz < 16 && p->bsz != 8) return (0); | |
101 | ||
102 | /* The length-of-the-length must be representable, and its encoding must | |
103 | * not be zero, since this is `reserved'. The small-block encoding stores | |
104 | * %$q - 1$%, so we must have %$q \ge 2$%; the large-block encoding stores | |
105 | * %$q$% directly, so only %$q = 0$% is forbidden. | |
106 | */ | |
107 | if (p->nsz > p->bsz - fsz - (p->bsz <= 16 ? 2 : 1)) return (0); | |
108 | if (q > (p->bsz <= 16 ? 8 : 127)) return (0); | |
109 | ||
110 | /* Verify that the message length will fit in the space allotted. */ | |
111 | for (i = 1, msz = p->msz >> 8; msz; i++, msz >>= 8); | |
112 | if (i > q) return (0); | |
113 | ||
114 | /* The tag can't be larger than the block size. Also, it must be | |
115 | * representable, and its encoding must not be zero, because otherwise it'd | |
116 | * be possible for a MAC header block to look like a counter block. The | |
117 | * tag encoding is fiddly: for 64-bit blocks, we store %$t - 1$%, so we | |
118 | * must have %$t \ge 2$%; for 128-bit blocks, we store %$t/2 - 2$%, so | |
119 | * we must have %$t \ge 4$% with %$t$% even; otherwise, we store %$t$% | |
120 | * directly, so the only requirement is that %$t \ge 1$%. | |
121 | */ | |
122 | if (p->tsz > p->bsz) return (0); | |
123 | if (p->tsz < (p->bsz == 8 ? 2 : p->bsz == 16 ? 4 : 1)) return (0); | |
124 | if (p->bsz == 16 && p->tsz%2 != 0) return (0); | |
125 | ||
126 | /* All looks good. */ | |
127 | return (1); | |
128 | } | |
129 | ||
130 | /* --- @ccm_fmthdr@ --- * | |
131 | * | |
132 | * Arguments: @const ccm_params *p@ = pointer to parameters | |
133 | * @octet *b@ = block-size buffer to write header | |
134 | * @const void *n@ = pointer to nonce | |
135 | * | |
136 | * Returns: --- | |
137 | * | |
138 | * Use: Format a MAC header block. | |
139 | */ | |
140 | ||
141 | void ccm_fmthdr(const ccm_params *p, octet *b, const void *n) | |
142 | { | |
143 | size_t fsz = p->bsz <= 16 ? 1 : 2, q = p->bsz - p->nsz - fsz; | |
144 | unsigned f0 = 0, f1 = 0; | |
145 | size_t i, t; | |
146 | ||
147 | /* Encode whether the AAD is empty. */ | |
148 | if (!p->hsz) /* do nothing */; | |
149 | else if (p->bsz <= 16) f0 |= 0x40; | |
150 | else f1 |= 0x80; | |
151 | ||
152 | /* Encode the tag size. */ | |
153 | switch (p->bsz) { | |
154 | case 8: f0 |= (p->tsz - 1) << 3; break; | |
155 | case 16: f0 |= (p->tsz - 2) << 2; break; | |
156 | default: f0 |= p->tsz; break; | |
157 | } | |
158 | ||
159 | /* Encode the length-of-the-length. (This is the most bletcherous part of | |
160 | * CCM.) | |
161 | */ | |
162 | if (p->bsz <= 16) f0 |= q - 1; | |
163 | else f1 |= q; | |
164 | ||
165 | /* Insert the flags and nonce. */ | |
166 | b[0] = f0; memcpy(b + fsz, n, p->nsz); | |
167 | if (p->bsz > 16) b[1] = f1; | |
168 | ||
169 | /* Write the message length. */ | |
170 | for (i = 0, t = p->msz; i < q; i++, t >>= 8) b[p->bsz - i - 1] = U8(t); | |
171 | } | |
172 | ||
173 | /* --- @ccm_fmtctr@ --- * | |
174 | * | |
175 | * Arguments: @const ccm_params *p@ = pointer to parameters | |
176 | * @octet *b@ = block-size buffer to write header | |
177 | * @const void *n@ = pointer to nonce | |
178 | * | |
179 | * Returns: --- | |
180 | * | |
181 | * Use: Format an initial counter block. | |
182 | */ | |
183 | ||
184 | void ccm_fmtctr(const ccm_params *p, octet *b, const void *n) | |
185 | { | |
186 | size_t fsz = p->bsz <= 16 ? 1 : 2, q = p->bsz - p->nsz - fsz; | |
187 | unsigned f0 = 0, f1 = 0; | |
188 | ||
189 | /* Encode the message length length. (Did I complain about this?) */ | |
190 | if (p->bsz <= 16) f0 |= q - 1; | |
191 | else f1 |= q; | |
192 | ||
193 | /* Insert the flags and nonce. */ | |
194 | b[0] = f0; memcpy(b + fsz, n, p->nsz); | |
195 | if (p->bsz > 16) b[1] = f1; | |
196 | ||
197 | /* Zero out the initial counter. */ | |
198 | memset(b + fsz + p->nsz, 0, q); | |
199 | } | |
200 | ||
201 | /*----- That's all, folks -------------------------------------------------*/ |