TEE_Result tee_do_cipher_update(void *ctx, uint32_t algo, TEE_OperationMode mode, bool last_block, const uint8_t *data, size_t len, uint8_t *dst) { TEE_Result res; size_t block_size; if (mode != TEE_MODE_ENCRYPT && mode != TEE_MODE_DECRYPT) return TEE_ERROR_BAD_PARAMETERS; /* * Check that the block contains the correct number of data, apart * for the last block in some XTS / CTR / XTS mode */ res = tee_cipher_get_block_size(algo, &block_size); if (res != TEE_SUCCESS) return res; if ((len % block_size) != 0) { if (!last_block && algo != TEE_ALG_AES_CTR) return TEE_ERROR_BAD_PARAMETERS; switch (algo) { case TEE_ALG_AES_ECB_NOPAD: case TEE_ALG_DES_ECB_NOPAD: case TEE_ALG_DES3_ECB_NOPAD: case TEE_ALG_AES_CBC_NOPAD: case TEE_ALG_DES_CBC_NOPAD: case TEE_ALG_DES3_CBC_NOPAD: return TEE_ERROR_BAD_PARAMETERS; case TEE_ALG_AES_CTR: case TEE_ALG_AES_XTS: case TEE_ALG_AES_CTS: /* * These modes doesn't require padding for the last * block. * * This isn't entirely true, both XTS and CTS can only * encrypt minimum one block and also they need at least * one complete block in the last update to finish the * encryption. The algorithms are supposed to detect * that, we're only making sure that all data fed up to * that point consists of complete blocks. */ break; default: return TEE_ERROR_NOT_SUPPORTED; } } return crypto_cipher_update(ctx, algo, mode, last_block, data, len, dst); }
TEE_Result tee_authenc_dec_final( void *ctx, uint32_t algo, const uint8_t *src_data, size_t src_len, uint8_t *dst_data, const uint8_t *tag, size_t tag_len) { TEE_Result res = TEE_ERROR_BAD_STATE; struct tee_ccm_state *ccm; struct tee_gcm_state *gcm; int ltc_res; uint8_t dst_tag[TEE_xCM_TAG_MAX_LENGTH]; size_t dst_len, init_len; unsigned long ltc_tag_len = tag_len; res = tee_cipher_get_block_size(algo, &dst_len); if (res != TEE_SUCCESS) return res; if (tag_len == 0) return TEE_ERROR_SHORT_BUFFER; if (tag_len > TEE_xCM_TAG_MAX_LENGTH) return TEE_ERROR_BAD_STATE; switch (algo) { case TEE_ALG_AES_CCM: ccm = ctx; init_len = ccm->current_payload_len; if (src_len) { memcpy(ccm->payload + ccm->current_payload_len, src_data, src_len); ccm->current_payload_len += src_len; } if (ccm->payload_len != ccm->current_payload_len) return TEE_ERROR_BAD_PARAMETERS; ltc_res = ccm_memory( ccm->ltc_cipherindex, ccm->key, ccm->key_len, 0, /* not previously scheduled */ ccm->nonce, ccm->nonce_len, ccm->header, ccm->header_len, ccm->res_payload, ccm->current_payload_len, ccm->payload, dst_tag, <c_tag_len, CCM_DECRYPT); if (ltc_res != CRYPT_OK) return TEE_ERROR_BAD_STATE; if (src_len) memcpy(dst_data, ccm->res_payload + init_len, src_len); break; case TEE_ALG_AES_GCM: /* Process the last buffer, if any */ gcm = ctx; res = tee_authenc_update_payload( &gcm->ctx, algo, TEE_MODE_DECRYPT, src_data, src_len, dst_data); if (res != TEE_SUCCESS) return res; /* Finalize the authentication */ ltc_res = gcm_done(&gcm->ctx, dst_tag, <c_tag_len); if (ltc_res != CRYPT_OK) return TEE_ERROR_BAD_STATE; break; default: return TEE_ERROR_NOT_SUPPORTED; } if (buf_compare_ct(dst_tag, tag, tag_len) != 0) res = TEE_ERROR_MAC_INVALID; else res = TEE_SUCCESS; return res; }
TEE_Result tee_authenc_enc_final( void *ctx, uint32_t algo, const uint8_t *src_data, size_t src_len, uint8_t *dst_data, uint8_t *dst_tag, size_t *dst_tag_len) { TEE_Result res, final_res = TEE_ERROR_MAC_INVALID; struct tee_ccm_state *ccm; struct tee_gcm_state *gcm; size_t digest_size; int ltc_res; int init_len; /* Check the resulting buffer is not too short */ res = tee_cipher_get_block_size(algo, &digest_size); if (res != TEE_SUCCESS) { final_res = res; goto out; } switch (algo) { case TEE_ALG_AES_CCM: ccm = ctx; init_len = ccm->current_payload_len; if (src_len) { memcpy(ccm->payload + ccm->current_payload_len, src_data, src_len); ccm->current_payload_len += src_len; } if (ccm->payload_len != ccm->current_payload_len) return TEE_ERROR_BAD_PARAMETERS; if (*dst_tag_len < ccm->tag_len) { *dst_tag_len = ccm->tag_len; return TEE_ERROR_SHORT_BUFFER; } *dst_tag_len = ccm->tag_len; ltc_res = ccm_memory( ccm->ltc_cipherindex, ccm->key, ccm->key_len, 0, /* not previously scheduled */ ccm->nonce, ccm->nonce_len, ccm->header, ccm->header_len, ccm->payload, ccm->current_payload_len, ccm->res_payload, dst_tag, (unsigned long *)dst_tag_len, CCM_ENCRYPT); if (ltc_res != CRYPT_OK) return TEE_ERROR_BAD_STATE; if (src_len) memcpy(dst_data, ccm->res_payload + init_len, src_len); break; case TEE_ALG_AES_GCM: /* Finalize the remaining buffer */ gcm = ctx; res = tee_authenc_update_payload( &gcm->ctx, algo, TEE_MODE_ENCRYPT, src_data, src_len, dst_data); if (res != TEE_SUCCESS) { final_res = res; goto out; } if (*dst_tag_len < gcm->tag_len) { *dst_tag_len = gcm->tag_len; return TEE_ERROR_SHORT_BUFFER; } *dst_tag_len = gcm->tag_len; /* Process the last buffer, if any */ ltc_res = gcm_done( &gcm->ctx, dst_tag, (unsigned long *)dst_tag_len); if (ltc_res != CRYPT_OK) goto out; break; default: return TEE_ERROR_NOT_SUPPORTED; } final_res = TEE_SUCCESS; out: return final_res; }
TEE_Result tee_cipher_update(void *ctx, uint32_t algo, TEE_OperationMode mode, bool last_block, const uint8_t *data, size_t len, uint8_t *dst) { TEE_Result res; int ltc_res = CRYPT_OK; size_t block_size; uint8_t tmp_block[64], tmp2_block[64]; int nb_blocks, len_last_block; struct symmetric_CTS *cts; /* * Check that the block contains the correct number of data, apart * for the last block in some XTS / CTR / XTS mode */ res = tee_cipher_get_block_size(algo, &block_size); if (res != TEE_SUCCESS) return res; if ((len % block_size) != 0) { if (!last_block) return TEE_ERROR_BAD_PARAMETERS; switch (algo) { case TEE_ALG_AES_ECB_NOPAD: case TEE_ALG_DES_ECB_NOPAD: case TEE_ALG_DES3_ECB_NOPAD: case TEE_ALG_AES_CBC_NOPAD: case TEE_ALG_DES_CBC_NOPAD: case TEE_ALG_DES3_CBC_NOPAD: return TEE_ERROR_BAD_PARAMETERS; case TEE_ALG_AES_CTR: case TEE_ALG_AES_XTS: case TEE_ALG_AES_CTS: /* * These modes doesn't require padding for the last * block. * * This isn't entirely true, both XTS and CTS can only * encrypt minimum one block and also they need at least * one complete block in the last update to finish the * encryption. The algorithms are supposed to detect * that, we're only making sure that all data fed up to * that point consists of complete blocks. */ break; default: return TEE_ERROR_NOT_SUPPORTED; } } switch (algo) { case TEE_ALG_AES_ECB_NOPAD: case TEE_ALG_DES_ECB_NOPAD: case TEE_ALG_DES3_ECB_NOPAD: if (mode == TEE_MODE_ENCRYPT) ltc_res = ecb_encrypt(data, dst, len, (symmetric_ECB *)ctx); else ltc_res = ecb_decrypt(data, dst, len, (symmetric_ECB *)ctx); break; case TEE_ALG_AES_CBC_NOPAD: case TEE_ALG_DES_CBC_NOPAD: case TEE_ALG_DES3_CBC_NOPAD: if (mode == TEE_MODE_ENCRYPT) ltc_res = cbc_encrypt(data, dst, len, (symmetric_CBC *)ctx); else ltc_res = cbc_decrypt(data, dst, len, (symmetric_CBC *)ctx); break; case TEE_ALG_AES_CTR: if (mode == TEE_MODE_ENCRYPT) ltc_res = ctr_encrypt(data, dst, len, (symmetric_CTR *)ctx); else ltc_res = ctr_decrypt(data, dst, len, (symmetric_CTR *)ctx); break; case TEE_ALG_AES_XTS: return TEE_ERROR_NOT_SUPPORTED; /* if (mode == TEE_MODE_ENCRYPT) { ltc_res = xts_encrypt(data, dst, len, (symmetric_xts *)ctx); } else { ltc_res = xts_decrypt(data, dst, len, (symmetric_xts *)ctx); } */ break; case TEE_ALG_AES_CTS: /* * From http://en.wikipedia.org/wiki/Ciphertext_stealing * CBC ciphertext stealing encryption using a standard * CBC interface: * 1. Pad the last partial plaintext block with 0. * 2. Encrypt the whole padded plaintext using the * standard CBC mode. * 3. Swap the last two ciphertext blocks. * 4. Truncate the ciphertext to the length of the * original plaintext. * * CBC ciphertext stealing decryption using a standard * CBC interface * 1. Dn = Decrypt (K, Cn-1). Decrypt the second to last * ciphertext block. * 2. Cn = Cn || Tail (Dn, B-M). Pad the ciphertext to the * nearest multiple of the block size using the last * B-M bits of block cipher decryption of the * second-to-last ciphertext block. * 3. Swap the last two ciphertext blocks. * 4. Decrypt the (modified) ciphertext using the standard * CBC mode. * 5. Truncate the plaintext to the length of the original * ciphertext. */ cts = (struct symmetric_CTS *)ctx; if (!last_block) return tee_cipher_update( &cts->cbc, TEE_ALG_AES_CBC_NOPAD, mode, last_block, data, len, dst); /* Compute the last block length and check constraints */ if (block_size > 64) return TEE_ERROR_BAD_STATE; nb_blocks = ((len + block_size - 1) / block_size); if (nb_blocks < 2) return TEE_ERROR_BAD_STATE; len_last_block = len % block_size; if (len_last_block == 0) len_last_block = block_size; if (mode == TEE_MODE_ENCRYPT) { memcpy(tmp_block, data + ((nb_blocks - 1) * block_size), len_last_block); memset(tmp_block + len_last_block, 0, block_size - len_last_block); res = tee_cipher_update( &cts->cbc, TEE_ALG_AES_CBC_NOPAD, mode, 0, data, (nb_blocks - 1) * block_size, dst); if (res != TEE_SUCCESS) return res; memcpy(dst + (nb_blocks - 1) * block_size, dst + (nb_blocks - 2) * block_size, len_last_block); res = tee_cipher_update( &cts->cbc, TEE_ALG_AES_CBC_NOPAD, mode, 0, tmp_block, block_size, dst + (nb_blocks - 2) * block_size); if (res != TEE_SUCCESS) return res; } else { /* 1. Decrypt the second to last ciphertext block */ res = tee_cipher_update( &cts->ecb, TEE_ALG_AES_ECB_NOPAD, mode, 0, data + (nb_blocks - 2) * block_size, block_size, tmp2_block); if (res != TEE_SUCCESS) return res; /* 2. Cn = Cn || Tail (Dn, B-M) */ memcpy(tmp_block, data + ((nb_blocks - 1) * block_size), len_last_block); memcpy(tmp_block + len_last_block, tmp2_block + len_last_block, block_size - len_last_block); /* 3. Swap the last two ciphertext blocks */ /* done by passing the correct buffers in step 4. */ /* 4. Decrypt the (modified) ciphertext */ if (nb_blocks > 2) { res = tee_cipher_update( &cts->cbc, TEE_ALG_AES_CBC_NOPAD, mode, 0, data, (nb_blocks - 2) * block_size, dst); if (res != TEE_SUCCESS) return res; } res = tee_cipher_update( &cts->cbc, TEE_ALG_AES_CBC_NOPAD, mode, 0, tmp_block, block_size, dst + ((nb_blocks - 2) * block_size)); if (res != TEE_SUCCESS) return res; res = tee_cipher_update( &cts->cbc, TEE_ALG_AES_CBC_NOPAD, mode, 0, data + ((nb_blocks - 2) * block_size), block_size, tmp_block); if (res != TEE_SUCCESS) return res; /* 5. Truncate the plaintext */ memcpy(dst + (nb_blocks - 1) * block_size, tmp_block, len_last_block); break; } break; default: return TEE_ERROR_NOT_SUPPORTED; } if (ltc_res == CRYPT_OK) return TEE_SUCCESS; else return TEE_ERROR_BAD_STATE; }