diff options
author | Jussi Kivilinna <jussi.kivilinna@iki.fi> | 2016-03-27 11:17:39 +0300 |
---|---|---|
committer | Jussi Kivilinna <jussi.kivilinna@iki.fi> | 2016-03-27 11:17:39 +0300 |
commit | f2260e3a2e962ac80124ef938e54041bbea08561 (patch) | |
tree | cc3443018415bac407e62570e7c47b375a6ec3be /cipher/cipher-gcm.c | |
parent | 4a064e2a06fe737f344d1dfd8a45cc4c2abbe4c9 (diff) | |
download | libgcrypt-f2260e3a2e962ac80124ef938e54041bbea08561.tar.gz |
cipher: GCM: check that length of supplied tag is one of valid lengths
* cipher/cipher-gcm.c (is_tag_length_valid): New.
(_gcry_cipher_gcm_tag): Check that 'outbuflen' has valid tag length.
* tests/basic.c (_check_gcm_cipher): Add test-vectors with different
valid tag lengths and negative test vectors with invalid lengths.
--
NIST SP 800-38D allows following tag lengths:
128, 120, 112, 104, 96, 64 and 32 bits.
[v2: allow larger buffer when outputting tag. 128-bit tag is written
to target buffer in this case]
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
Diffstat (limited to 'cipher/cipher-gcm.c')
-rw-r--r-- | cipher/cipher-gcm.c | 36 |
1 files changed, 29 insertions, 7 deletions
diff --git a/cipher/cipher-gcm.c b/cipher/cipher-gcm.c index 712641e8..6e0959ad 100644 --- a/cipher/cipher-gcm.c +++ b/cipher/cipher-gcm.c @@ -769,12 +769,32 @@ _gcry_cipher_gcm_geniv (gcry_cipher_hd_t c, #endif +static int +is_tag_length_valid(size_t taglen) +{ + switch (taglen) + { + /* Allowed tag lengths from NIST SP 800-38D. */ + case 128 / 8: /* GCRY_GCM_BLOCK_LEN */ + case 120 / 8: + case 112 / 8: + case 104 / 8: + case 96 / 8: + case 64 / 8: + case 32 / 8: + return 1; + + default: + return 0; + } +} + static gcry_err_code_t _gcry_cipher_gcm_tag (gcry_cipher_hd_t c, byte * outbuf, size_t outbuflen, int check) { - if (outbuflen < GCRY_GCM_BLOCK_LEN) - return GPG_ERR_BUFFER_TOO_SHORT; + if (!(is_tag_length_valid (outbuflen) || outbuflen >= GCRY_GCM_BLOCK_LEN)) + return GPG_ERR_INV_LENGTH; if (c->u_mode.gcm.datalen_over_limits) return GPG_ERR_INV_LENGTH; @@ -815,17 +835,19 @@ _gcry_cipher_gcm_tag (gcry_cipher_hd_t c, if (!check) { + if (outbuflen > GCRY_GCM_BLOCK_LEN) + outbuflen = GCRY_GCM_BLOCK_LEN; + /* NB: We already checked that OUTBUF is large enough to hold - the result. */ - memcpy (outbuf, c->u_mode.gcm.u_tag.tag, GCRY_GCM_BLOCK_LEN); + * the result or has valid truncated length. */ + memcpy (outbuf, c->u_mode.gcm.u_tag.tag, outbuflen); } else { /* OUTBUFLEN gives the length of the user supplied tag in OUTBUF * and thus we need to compare its length first. */ - if (outbuflen != GCRY_GCM_BLOCK_LEN - || !buf_eq_const (outbuf, c->u_mode.gcm.u_tag.tag, - GCRY_GCM_BLOCK_LEN)) + if (!is_tag_length_valid (outbuflen) + || !buf_eq_const (outbuf, c->u_mode.gcm.u_tag.tag, outbuflen)) return GPG_ERR_CHECKSUM; } |