/* cipher-aeswrap.c - Generic AESWRAP mode implementation
* Copyright (C) 2009, 2011 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser general Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include "g10lib.h"
#include "cipher.h"
#include "bufhelp.h"
#include "./cipher-internal.h"
/* Perform the wrap algorithm W as specified by NIST SP 800-38F.
Cipher block size must be 128-bit. */
static gcry_err_code_t
wrap (gcry_cipher_hd_t c, byte *outbuf, size_t inbuflen)
{
int j, x;
size_t n, i;
unsigned char *r, *a, *b;
unsigned char t[8];
unsigned int burn, nburn;
#if MAX_BLOCKSIZE < 8
#error Invalid block size
#endif
/* We require a cipher with a 128 bit block length. */
if (c->spec->blocksize != 16)
return GPG_ERR_INV_LENGTH;
/* Input data must be multiple of 64 bits. */
if (inbuflen % 8)
return GPG_ERR_INV_ARG;
n = inbuflen / 8;
/* We need at least three 64 bit blocks. */
if (n < 3)
return GPG_ERR_INV_ARG;
burn = 0;
r = outbuf;
a = outbuf; /* We store A directly in OUTBUF. */
b = c->u_ctr.ctr; /* B is also used to concatenate stuff. */
memset (t, 0, sizeof t); /* t := 0. */
for (j = 0; j <= 5; j++)
{
for (i = 1; i < n; i++)
{
/* B := CIPH_k( A | R[i] ) */
memcpy (b, a, 8);
memcpy (b+8, r+i*8, 8);
nburn = c->spec->encrypt (&c->context.c, b, b);
burn = nburn > burn ? nburn : burn;
/* t := t + 1 */
for (x = 7; x >= 0; x--)
if (++t[x])
break;
/* A := MSB_64(B) ^ t */
cipher_block_xor (a, b, t, 8);
/* R[i] := LSB_64(B) */
memcpy (r+i*8, b+8, 8);
}
}
if (burn > 0)
_gcry_burn_stack (burn + 4 * sizeof(void *));
return 0;
}
/* Perform the Key Wrap algorithm as specified by RFC3394. We
implement this as a mode usable with any cipher algorithm of
blocksize 128. */
gcry_err_code_t
_gcry_cipher_keywrap_encrypt (gcry_cipher_hd_t c,
byte *outbuf, size_t outbuflen,
const byte *inbuf, size_t inbuflen)
{
gcry_err_code_t err;
unsigned char *r = outbuf;
/* We require a cipher with a 128 bit block length. */
if (c->spec->blocksize != 16)
return GPG_ERR_INV_LENGTH;
/* The output buffer must be able to hold the input data plus one
additional block. */
if (outbuflen < inbuflen + 8)
return GPG_ERR_BUFFER_TOO_SHORT;
/* Input data must be multiple of 64 bits. */
if (inbuflen % 8)
return GPG_ERR_INV_ARG;
/* We need at least two 64 bit blocks. */
if ((inbuflen / 8) < 2)
return GPG_ERR_INV_ARG;
/* Copy the inbuf to the outbuf. */
memmove (r+8, inbuf, inbuflen);
/* If an IV has been set we use that IV as the Alternative Initial
Value; if it has not been set we use the standard value. */
if (c->marks.iv)
memcpy (r, c->u_iv.iv, 8);
else
memset (r, 0xa6, 8);
err = wrap (c, r, inbuflen + 8);
return err;
}
static const unsigned char icv2[] = { 0xA6, 0x59, 0x59, 0xA6 };
/* Perform the Key Wrap algorithm as specified by RFC5649. */
gcry_err_code_t
_gcry_cipher_keywrap_encrypt_padding (gcry_cipher_hd_t c,
byte *outbuf, size_t outbuflen,
const byte *inbuf, size_t inbuflen)
{
gcry_err_code_t err;
unsigned char *r = outbuf;
unsigned int padlen;
/* We require a cipher with a 128 bit block length. */
if (c->spec->blocksize != 16)
return GPG_ERR_INV_LENGTH;
/* The output buffer must be able to hold the input data plus one
additional block and padding. */
if (outbuflen < ((inbuflen + 7)/8)*8 + 8)
return GPG_ERR_BUFFER_TOO_SHORT;
if (inbuflen % 8)
padlen = 8 - (inbuflen % 8);
else
padlen = 0;
memcpy (r, icv2, 4);
r[4] = ((inbuflen >> 24) & 0xff);
r[5] = ((inbuflen >> 16) & 0xff);
r[6] = ((inbuflen >> 8) & 0xff);
r[7] = (inbuflen & 0xff);
memcpy (r+8, inbuf, inbuflen);
if (padlen)
memset (r+8+inbuflen, 0, padlen);
if (inbuflen <= 8)
{
unsigned int burn;
burn = c->spec->encrypt (&c->context.c, r, r);
if (burn > 0)
_gcry_burn_stack (burn + 4 * sizeof(void *));
err = 0;
}
else
err = wrap (c, r, ((inbuflen + 7)/8)*8 + 8);
return err;
}
/* Perform the unwrap algorithm W^-1 as specified by NIST SP 800-38F.
Cipher block size must be 128-bit. */
static gcry_err_code_t
unwrap (gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf, size_t inbuflen)
{
int j, x;
size_t n, i;
unsigned char *r, *a, *b;
unsigned char t[8];
unsigned int burn, nburn;
#if MAX_BLOCKSIZE < 8
#error Invalid block size
#endif
/* We require a cipher with a 128 bit block length. */
if (c->spec->blocksize != 16)
return GPG_ERR_INV_LENGTH;
/* Input data must be multiple of 64 bits. */
if (inbuflen % 8)
return GPG_ERR_INV_ARG;
n = inbuflen / 8;
/* We need at least three 64 bit blocks. */
if (n < 3)
return GPG_ERR_INV_ARG;
burn = 0;
r = outbuf;
a = c->lastiv; /* We use c->LASTIV as buffer for A. */
b = c->u_ctr.ctr; /* B is also used to concatenate stuff. */
/* Copy the inbuf to the outbuf and save A. */
memcpy (a, inbuf, 8);
memmove (r, inbuf+8, inbuflen-8);
n--; /* Reduce to actual number of data blocks. */
/* t := 6 * n */
i = n * 6; /* The range is valid because: n = inbuflen / 8 - 1. */
for (x=0; x < 8 && x < sizeof (i); x++)
t[7-x] = i >> (8*x);
for (; x < 8; x++)
t[7-x] = 0;
for (j = 5; j >= 0; j--)
{
for (i = n; i >= 1; i--)
{
/* B := CIPH_k^-1( (A ^ t)| R[i] ) */
cipher_block_xor (b, a, t, 8);
memcpy (b+8, r+(i-1)*8, 8);
nburn = c->spec->decrypt (&c->context.c, b, b);
burn = nburn > burn ? nburn : burn;
/* t := t - 1 */
for (x = 7; x >= 0; x--)
if (--t[x] != 0xff)
break;
/* A := MSB_64(B) */
memcpy (a, b, 8);
/* R[i] := LSB_64(B) */
memcpy (r+(i-1)*8, b+8, 8);
}
}
wipememory (b, 16); /* Clear scratch area. */
if (burn > 0)
_gcry_burn_stack (burn + 4 * sizeof(void *));
return 0;
}
/* Perform the Key Unwrap algorithm as specified by RFC3394 and
RFC5649. */
gcry_err_code_t
_gcry_cipher_keywrap_decrypt_auto (gcry_cipher_hd_t c,
byte *outbuf, size_t outbuflen,
const byte *inbuf, size_t inbuflen)
{
gcry_err_code_t err;
/* We require a cipher with a 128 bit block length. */
if (c->spec->blocksize != 16)
return GPG_ERR_INV_LENGTH;
/* The output buffer must be able to hold the input data minus one
additional block. Fixme: The caller has more restrictive checks
- we may want to fix them for this mode. */
if (outbuflen + 8 < inbuflen)
return GPG_ERR_BUFFER_TOO_SHORT;
/* Input data must be multiple of 64 bits. */
if (inbuflen % 8)
return GPG_ERR_INV_ARG;
if (inbuflen == 16)
{
unsigned int burn;
unsigned char t[16];
if (!(c->flags & GCRY_CIPHER_EXTENDED))
return GPG_ERR_BUFFER_TOO_SHORT;
burn = c->spec->decrypt (&c->context.c, t, inbuf);
if (burn > 0)
_gcry_burn_stack (burn + 4 * sizeof(void *));
if (memcmp (t, icv2, 4))
err = GPG_ERR_CHECKSUM;
else
{
unsigned int plen = (t[4]<<24) | (t[5]<<16) | (t[6]<<8) | t[7];
err = 0;
if (plen > 8)
err = GPG_ERR_CHECKSUM;
else if (plen)
{
int i;
for (i = 0; i < 16 - (8+plen); i++)
if (t[8+plen+i])
{
err = GPG_ERR_CHECKSUM;
break;
}
if (!err)
{
memcpy (outbuf, t+8, 8);
memcpy (c->u_mode.wrap.plen, t+4, 4);
}
}
}
}
else
{
/* We need at least three 64 bit blocks. */
if ((inbuflen / 8) < 3)
return GPG_ERR_INV_ARG;
err = unwrap (c, outbuf, inbuf, inbuflen);
if (!err)
{
unsigned char *a;
a = c->lastiv; /* We use c->LASTIV as buffer for A. */
/* If an IV has been set we compare against this Alternative Initial
Value; if it has not been set we compare against the standard IV. */
if (c->marks.iv && !memcmp (a, c->u_iv.iv, 8))
memset (c->u_mode.wrap.plen, 0, 4);
else if (!memcmp (a, icv2, 4)) /* It's a packet wrapped by KWP. */
{
unsigned int plen = (a[4]<<24) | (a[5]<<16) | (a[6]<<8) | a[7];
int padlen = inbuflen - 8 - plen;
if (!(c->flags & GCRY_CIPHER_EXTENDED))
err = GPG_ERR_CHECKSUM;
else if (padlen < 0 || padlen > 7)
err = GPG_ERR_CHECKSUM;
else if (padlen)
{
int i;
for (i = 0; i < padlen; i++)
if (outbuf[plen+i])
{
err = GPG_ERR_CHECKSUM;
break;
}
}
if (!err)
memcpy (c->u_mode.wrap.plen, a+4, 4);
}
else /* It's a packet wrapped by KW. */
{
int i;
for (i = 0; i < 8; i++)
if (a[i] != 0xa6)
{
err = GPG_ERR_CHECKSUM;
break;
}
if (!err)
memset (c->u_mode.wrap.plen, 0, 4);
}
}
}
return err;
}