/*1* Copyright (c) 2017 Thomas Pornin <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining4* a copy of this software and associated documentation files (the5* "Software"), to deal in the Software without restriction, including6* without limitation the rights to use, copy, modify, merge, publish,7* distribute, sublicense, and/or sell copies of the Software, and to8* permit persons to whom the Software is furnished to do so, subject to9* the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#include "inner.h"2526/*27* Implementation Notes28* ====================29*30* The combined CTR + CBC-MAC functions can only handle full blocks,31* so some buffering is necessary.32*33* - 'ptr' contains a value from 0 to 15, which is the number of bytes34* accumulated in buf[] that still needs to be processed with the35* current CBC-MAC computation.36*37* - When processing the message itself, CTR encryption/decryption is38* also done at the same time. The first 'ptr' bytes of buf[] then39* contains the plaintext bytes, while the last '16 - ptr' bytes of40* buf[] are the remnants of the stream block, to be used against41* the next input bytes, when available. When 'ptr' is 0, the42* contents of buf[] are to be ignored.43*44* - The current counter and running CBC-MAC values are kept in 'ctr'45* and 'cbcmac', respectively.46*/4748/* see bearssl_block.h */49void50br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx)51{52ctx->bctx = bctx;53}5455/* see bearssl_block.h */56int57br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len,58uint64_t aad_len, uint64_t data_len, size_t tag_len)59{60unsigned char tmp[16];61unsigned u, q;6263if (nonce_len < 7 || nonce_len > 13) {64return 0;65}66if (tag_len < 4 || tag_len > 16 || (tag_len & 1) != 0) {67return 0;68}69q = 15 - (unsigned)nonce_len;70ctx->tag_len = tag_len;7172/*73* Block B0, to start CBC-MAC.74*/75tmp[0] = (aad_len > 0 ? 0x40 : 0x00)76| (((unsigned)tag_len - 2) << 2)77| (q - 1);78memcpy(tmp + 1, nonce, nonce_len);79for (u = 0; u < q; u ++) {80tmp[15 - u] = (unsigned char)data_len;81data_len >>= 8;82}83if (data_len != 0) {84/*85* If the data length was not entirely consumed in the86* loop above, then it exceeds the maximum limit of87* q bytes (when encoded).88*/89return 0;90}9192/*93* Start CBC-MAC.94*/95memset(ctx->cbcmac, 0, sizeof ctx->cbcmac);96(*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, tmp, sizeof tmp);9798/*99* Assemble AAD length header.100*/101if ((aad_len >> 32) != 0) {102ctx->buf[0] = 0xFF;103ctx->buf[1] = 0xFF;104br_enc64be(ctx->buf + 2, aad_len);105ctx->ptr = 10;106} else if (aad_len >= 0xFF00) {107ctx->buf[0] = 0xFF;108ctx->buf[1] = 0xFE;109br_enc32be(ctx->buf + 2, (uint32_t)aad_len);110ctx->ptr = 6;111} else if (aad_len > 0) {112br_enc16be(ctx->buf, (unsigned)aad_len);113ctx->ptr = 2;114} else {115ctx->ptr = 0;116}117118/*119* Make initial counter value and compute tag mask.120*/121ctx->ctr[0] = q - 1;122memcpy(ctx->ctr + 1, nonce, nonce_len);123memset(ctx->ctr + 1 + nonce_len, 0, q);124memset(ctx->tagmask, 0, sizeof ctx->tagmask);125(*ctx->bctx)->ctr(ctx->bctx, ctx->ctr,126ctx->tagmask, sizeof ctx->tagmask);127128return 1;129}130131/* see bearssl_block.h */132void133br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len)134{135const unsigned char *dbuf;136size_t ptr;137138dbuf = data;139140/*141* Complete partial block, if needed.142*/143ptr = ctx->ptr;144if (ptr != 0) {145size_t clen;146147clen = (sizeof ctx->buf) - ptr;148if (clen > len) {149memcpy(ctx->buf + ptr, dbuf, len);150ctx->ptr = ptr + len;151return;152}153memcpy(ctx->buf + ptr, dbuf, clen);154dbuf += clen;155len -= clen;156(*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac,157ctx->buf, sizeof ctx->buf);158}159160/*161* Process complete blocks.162*/163ptr = len & 15;164len -= ptr;165(*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, dbuf, len);166dbuf += len;167168/*169* Copy last partial block in the context buffer.170*/171memcpy(ctx->buf, dbuf, ptr);172ctx->ptr = ptr;173}174175/* see bearssl_block.h */176void177br_ccm_flip(br_ccm_context *ctx)178{179size_t ptr;180181/*182* Complete AAD partial block with zeros, if necessary.183*/184ptr = ctx->ptr;185if (ptr != 0) {186memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr);187(*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac,188ctx->buf, sizeof ctx->buf);189ctx->ptr = 0;190}191192/*193* Counter was already set by br_ccm_reset().194*/195}196197/* see bearssl_block.h */198void199br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len)200{201unsigned char *dbuf;202size_t ptr;203204dbuf = data;205206/*207* Complete a partial block, if any: ctx->buf[] contains208* ctx->ptr plaintext bytes (already reported), and the other209* bytes are CTR stream output.210*/211ptr = ctx->ptr;212if (ptr != 0) {213size_t clen;214size_t u;215216clen = (sizeof ctx->buf) - ptr;217if (clen > len) {218clen = len;219}220if (encrypt) {221for (u = 0; u < clen; u ++) {222unsigned w, x;223224w = ctx->buf[ptr + u];225x = dbuf[u];226ctx->buf[ptr + u] = x;227dbuf[u] = w ^ x;228}229} else {230for (u = 0; u < clen; u ++) {231unsigned w;232233w = ctx->buf[ptr + u] ^ dbuf[u];234dbuf[u] = w;235ctx->buf[ptr + u] = w;236}237}238dbuf += clen;239len -= clen;240ptr += clen;241if (ptr < sizeof ctx->buf) {242ctx->ptr = ptr;243return;244}245(*ctx->bctx)->mac(ctx->bctx,246ctx->cbcmac, ctx->buf, sizeof ctx->buf);247}248249/*250* Process all complete blocks. Note that the ctrcbc API is for251* encrypt-then-MAC (CBC-MAC is computed over the encrypted252* blocks) while CCM uses MAC-and-encrypt (CBC-MAC is computed253* over the plaintext blocks). Therefore, we need to use the254* _decryption_ function for encryption, and the encryption255* function for decryption (this works because CTR encryption256* and decryption are identical, so the choice really is about257* computing the CBC-MAC before or after XORing with the CTR258* stream).259*/260ptr = len & 15;261len -= ptr;262if (encrypt) {263(*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac,264dbuf, len);265} else {266(*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac,267dbuf, len);268}269dbuf += len;270271/*272* If there is some remaining data, then we need to compute an273* extra block of CTR stream.274*/275if (ptr != 0) {276size_t u;277278memset(ctx->buf, 0, sizeof ctx->buf);279(*ctx->bctx)->ctr(ctx->bctx, ctx->ctr,280ctx->buf, sizeof ctx->buf);281if (encrypt) {282for (u = 0; u < ptr; u ++) {283unsigned w, x;284285w = ctx->buf[u];286x = dbuf[u];287ctx->buf[u] = x;288dbuf[u] = w ^ x;289}290} else {291for (u = 0; u < ptr; u ++) {292unsigned w;293294w = ctx->buf[u] ^ dbuf[u];295dbuf[u] = w;296ctx->buf[u] = w;297}298}299}300ctx->ptr = ptr;301}302303/* see bearssl_block.h */304size_t305br_ccm_get_tag(br_ccm_context *ctx, void *tag)306{307size_t ptr;308size_t u;309310/*311* If there is some buffered data, then we need to pad it with312* zeros and finish up CBC-MAC.313*/314ptr = ctx->ptr;315if (ptr != 0) {316memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr);317(*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac,318ctx->buf, sizeof ctx->buf);319}320321/*322* XOR the tag mask into the CBC-MAC output.323*/324for (u = 0; u < ctx->tag_len; u ++) {325ctx->cbcmac[u] ^= ctx->tagmask[u];326}327memcpy(tag, ctx->cbcmac, ctx->tag_len);328return ctx->tag_len;329}330331/* see bearssl_block.h */332uint32_t333br_ccm_check_tag(br_ccm_context *ctx, const void *tag)334{335unsigned char tmp[16];336size_t u, tag_len;337uint32_t z;338339tag_len = br_ccm_get_tag(ctx, tmp);340z = 0;341for (u = 0; u < tag_len; u ++) {342z |= tmp[u] ^ ((const unsigned char *)tag)[u];343}344return EQ0(z);345}346347348