* This is a variable Initialization Vector length mode (recommended 96-bits).
*
* Supported properties:
- * - #YACA_PROPERTY_GCM_TAG_LEN = GCM tag length\n
- * Supported tag lengths: @c 4, @c 8, @c 12, @c 13, @c 14, @c 15, @c 16,
- * (recommended 16 bytes tag).\n
+ * - #YACA_PROPERTY_GCM_TAG_LEN = GCM tag length (optional)\n
+ * Supported tag lengths: @c 4, @c 8, @c 12, @c 13, @c 14, @c 15, @c 16
+ * (16 bytes tag by default).\n
* Set after yaca_encrypt_finalize() / yaca_seal_finalize() and before
* yaca_context_get_property(#YACA_PROPERTY_GCM_TAG) in encryption / seal operation.\n
* The @a value should be a size_t variable.\n
*
* - #YACA_PROPERTY_GCM_TAG = GCM tag\n
* Get after yaca_encrypt_finalize() / yaca_seal_finalize() in encryption / seal operation.\n
- * Set before yaca_decrypt_finalize() / yaca_open_finalize() in decryption / open operation.\n\n
+ * Set after yaca_decrypt_update() / yaca_open_update() and before
+ * yaca_decrypt_finalize() / yaca_open_finalize() in decryption / open operation.\n\n
*
* - #YACA_PROPERTY_GCM_AAD = additional authentication data (optional)\n
* Set after yaca_encrypt_initialize() / yaca_seal_initialize() and before
* yaca_decrypt_update() / yaca_open_update() in decryption / open operation.\n\n
*
* @see yaca_context_set_property()
+ * @see yaca_context_get_property()
+ *
*/
YACA_BCM_GCM,
* (recommended 56-bits).\n\n
*
* Supported properties:
- * - #YACA_PROPERTY_CCM_TAG_LEN = CCM tag length\n
- * Supported tag lengths: 4-16 bytes in steps of 2 bytes (recommended 12 bytes tag).\n
+ * - #YACA_PROPERTY_CCM_TAG_LEN = CCM tag length (optional)\n
+ * Supported tag lengths: 4-16 bytes in steps of 2 bytes (12 bytes tag by default).\n
* Set after yaca_encrypt_initialize() / yaca_seal_initialize() and before
* yaca_encrypt_update() / yaca_seal_update() in encryption / seal operation.\n
* The @a value should be a size_t variable.\n
* The total encrypted text length must be passed to yaca_decrypt_update() /
* yaca_open_update() if AAD is used.\n
* Set after yaca_decrypt_initialize() / yaca_open_initialize() and before
- * yaca_decrypt_update() / yaca_open_update() in decryption / open operation.\n\n
+ * yaca_decrypt_update() / yaca_open_update() in decryption / open operation.\n
+ * You can only call yaca_decrypt_update() / yaca_open_update() once for AAD
+ * and once for the encrypted text.\n\n
*
* @see yaca_context_set_property()
+ * @see yaca_context_get_property()
+ *
*/
YACA_BCM_CCM,
static const size_t VALID_GCM_TAG_LENGTHS_LENGTH =
sizeof(VALID_GCM_TAG_LENGTHS) / sizeof(VALID_GCM_TAG_LENGTHS[0]);
+static const size_t VALID_CCM_TAG_LENGTHS[] = { 4, 6, 8, 10, 12, 14, 16 };
+static const size_t VALID_CCM_TAG_LENGTHS_LENGTH =
+ sizeof(VALID_CCM_TAG_LENGTHS) / sizeof(VALID_CCM_TAG_LENGTHS[0]);
+
+static bool is_valid_tag_len(int mode, size_t tag_len)
+{
+ switch (mode) {
+ case EVP_CIPH_GCM_MODE:
+ for (size_t i = 0; i < VALID_GCM_TAG_LENGTHS_LENGTH; i++) {
+ if (tag_len == VALID_GCM_TAG_LENGTHS[i])
+ return true;
+ }
+ return false;
+ case EVP_CIPH_CCM_MODE:
+ for (size_t i = 0; i < VALID_CCM_TAG_LENGTHS_LENGTH; i++) {
+ if (tag_len == VALID_CCM_TAG_LENGTHS[i])
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
struct yaca_encrypt_context_s *get_encrypt_context(const yaca_context_h ctx)
{
if (ctx == YACA_CONTEXT_NULL)
assert(c->backup_ctx != NULL);
assert(is_encryption_op(c->op_type));
- if (tag_len == 0 || tag_len > INT_MAX)
- return YACA_ERROR_INVALID_PARAMETER;
-
ret = encrypt_ctx_restore(c);
if (ret != YACA_ERROR_NONE)
return ret;
assert(!is_encryption_op(c->op_type));
assert(tag != NULL);
- if (tag_len == 0 || tag_len > INT_MAX)
- return YACA_ERROR_INVALID_PARAMETER;
-
ret = encrypt_ctx_restore(c);
if (ret != YACA_ERROR_NONE)
return ret;
switch (property) {
case YACA_PROPERTY_GCM_AAD:
- if (mode != EVP_CIPH_GCM_MODE)
- return YACA_ERROR_INVALID_PARAMETER;
-
- if (!verify_state_change(c, STATE_AAD_UPDATED))
+ if (mode != EVP_CIPH_GCM_MODE ||
+ !verify_state_change(c, STATE_AAD_UPDATED))
return YACA_ERROR_INVALID_PARAMETER;
if (EVP_CipherUpdate(c->cipher_ctx, NULL, &len, value, value_len) != 1) {
c->state = STATE_AAD_UPDATED;
break;
case YACA_PROPERTY_CCM_AAD:
- if (mode != EVP_CIPH_CCM_MODE)
- return YACA_ERROR_INVALID_PARAMETER;
-
- if (!verify_state_change(c, STATE_AAD_UPDATED))
+ if (mode != EVP_CIPH_CCM_MODE ||
+ !verify_state_change(c, STATE_AAD_UPDATED))
return YACA_ERROR_INVALID_PARAMETER;
if (EVP_CipherUpdate(c->cipher_ctx, NULL, &len, value, value_len) != 1) {
break;
case YACA_PROPERTY_GCM_TAG:
if (mode != EVP_CIPH_GCM_MODE || is_encryption_op(c->op_type) ||
- value_len == 0 || value_len > INT_MAX)
- return YACA_ERROR_INVALID_PARAMETER;
-
- if (!verify_state_change(c, STATE_TAG_SET))
+ !is_valid_tag_len(mode, value_len) ||
+ !verify_state_change(c, STATE_TAG_SET))
return YACA_ERROR_INVALID_PARAMETER;
- if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
- EVP_CTRL_GCM_SET_TAG,
- value_len,
- (void*)value) != 1) {
+ if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx, EVP_CTRL_GCM_SET_TAG, value_len, (void*)value) != 1) {
ERROR_DUMP(YACA_ERROR_INTERNAL);
return YACA_ERROR_INTERNAL;
}
case YACA_PROPERTY_GCM_TAG_LEN:
if (value_len != sizeof(size_t) || mode != EVP_CIPH_GCM_MODE ||
!is_encryption_op(c->op_type) ||
- *(size_t*)value == 0 || *(size_t*)value > INT_MAX)
+ !is_valid_tag_len(mode, *(size_t*)value) ||
+ !verify_state_change(c, STATE_TAG_LENGTH_SET))
return YACA_ERROR_INVALID_PARAMETER;
- if (!verify_state_change(c, STATE_TAG_LENGTH_SET))
- return YACA_ERROR_INVALID_PARAMETER;
-
- /* check allowed tag lengths */
- {
- size_t tag_len = *(size_t*)value;
- for (size_t i = 0; i < VALID_GCM_TAG_LENGTHS_LENGTH; i++) {
- if (tag_len == VALID_GCM_TAG_LENGTHS[i]) {
- c->tag_len = tag_len;
- c->state = STATE_TAG_LENGTH_SET;
- return YACA_ERROR_NONE;
- }
- }
- return YACA_ERROR_INVALID_PARAMETER;
- }
+ c->tag_len = *(size_t*)value;
+ c->state = STATE_TAG_LENGTH_SET;
break;
case YACA_PROPERTY_CCM_TAG:
- if (mode != EVP_CIPH_CCM_MODE || is_encryption_op(c->op_type))
- return YACA_ERROR_INVALID_PARAMETER;
-
- if (!verify_state_change(c, STATE_TAG_SET))
+ if (mode != EVP_CIPH_CCM_MODE || is_encryption_op(c->op_type) ||
+ !is_valid_tag_len(mode, value_len) ||
+ !verify_state_change(c, STATE_TAG_SET))
return YACA_ERROR_INVALID_PARAMETER;
ret = encrypt_ctx_set_ccm_tag(c, (char*)value, value_len);
break;
case YACA_PROPERTY_CCM_TAG_LEN:
if (value_len != sizeof(size_t) || mode != EVP_CIPH_CCM_MODE ||
- !is_encryption_op(c->op_type))
- return YACA_ERROR_INVALID_PARAMETER;
-
- if (!verify_state_change(c, STATE_TAG_LENGTH_SET))
+ !is_encryption_op(c->op_type) ||
+ !is_valid_tag_len(mode, *(size_t*)value) ||
+ !verify_state_change(c, STATE_TAG_LENGTH_SET))
return YACA_ERROR_INVALID_PARAMETER;
ret = encrypt_ctx_set_ccm_tag_len(c, *(size_t*)value);
ret = EVP_CipherUpdate(c->cipher_ctx, output, &loutput_len, input, input_len);
if (ret != 1 || loutput_len < 0) {
- ret = YACA_ERROR_INTERNAL;
- ERROR_DUMP(ret);
+ if (mode == EVP_CIPH_CCM_MODE && op_type == OP_DECRYPT) {
+ /* A non positive return value from EVP_CipherUpdate should be considered as
+ * a failure to authenticate ciphertext and/or AAD.
+ * It does not necessarily indicate a more serious error.
+ * There is no call to EVP_CipherFinal.
+ */
+ ret = YACA_ERROR_INVALID_PARAMETER;
+ } else {
+ ret = YACA_ERROR_INTERNAL;
+ ERROR_DUMP(ret);
+ }
return ret;
}
{
struct yaca_encrypt_context_s *c = get_encrypt_context(ctx);
int ret;
+ int mode;
int loutput_len = 0;
if (c == NULL || output == NULL || output_len == NULL || op_type != c->op_type)
if (!verify_state_change(c, STATE_FINALIZED))
return YACA_ERROR_INVALID_PARAMETER;
- if (EVP_CIPHER_CTX_mode(c->cipher_ctx) != EVP_CIPH_WRAP_MODE) {
+ mode = EVP_CIPHER_CTX_mode(c->cipher_ctx);
+ if (mode != EVP_CIPH_WRAP_MODE && mode != EVP_CIPH_CCM_MODE) {
ret = EVP_CipherFinal(c->cipher_ctx, output, &loutput_len);
- if (ret != 1 || loutput_len < 0)
- return ERROR_HANDLE();
+ if (ret != 1 || loutput_len < 0) {
+ if (mode == EVP_CIPH_GCM_MODE && op_type == OP_DECRYPT)
+ /* A non positive return value from EVP_CipherFinal should be considered as
+ * a failure to authenticate ciphertext and/or AAD.
+ * It does not necessarily indicate a more serious error.
+ */
+ return YACA_ERROR_INVALID_PARAMETER;
+ else
+ return ERROR_HANDLE();
+ }
}
*output_len = loutput_len;