progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / symm / ccm.c
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_check@ --- *
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 -------------------------------------------------*/