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