| /* LibTomCrypt, modular cryptographic library -- Tom St Denis |
| * |
| * LibTomCrypt is a library that provides various cryptographic |
| * algorithms in a highly modular and flexible manner. |
| * |
| * The library is free for all purposes without any express |
| * guarantee it works. |
| * |
| * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com |
| */ |
| #include "tomcrypt.h" |
| |
| /** |
| @file ccm_memory.c |
| CCM support, process a block of memory, Tom St Denis |
| */ |
| |
| #ifdef CCM_MODE |
| |
| /** |
| CCM encrypt/decrypt and produce an authentication tag |
| @param cipher The index of the cipher desired |
| @param key The secret key to use |
| @param keylen The length of the secret key (octets) |
| @param uskey A previously scheduled key [optional can be NULL] |
| @param nonce The session nonce [use once] |
| @param noncelen The length of the nonce |
| @param header The header for the session |
| @param headerlen The length of the header (octets) |
| @param pt [out] The plaintext |
| @param ptlen The length of the plaintext (octets) |
| @param ct [out] The ciphertext |
| @param tag [out] The destination tag |
| @param taglen [in/out] The max size and resulting size of the authentication tag |
| @param direction Encrypt or Decrypt direction (0 or 1) |
| @return CRYPT_OK if successful |
| */ |
| int ccm_memory(int cipher, |
| const unsigned char *key, unsigned long keylen, |
| symmetric_key *uskey, |
| const unsigned char *nonce, unsigned long noncelen, |
| const unsigned char *header, unsigned long headerlen, |
| unsigned char *pt, unsigned long ptlen, |
| unsigned char *ct, |
| unsigned char *tag, unsigned long *taglen, |
| int direction) |
| { |
| unsigned char PAD[16], ctr[16], CTRPAD[16], b; |
| symmetric_key *skey; |
| int err; |
| unsigned long len, L, x, y, z, CTRlen; |
| |
| if (uskey == NULL) { |
| LTC_ARGCHK(key != NULL); |
| } |
| LTC_ARGCHK(nonce != NULL); |
| if (headerlen > 0) { |
| LTC_ARGCHK(header != NULL); |
| } |
| LTC_ARGCHK(pt != NULL); |
| LTC_ARGCHK(ct != NULL); |
| LTC_ARGCHK(tag != NULL); |
| LTC_ARGCHK(taglen != NULL); |
| |
| #ifdef LTC_FAST |
| if (16 % sizeof(LTC_FAST_TYPE)) { |
| return CRYPT_INVALID_ARG; |
| } |
| #endif |
| |
| /* check cipher input */ |
| if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { |
| return err; |
| } |
| if (cipher_descriptor[cipher].block_length != 16) { |
| return CRYPT_INVALID_CIPHER; |
| } |
| |
| /* make sure the taglen is even and <= 16 */ |
| *taglen &= ~1; |
| if (*taglen > 16) { |
| *taglen = 16; |
| } |
| |
| /* can't use < 4 */ |
| if (*taglen < 4) { |
| return CRYPT_INVALID_ARG; |
| } |
| |
| /* is there an accelerator? */ |
| if (cipher_descriptor[cipher].accel_ccm_memory != NULL) { |
| return cipher_descriptor[cipher].accel_ccm_memory( |
| key, keylen, |
| uskey, |
| nonce, noncelen, |
| header, headerlen, |
| pt, ptlen, |
| ct, |
| tag, taglen, |
| direction); |
| } |
| |
| /* let's get the L value */ |
| len = ptlen; |
| L = 0; |
| while (len) { |
| ++L; |
| len >>= 8; |
| } |
| if (L <= 1) { |
| L = 2; |
| } |
| |
| /* increase L to match the nonce len */ |
| noncelen = (noncelen > 13) ? 13 : noncelen; |
| if ((15 - noncelen) > L) { |
| L = 15 - noncelen; |
| } |
| |
| /* decrease noncelen to match L */ |
| if ((noncelen + L) > 15) { |
| noncelen = 15 - L; |
| } |
| |
| /* allocate mem for the symmetric key */ |
| if (uskey == NULL) { |
| skey = XMALLOC(sizeof(*skey)); |
| if (skey == NULL) { |
| return CRYPT_MEM; |
| } |
| |
| /* initialize the cipher */ |
| if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, skey)) != CRYPT_OK) { |
| XFREE(skey); |
| return err; |
| } |
| } else { |
| skey = uskey; |
| } |
| |
| /* form B_0 == flags | Nonce N | l(m) */ |
| x = 0; |
| PAD[x++] = (unsigned char)(((headerlen > 0) ? (1<<6) : 0) | |
| (((*taglen - 2)>>1)<<3) | |
| (L-1)); |
| |
| /* nonce */ |
| for (y = 0; y < (16 - (L + 1)); y++) { |
| PAD[x++] = nonce[y]; |
| } |
| |
| /* store len */ |
| len = ptlen; |
| |
| /* shift len so the upper bytes of len are the contents of the length */ |
| for (y = L; y < 4; y++) { |
| len <<= 8; |
| } |
| |
| /* store l(m) (only store 32-bits) */ |
| for (y = 0; L > 4 && (L-y)>4; y++) { |
| PAD[x++] = 0; |
| } |
| for (; y < L; y++) { |
| PAD[x++] = (unsigned char)((len >> 24) & 255); |
| len <<= 8; |
| } |
| |
| /* encrypt PAD */ |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* handle header */ |
| if (headerlen > 0) { |
| x = 0; |
| |
| /* store length */ |
| if (headerlen < ((1UL<<16) - (1UL<<8))) { |
| PAD[x++] ^= (headerlen>>8) & 255; |
| PAD[x++] ^= headerlen & 255; |
| } else { |
| PAD[x++] ^= 0xFF; |
| PAD[x++] ^= 0xFE; |
| PAD[x++] ^= (headerlen>>24) & 255; |
| PAD[x++] ^= (headerlen>>16) & 255; |
| PAD[x++] ^= (headerlen>>8) & 255; |
| PAD[x++] ^= headerlen & 255; |
| } |
| |
| /* now add the data */ |
| for (y = 0; y < headerlen; y++) { |
| if (x == 16) { |
| /* full block so let's encrypt it */ |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| x = 0; |
| } |
| PAD[x++] ^= header[y]; |
| } |
| |
| /* remainder? */ |
| if (x != 0) { |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| } |
| } |
| |
| /* setup the ctr counter */ |
| x = 0; |
| |
| /* flags */ |
| ctr[x++] = (unsigned char)L-1; |
| |
| /* nonce */ |
| for (y = 0; y < (16 - (L+1)); ++y) { |
| ctr[x++] = nonce[y]; |
| } |
| /* offset */ |
| while (x < 16) { |
| ctr[x++] = 0; |
| } |
| |
| x = 0; |
| CTRlen = 16; |
| |
| /* now handle the PT */ |
| if (ptlen > 0) { |
| y = 0; |
| #ifdef LTC_FAST |
| if (ptlen & ~15) { |
| if (direction == CCM_ENCRYPT) { |
| for (; y < (ptlen & ~15); y += 16) { |
| /* increment the ctr? */ |
| for (z = 15; z > 15-L; z--) { |
| ctr[z] = (ctr[z] + 1) & 255; |
| if (ctr[z]) break; |
| } |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* xor the PT against the pad first */ |
| for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) { |
| *((LTC_FAST_TYPE*)(&PAD[z])) ^= *((LTC_FAST_TYPE*)(&pt[y+z])); |
| *((LTC_FAST_TYPE*)(&ct[y+z])) = *((LTC_FAST_TYPE*)(&pt[y+z])) ^ *((LTC_FAST_TYPE*)(&CTRPAD[z])); |
| } |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| } |
| } else { |
| for (; y < (ptlen & ~15); y += 16) { |
| /* increment the ctr? */ |
| for (z = 15; z > 15-L; z--) { |
| ctr[z] = (ctr[z] + 1) & 255; |
| if (ctr[z]) break; |
| } |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* xor the PT against the pad last */ |
| for (z = 0; z < 16; z += sizeof(LTC_FAST_TYPE)) { |
| *((LTC_FAST_TYPE*)(&pt[y+z])) = *((LTC_FAST_TYPE*)(&ct[y+z])) ^ *((LTC_FAST_TYPE*)(&CTRPAD[z])); |
| *((LTC_FAST_TYPE*)(&PAD[z])) ^= *((LTC_FAST_TYPE*)(&pt[y+z])); |
| } |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| } |
| } |
| } |
| #endif |
| |
| for (; y < ptlen; y++) { |
| /* increment the ctr? */ |
| if (CTRlen == 16) { |
| for (z = 15; z > 15-L; z--) { |
| ctr[z] = (ctr[z] + 1) & 255; |
| if (ctr[z]) break; |
| } |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| CTRlen = 0; |
| } |
| |
| /* if we encrypt we add the bytes to the MAC first */ |
| if (direction == CCM_ENCRYPT) { |
| b = pt[y]; |
| ct[y] = b ^ CTRPAD[CTRlen++]; |
| } else { |
| b = ct[y] ^ CTRPAD[CTRlen++]; |
| pt[y] = b; |
| } |
| |
| if (x == 16) { |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| x = 0; |
| } |
| PAD[x++] ^= b; |
| } |
| |
| if (x != 0) { |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(PAD, PAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| } |
| } |
| |
| /* setup CTR for the TAG (zero the count) */ |
| for (y = 15; y > 15 - L; y--) { |
| ctr[y] = 0x00; |
| } |
| if ((err = cipher_descriptor[cipher].ecb_encrypt(ctr, CTRPAD, skey)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if (skey != uskey) { |
| cipher_descriptor[cipher].done(skey); |
| } |
| |
| /* store the TAG */ |
| for (x = 0; x < 16 && x < *taglen; x++) { |
| tag[x] = PAD[x] ^ CTRPAD[x]; |
| } |
| *taglen = x; |
| |
| #ifdef LTC_CLEAN_STACK |
| zeromem(skey, sizeof(*skey)); |
| zeromem(PAD, sizeof(PAD)); |
| zeromem(CTRPAD, sizeof(CTRPAD)); |
| #endif |
| error: |
| if (skey != uskey) { |
| XFREE(skey); |
| } |
| |
| return err; |
| } |
| |
| #endif |
| |
| /* $Source: /cvs/libtom/libtomcrypt/src/encauth/ccm/ccm_memory.c,v $ */ |
| /* $Revision: 1.18 $ */ |
| /* $Date: 2006/12/04 21:34:03 $ */ |