/** Finish OCB processing and compute the tag @param ocb The OCB state @param tag [out] The destination for the authentication tag @param taglen [in/out] The max size and resulting size of the authentication tag @return CRYPT_OK if successful */ int ocb3_done(ocb3_state *ocb, unsigned char *tag, unsigned long *taglen) { unsigned char tmp[MAXBLOCKSIZE]; int err, x; LTC_ARGCHK(ocb != NULL); LTC_ARGCHK(tag != NULL); LTC_ARGCHK(taglen != NULL); if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) { goto LBL_ERR; } /* finalize AAD processing */ if (ocb->adata_buffer_bytes>0) { /* Offset_* = Offset_m xor L_* */ ocb3_int_xor_blocks(ocb->aOffset_current, ocb->aOffset_current, ocb->L_star, ocb->block_len); /* CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_* */ ocb3_int_xor_blocks(tmp, ocb->adata_buffer, ocb->aOffset_current, ocb->adata_buffer_bytes); for(x=ocb->adata_buffer_bytes; x<ocb->block_len; x++) { if (x == ocb->adata_buffer_bytes) { tmp[x] = 0x80 ^ ocb->aOffset_current[x]; } else { tmp[x] = 0x00 ^ ocb->aOffset_current[x]; } } /* Sum = Sum_m xor ENCIPHER(K, CipherInput) */ if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) { goto LBL_ERR; } ocb3_int_xor_blocks(ocb->aSum_current, ocb->aSum_current, tmp, ocb->block_len); } /* finalize TAG computing */ /* at this point ocb->aSum_current = HASH(K, A) */ /* tag = tag ^ HASH(K, A) */ ocb3_int_xor_blocks(tmp, ocb->tag_part, ocb->aSum_current, ocb->block_len); /* fix taglen if needed */ if ((int)*taglen > ocb->block_len) { *taglen = (unsigned long)ocb->block_len; } /* copy tag bytes */ for(x=0; x<(int)*taglen; x++) tag[x] = tmp[x]; err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(tmp, MAXBLOCKSIZE); zeromem(ocb, sizeof(*ocb)); #endif return err; }
/** Encrypt blocks of data with OCB @param ocb The OCB state @param pt The plaintext (length multiple of the block size of the block cipher) @param ptlen The length of the input (octets) @param ct [out] The ciphertext (same size as the pt) @return CRYPT_OK if successful */ int ocb3_encrypt(ocb3_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct) { unsigned char tmp[MAXBLOCKSIZE]; int err, i, full_blocks; unsigned char *pt_b, *ct_b; LTC_ARGCHK(ocb != NULL); if (ptlen == 0) return CRYPT_OK; /* no data, nothing to do */ LTC_ARGCHK(pt != NULL); LTC_ARGCHK(ct != NULL); if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) { return err; } if (ocb->block_len != cipher_descriptor[ocb->cipher].block_length) { return CRYPT_INVALID_ARG; } if (ptlen % ocb->block_len) { /* ptlen has to bu multiple of block_len */ return CRYPT_INVALID_ARG; } full_blocks = ptlen/ocb->block_len; for(i=0; i<full_blocks; i++) { pt_b = (unsigned char *)pt+i*ocb->block_len; ct_b = (unsigned char *)ct+i*ocb->block_len; /* ocb->Offset_current[] = ocb->Offset_current[] ^ Offset_{ntz(block_index)} */ ocb3_int_xor_blocks(ocb->Offset_current, ocb->Offset_current, ocb->L_[ocb3_int_ntz(ocb->block_index)], ocb->block_len); /* tmp[] = pt[] XOR ocb->Offset_current[] */ ocb3_int_xor_blocks(tmp, pt_b, ocb->Offset_current, ocb->block_len); /* encrypt */ if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) { goto LBL_ERR; } /* ct[] = tmp[] XOR ocb->Offset_current[] */ ocb3_int_xor_blocks(ct_b, tmp, ocb->Offset_current, ocb->block_len); /* ocb->checksum[] = ocb->checksum[] XOR pt[] */ ocb3_int_xor_blocks(ocb->checksum, ocb->checksum, pt_b, ocb->block_len); ocb->block_index++; } err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(tmp, sizeof(tmp)); #endif return err; }
/** Add one block of AAD data (internal function) @param ocb The OCB state @param aad_block [in] AAD data (block_len size) @return CRYPT_OK if successful */ static int _ocb3_int_aad_add_block(ocb3_state *ocb, const unsigned char *aad_block) { unsigned char tmp[MAXBLOCKSIZE]; int err; /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */ ocb3_int_xor_blocks(ocb->aOffset_current, ocb->aOffset_current, ocb->L_[ocb3_int_ntz(ocb->ablock_index)], ocb->block_len); /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) */ ocb3_int_xor_blocks(tmp, aad_block, ocb->aOffset_current, ocb->block_len); if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(tmp, tmp, &ocb->key)) != CRYPT_OK) { return err; } ocb3_int_xor_blocks(ocb->aSum_current, ocb->aSum_current, tmp, ocb->block_len); ocb->ablock_index++; return CRYPT_OK; }
/** Finish an OCB (decryption) stream @param ocb The OCB state @param ct The remaining ciphertext @param ctlen The length of the ciphertext (octets) @param pt [out] The output buffer @return CRYPT_OK if successful */ int ocb3_decrypt_last(ocb3_state *ocb, const unsigned char *ct, unsigned long ctlen, unsigned char *pt) { unsigned char iOffset_star[MAXBLOCKSIZE]; unsigned char iPad[MAXBLOCKSIZE]; int err, x, full_blocks, full_blocks_len, last_block_len; LTC_ARGCHK(ocb != NULL); LTC_ARGCHK(ct != NULL); if ((err = cipher_is_valid(ocb->cipher)) != CRYPT_OK) { goto LBL_ERR; } full_blocks = ctlen/ocb->block_len; full_blocks_len = full_blocks * ocb->block_len; last_block_len = ctlen - full_blocks_len; /* process full blocks first */ if (full_blocks>0) { if ((err = ocb3_decrypt(ocb, ct, full_blocks_len, pt)) != CRYPT_OK) { goto LBL_ERR; } } if (last_block_len>0) { /* Offset_* = Offset_m xor L_* */ ocb3_int_xor_blocks(iOffset_star, ocb->Offset_current, ocb->L_star, ocb->block_len); /* Pad = ENCIPHER(K, Offset_*) */ if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(iOffset_star, iPad, &ocb->key)) != CRYPT_OK) { goto LBL_ERR; } /* P_* = C_* xor Pad[1..bitlen(C_*)] */ ocb3_int_xor_blocks(pt+full_blocks_len, (unsigned char *)ct+full_blocks_len, iPad, last_block_len); /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */ ocb3_int_xor_blocks(ocb->checksum, ocb->checksum, pt+full_blocks_len, last_block_len); for(x=last_block_len; x<ocb->block_len; x++) { if (x == last_block_len) ocb->checksum[x] ^= 0x80; else ocb->checksum[x] ^= 0x00; } /* Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A) */ /* at this point we calculate only: Tag_part = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) */ for(x=0; x<ocb->block_len; x++) { ocb->tag_part[x] = (ocb->checksum[x] ^ iOffset_star[x]) ^ ocb->L_dollar[x]; } if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->tag_part, ocb->tag_part, &ocb->key)) != CRYPT_OK) { goto LBL_ERR; } } else { /* Tag = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) xor HASH(K,A) */ /* at this point we calculate only: Tag_part = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) */ for(x=0; x<ocb->block_len; x++) { ocb->tag_part[x] = (ocb->checksum[x] ^ ocb->Offset_current[x]) ^ ocb->L_dollar[x]; } if ((err = cipher_descriptor[ocb->cipher].ecb_encrypt(ocb->tag_part, ocb->tag_part, &ocb->key)) != CRYPT_OK) { goto LBL_ERR; } } err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(iOffset_star, MAXBLOCKSIZE); zeromem(iPad, MAXBLOCKSIZE); #endif return err; }