1 /*
2 * cifra - embedded cryptography library
3 * Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
4 *
5 * To the extent possible under law, the author(s) have dedicated all
6 * copyright and related and neighboring rights to this software to the
7 * public domain worldwide. This software is distributed without any
8 * warranty.
9 *
10 * You should have received a copy of the CC0 Public Domain Dedication
11 * along with this software. If not, see
12 * <http://creativecommons.org/publicdomain/zero/1.0/>.
13 */
14
15 #include "handy.h"
16 #include "prp.h"
17 #include "modes.h"
18 #include "tassert.h"
19
20 #include <string.h>
21
22 #define CCM_ADATA_PRESENT 0x40
23
write_be(uint8_t * out,size_t value,size_t bytes)24 static void write_be(uint8_t *out, size_t value, size_t bytes)
25 {
26 while (bytes)
27 {
28 out[bytes - 1] = value & 0xff;
29 value >>= 8;
30 bytes--;
31 }
32
33 assert(value == 0); /* or we couldn't encode the value. */
34 }
35
zero_pad(cf_cbcmac_stream * cm)36 static void zero_pad(cf_cbcmac_stream *cm)
37 {
38 cf_cbcmac_stream_finish_block_zero(cm);
39 }
40
41 /* nb. block is general workspace. */
add_aad(cf_cbcmac_stream * cm,uint8_t block[CF_MAXBLOCK],const uint8_t * header,size_t nheader)42 static void add_aad(cf_cbcmac_stream *cm, uint8_t block[CF_MAXBLOCK],
43 const uint8_t *header, size_t nheader)
44 {
45 assert(nheader <= 0xffffffff); /* we don't support 64 bit lengths. */
46
47 /* Add length using stupidly complicated rules. */
48 if (nheader < 0xff00)
49 {
50 write_be(block, nheader, 2);
51 cf_cbcmac_stream_update(cm, block, 2);
52 } else {
53 write_be(block, 0xfffe, 2);
54 write_be(block + 2, nheader, 4);
55 cf_cbcmac_stream_update(cm, block, 6);
56 }
57
58 cf_cbcmac_stream_update(cm, header, nheader);
59 zero_pad(cm);
60 }
61
add_block0(cf_cbcmac_stream * cm,uint8_t block[CF_MAXBLOCK],size_t nblock,const uint8_t * nonce,size_t nnonce,size_t L,size_t nplain,size_t nheader,size_t ntag)62 static void add_block0(cf_cbcmac_stream *cm,
63 uint8_t block[CF_MAXBLOCK], size_t nblock,
64 const uint8_t *nonce, size_t nnonce,
65 size_t L, size_t nplain,
66 size_t nheader, size_t ntag)
67 {
68 /* Construct first block B_0. */
69 block[0] = ((nheader == 0) ? 0x00 : CCM_ADATA_PRESENT) |
70 ((ntag - 2) / 2) << 3 |
71 (L - 1);
72 memcpy(block + 1, nonce, nnonce);
73 write_be(block + 1 + nnonce, nplain, L);
74
75 cf_cbcmac_stream_update(cm, block, nblock);
76 }
77
build_ctr_nonce(uint8_t ctr_nonce[CF_MAXBLOCK],size_t L,const uint8_t * nonce,size_t nnonce)78 static void build_ctr_nonce(uint8_t ctr_nonce[CF_MAXBLOCK],
79 size_t L,
80 const uint8_t *nonce, size_t nnonce)
81 {
82 ctr_nonce[0] = (L - 1);
83 memcpy(ctr_nonce + 1, nonce, nnonce);
84 memset(ctr_nonce + 1 + nnonce, 0, L);
85 }
86
cf_ccm_encrypt(const cf_prp * prp,void * prpctx,const uint8_t * plain,size_t nplain,size_t L,const uint8_t * header,size_t nheader,const uint8_t * nonce,size_t nnonce,uint8_t * cipher,uint8_t * tag,size_t ntag)87 void cf_ccm_encrypt(const cf_prp *prp, void *prpctx,
88 const uint8_t *plain, size_t nplain, size_t L,
89 const uint8_t *header, size_t nheader,
90 const uint8_t *nonce, size_t nnonce,
91 uint8_t *cipher,
92 uint8_t *tag, size_t ntag)
93 {
94 uint8_t block[CF_MAXBLOCK];
95
96 assert(ntag >= 4 && ntag <= 16 && ntag % 2 == 0);
97 assert(L >= 2 && L <= 8);
98 assert(nnonce == prp->blocksz - L - 1);
99
100 cf_cbcmac_stream cm;
101 cf_cbcmac_stream_init(&cm, prp, prpctx);
102
103 /* Add first block. */
104 add_block0(&cm, block, prp->blocksz,
105 nonce, nnonce,
106 L, nplain, nheader, ntag);
107
108 /* Add AAD with length prefix, if present. */
109 if (nheader)
110 add_aad(&cm, block, header, nheader);
111
112 /* Add message. */
113 cf_cbcmac_stream_update(&cm, plain, nplain);
114 zero_pad(&cm);
115
116 /* Finish tag. */
117 cf_cbcmac_stream_nopad_final(&cm, block);
118
119 /* Start encryption. */
120 /* Construct A_0 */
121 uint8_t ctr_nonce[CF_MAXBLOCK];
122 build_ctr_nonce(ctr_nonce, L, nonce, nnonce);
123
124 cf_ctr ctr;
125 cf_ctr_init(&ctr, prp, prpctx, ctr_nonce);
126 cf_ctr_custom_counter(&ctr, prp->blocksz - L, L);
127
128 /* Encrypt tag first. */
129 cf_ctr_cipher(&ctr, block, block, prp->blocksz);
130 memcpy(tag, block, ntag);
131
132 /* Then encrypt message. */
133 cf_ctr_cipher(&ctr, plain, cipher, nplain);
134 }
135
cf_ccm_decrypt(const cf_prp * prp,void * prpctx,const uint8_t * cipher,size_t ncipher,size_t L,const uint8_t * header,size_t nheader,const uint8_t * nonce,size_t nnonce,const uint8_t * tag,size_t ntag,uint8_t * plain)136 int cf_ccm_decrypt(const cf_prp *prp, void *prpctx,
137 const uint8_t *cipher, size_t ncipher, size_t L,
138 const uint8_t *header, size_t nheader,
139 const uint8_t *nonce, size_t nnonce,
140 const uint8_t *tag, size_t ntag,
141 uint8_t *plain)
142 {
143 uint8_t block[CF_MAXBLOCK];
144
145 assert(ntag >= 4 && ntag <= 16 && ntag % 2 == 0);
146 assert(L >= 2 && L <= 8);
147 assert(nnonce == prp->blocksz - L - 1);
148
149 uint8_t ctr_nonce[CF_MAXBLOCK];
150 build_ctr_nonce(ctr_nonce, L, nonce, nnonce);
151
152 cf_ctr ctr;
153 cf_ctr_init(&ctr, prp, prpctx, ctr_nonce);
154 cf_ctr_custom_counter(&ctr, prp->blocksz - L, L);
155
156 /* Decrypt tag. */
157 uint8_t plain_tag[CF_MAXBLOCK];
158 cf_ctr_cipher(&ctr, tag, plain_tag, ntag);
159 cf_ctr_discard_block(&ctr);
160
161 /* Decrypt message. */
162 cf_ctr_cipher(&ctr, cipher, plain, ncipher);
163
164 cf_cbcmac_stream cm;
165 cf_cbcmac_stream_init(&cm, prp, prpctx);
166
167 /* Add first block. */
168 add_block0(&cm, block, prp->blocksz,
169 nonce, nnonce,
170 L, ncipher, nheader, ntag);
171
172 if (nheader)
173 add_aad(&cm, block, header, nheader);
174
175 cf_cbcmac_stream_update(&cm, plain, ncipher);
176 zero_pad(&cm);
177
178 /* Finish tag. */
179 cf_cbcmac_stream_nopad_final(&cm, block);
180
181 int err = 0;
182
183 if (!mem_eq(block, plain_tag, ntag))
184 {
185 err = 1;
186 mem_clean(plain, ncipher);
187 }
188
189 mem_clean(block, sizeof block);
190 mem_clean(plain_tag, sizeof plain_tag);
191 return err;
192 }
193
194