Annotation of embedaddon/strongswan/src/libstrongswan/plugins/chapoly/chapoly_aead.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2015 Martin Willi
! 3: * Copyright (C) 2015 revosec AG
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #include "chapoly_aead.h"
! 17: #include "chapoly_drv.h"
! 18:
! 19: #include <crypto/iv/iv_gen_seq.h>
! 20:
! 21: /* maximum plain message size */
! 22: #define P_MAX 247877906880
! 23:
! 24: typedef struct private_chapoly_aead_t private_chapoly_aead_t;
! 25:
! 26: /**
! 27: * Private data of an chapoly_aead_t object.
! 28: */
! 29: struct private_chapoly_aead_t {
! 30:
! 31: /**
! 32: * Public chapoly_aead_t interface.
! 33: */
! 34: chapoly_aead_t public;
! 35:
! 36: /**
! 37: * IV generator.
! 38: */
! 39: iv_gen_t *iv_gen;
! 40:
! 41: /**
! 42: * Driver backend
! 43: */
! 44: chapoly_drv_t *drv;
! 45: };
! 46:
! 47: /**
! 48: * Include a partial block to ICV by padding it with zero bytes
! 49: */
! 50: static bool poly_update_padded(private_chapoly_aead_t *this,
! 51: u_char *in, size_t len)
! 52: {
! 53: u_char b[POLY_BLOCK_SIZE];
! 54:
! 55: memset(b, 0, sizeof(b));
! 56: memcpy(b, in, len);
! 57:
! 58: return this->drv->poly(this->drv, b, 1);
! 59: }
! 60:
! 61: /**
! 62: * Include associated data with padding to ICV
! 63: */
! 64: static bool poly_head(private_chapoly_aead_t *this, u_char *assoc, size_t len)
! 65: {
! 66: u_int blocks, rem;
! 67:
! 68: blocks = len / POLY_BLOCK_SIZE;
! 69: rem = len % POLY_BLOCK_SIZE;
! 70: if (!this->drv->poly(this->drv, assoc, blocks))
! 71: {
! 72: return FALSE;
! 73: }
! 74: if (rem)
! 75: {
! 76: return poly_update_padded(this, assoc + blocks * POLY_BLOCK_SIZE, rem);
! 77: }
! 78: return TRUE;
! 79: }
! 80:
! 81: /**
! 82: * Include length fields to ICV
! 83: */
! 84: static bool poly_tail(private_chapoly_aead_t *this, size_t alen, size_t clen)
! 85: {
! 86: struct {
! 87: uint64_t alen;
! 88: uint64_t clen;
! 89: } b;
! 90:
! 91: b.alen = htole64(alen);
! 92: b.clen = htole64(clen);
! 93:
! 94: return this->drv->poly(this->drv, (u_char*)&b, 1);
! 95: }
! 96:
! 97: /**
! 98: * Perform ChaCha20 encryption inline and generate an ICV tag
! 99: */
! 100: static bool do_encrypt(private_chapoly_aead_t *this, size_t len, u_char *data,
! 101: u_char *iv, size_t alen, u_char *assoc, u_char *icv)
! 102: {
! 103: u_int blocks, rem, prem;
! 104:
! 105: if (!this->drv->init(this->drv, iv) ||
! 106: !poly_head(this, assoc, alen))
! 107: {
! 108: return FALSE;
! 109: }
! 110: blocks = len / CHACHA_BLOCK_SIZE;
! 111: if (!this->drv->encrypt(this->drv, data, blocks))
! 112: {
! 113: return FALSE;
! 114: }
! 115: rem = len % CHACHA_BLOCK_SIZE;
! 116: if (rem)
! 117: {
! 118: u_char stream[CHACHA_BLOCK_SIZE];
! 119:
! 120: data += blocks * CHACHA_BLOCK_SIZE;
! 121: if (!this->drv->chacha(this->drv, stream))
! 122: {
! 123: return FALSE;
! 124: }
! 125: memxor(data, stream, rem);
! 126:
! 127: blocks = rem / POLY_BLOCK_SIZE;
! 128: if (!this->drv->poly(this->drv, data, blocks))
! 129: {
! 130: return FALSE;
! 131: }
! 132: prem = rem % POLY_BLOCK_SIZE;
! 133: if (prem)
! 134: {
! 135: poly_update_padded(this, data + blocks * POLY_BLOCK_SIZE, prem);
! 136: }
! 137: }
! 138: return poly_tail(this, alen, len) &&
! 139: this->drv->finish(this->drv, icv);
! 140: }
! 141:
! 142: /**
! 143: * Perform ChaCha20 decryption inline and generate an ICV tag
! 144: */
! 145: static bool do_decrypt(private_chapoly_aead_t *this, size_t len, u_char *data,
! 146: u_char *iv, size_t alen, u_char *assoc, u_char *icv)
! 147: {
! 148: u_int blocks, rem, prem;
! 149:
! 150: if (!this->drv->init(this->drv, iv) ||
! 151: !poly_head(this, assoc, alen))
! 152: {
! 153: return FALSE;
! 154: }
! 155: blocks = len / CHACHA_BLOCK_SIZE;
! 156: if (!this->drv->decrypt(this->drv, data, blocks))
! 157: {
! 158: return FALSE;
! 159: }
! 160: rem = len % CHACHA_BLOCK_SIZE;
! 161: if (rem)
! 162: {
! 163: u_char stream[CHACHA_BLOCK_SIZE];
! 164:
! 165: data += blocks * CHACHA_BLOCK_SIZE;
! 166:
! 167: blocks = rem / POLY_BLOCK_SIZE;
! 168: if (!this->drv->poly(this->drv, data, blocks))
! 169: {
! 170: return FALSE;
! 171: }
! 172: prem = rem % POLY_BLOCK_SIZE;
! 173: if (prem)
! 174: {
! 175: poly_update_padded(this, data + blocks * POLY_BLOCK_SIZE, prem);
! 176: }
! 177: if (!this->drv->chacha(this->drv, stream))
! 178: {
! 179: return FALSE;
! 180: }
! 181: memxor(data, stream, rem);
! 182: }
! 183: return poly_tail(this, alen, len) &&
! 184: this->drv->finish(this->drv, icv);
! 185: }
! 186:
! 187: METHOD(aead_t, encrypt, bool,
! 188: private_chapoly_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
! 189: chunk_t *encr)
! 190: {
! 191: u_char *out;
! 192:
! 193: if (sizeof(plain.len) > sizeof(uint32_t) && plain.len > P_MAX)
! 194: {
! 195: return FALSE;
! 196: }
! 197: if (iv.len != CHACHA_IV_SIZE)
! 198: {
! 199: return FALSE;
! 200: }
! 201: out = plain.ptr;
! 202: if (encr)
! 203: {
! 204: *encr = chunk_alloc(plain.len + POLY_ICV_SIZE);
! 205: out = encr->ptr;
! 206: memcpy(out, plain.ptr, plain.len);
! 207: }
! 208: do_encrypt(this, plain.len, out, iv.ptr, assoc.len, assoc.ptr,
! 209: out + plain.len);
! 210: return TRUE;
! 211: }
! 212:
! 213: METHOD(aead_t, decrypt, bool,
! 214: private_chapoly_aead_t *this, chunk_t encr, chunk_t assoc, chunk_t iv,
! 215: chunk_t *plain)
! 216: {
! 217: u_char *out, icv[POLY_ICV_SIZE];
! 218: if (iv.len != CHACHA_IV_SIZE || encr.len < POLY_ICV_SIZE)
! 219: {
! 220: return FALSE;
! 221: }
! 222: encr.len -= POLY_ICV_SIZE;
! 223: if (sizeof(encr.len) > sizeof(uint32_t) && encr.len > P_MAX)
! 224: {
! 225: return FALSE;
! 226: }
! 227: out = encr.ptr;
! 228: if (plain)
! 229: {
! 230: *plain = chunk_alloc(encr.len);
! 231: out = plain->ptr;
! 232: memcpy(out, encr.ptr, encr.len);
! 233: }
! 234: do_decrypt(this, encr.len, out, iv.ptr, assoc.len, assoc.ptr, icv);
! 235: return memeq_const(icv, encr.ptr + encr.len, POLY_ICV_SIZE);
! 236: }
! 237:
! 238: METHOD(aead_t, get_block_size, size_t,
! 239: private_chapoly_aead_t *this)
! 240: {
! 241: return 1;
! 242: }
! 243:
! 244: METHOD(aead_t, get_icv_size, size_t,
! 245: private_chapoly_aead_t *this)
! 246: {
! 247: return POLY_ICV_SIZE;
! 248: }
! 249:
! 250: METHOD(aead_t, get_iv_size, size_t,
! 251: private_chapoly_aead_t *this)
! 252: {
! 253: return CHACHA_IV_SIZE;
! 254: }
! 255:
! 256: METHOD(aead_t, get_iv_gen, iv_gen_t*,
! 257: private_chapoly_aead_t *this)
! 258: {
! 259: return this->iv_gen;
! 260: }
! 261:
! 262: METHOD(aead_t, get_key_size, size_t,
! 263: private_chapoly_aead_t *this)
! 264: {
! 265: return CHACHA_KEY_SIZE + CHACHA_SALT_SIZE;
! 266: }
! 267:
! 268: METHOD(aead_t, set_key, bool,
! 269: private_chapoly_aead_t *this, chunk_t key)
! 270: {
! 271: if (key.len != CHACHA_KEY_SIZE + CHACHA_SALT_SIZE)
! 272: {
! 273: return FALSE;
! 274: }
! 275: return this->drv->set_key(this->drv, "expand 32-byte k",
! 276: key.ptr, key.ptr + CHACHA_KEY_SIZE);
! 277: }
! 278:
! 279: METHOD(aead_t, destroy, void,
! 280: private_chapoly_aead_t *this)
! 281: {
! 282: this->drv->destroy(this->drv);
! 283: this->iv_gen->destroy(this->iv_gen);
! 284: free(this);
! 285: }
! 286:
! 287: /**
! 288: * See header
! 289: */
! 290: chapoly_aead_t *chapoly_aead_create(encryption_algorithm_t algo,
! 291: size_t key_size, size_t salt_size)
! 292: {
! 293: private_chapoly_aead_t *this;
! 294: chapoly_drv_t *drv;
! 295:
! 296: if (algo != ENCR_CHACHA20_POLY1305)
! 297: {
! 298: return NULL;
! 299: }
! 300: if (key_size && key_size != CHACHA_KEY_SIZE)
! 301: {
! 302: return NULL;
! 303: }
! 304: if (salt_size && salt_size != CHACHA_SALT_SIZE)
! 305: {
! 306: return NULL;
! 307: }
! 308: drv = chapoly_drv_probe();
! 309: if (!drv)
! 310: {
! 311: return NULL;
! 312: }
! 313:
! 314: INIT(this,
! 315: .public = {
! 316: .aead = {
! 317: .encrypt = _encrypt,
! 318: .decrypt = _decrypt,
! 319: .get_block_size = _get_block_size,
! 320: .get_icv_size = _get_icv_size,
! 321: .get_iv_size = _get_iv_size,
! 322: .get_iv_gen = _get_iv_gen,
! 323: .get_key_size = _get_key_size,
! 324: .set_key = _set_key,
! 325: .destroy = _destroy,
! 326: },
! 327: },
! 328: .iv_gen = iv_gen_seq_create(),
! 329: .drv = drv,
! 330: );
! 331:
! 332: return &this->public;
! 333: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>