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; }
/** Process an entire GCM packet in one call. @param cipher Index of cipher to use @param key The secret key @param keylen The length of the secret key @param IV The initial vector @param IVlen The length of the initial vector @param adata The additional authentication data (header) @param adatalen The length of the adata @param pt The plaintext @param ptlen The length of the plaintext (ciphertext length is the same) @param ct The ciphertext @param tag [out] The MAC tag @param taglen [in/out] The MAC tag length @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) @return CRYPT_OK on success */ int gcm_memory( int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *IV, unsigned long IVlen, const unsigned char *adata, unsigned long adatalen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen, int direction) { void *orig; gcm_state *gcm; int err; if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { return err; } if (cipher_descriptor[cipher].accel_gcm_memory != NULL) { return cipher_descriptor[cipher].accel_gcm_memory (key, keylen, IV, IVlen, adata, adatalen, pt, ptlen, ct, tag, taglen, direction); } #ifndef LTC_GCM_TABLES_SSE2 orig = gcm = XMALLOC(sizeof(*gcm)); #else orig = gcm = XMALLOC(sizeof(*gcm) + 16); #endif if (gcm == NULL) { return CRYPT_MEM; } /* Force GCM to be on a multiple of 16 so we can use 128-bit aligned operations * note that we only modify gcm and keep orig intact. This code is not portable * but again it's only for SSE2 anyways, so who cares? */ #ifdef LTC_GCM_TABLES_SSE2 if ((unsigned long)gcm & 15) { gcm = (gcm_state *)((unsigned long)gcm + (16 - ((unsigned long)gcm & 15))); } #endif if ((err = gcm_init(gcm, cipher, key, keylen)) != CRYPT_OK) { goto LTC_ERR; } if ((err = gcm_add_iv(gcm, IV, IVlen)) != CRYPT_OK) { goto LTC_ERR; } if ((err = gcm_add_aad(gcm, adata, adatalen)) != CRYPT_OK) { goto LTC_ERR; } if ((err = gcm_process(gcm, pt, ptlen, ct, direction)) != CRYPT_OK) { goto LTC_ERR; } err = gcm_done(gcm, tag, taglen); LTC_ERR: XFREE(orig); return err; }
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; }