gcry_err_code_t _gcry_cipher_cfb_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf, size_t outbuflen, const unsigned char *inbuf, size_t inbuflen) { unsigned char *ivp; gcry_cipher_encrypt_t enc_fn = c->spec->encrypt; size_t blocksize = c->spec->blocksize; size_t blocksize_x_2 = blocksize + blocksize; unsigned int burn, nburn; /* Tell compiler that we require a cipher with a 64bit or 128 bit block * length, to allow better optimization of this function. */ if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1)) return GPG_ERR_INV_LENGTH; if (outbuflen < inbuflen) return GPG_ERR_BUFFER_TOO_SHORT; if ( inbuflen <= c->unused ) { /* Short enough to be encoded by the remaining XOR mask. */ /* XOR the input with the IV and store input into IV. */ ivp = c->u_iv.iv + blocksize - c->unused; buf_xor_2dst(outbuf, ivp, inbuf, inbuflen); c->unused -= inbuflen; return 0; } burn = 0; if ( c->unused ) { /* XOR the input with the IV and store input into IV */ inbuflen -= c->unused; ivp = c->u_iv.iv + blocksize - c->unused; buf_xor_2dst(outbuf, ivp, inbuf, c->unused); outbuf += c->unused; inbuf += c->unused; c->unused = 0; } /* Now we can process complete blocks. We use a loop as long as we have at least 2 blocks and use conditions for the rest. This also allows to use a bulk encryption function if available. */ if (inbuflen >= blocksize_x_2 && c->bulk.cfb_enc) { size_t nblocks = inbuflen / blocksize; c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); outbuf += nblocks * blocksize; inbuf += nblocks * blocksize; inbuflen -= nblocks * blocksize; } else { while ( inbuflen >= blocksize_x_2 ) { /* Encrypt the IV. */ nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); burn = nburn > burn ? nburn : burn; /* XOR the input with the IV and store input into IV. */ buf_xor_2dst(outbuf, c->u_iv.iv, inbuf, blocksize); outbuf += blocksize; inbuf += blocksize; inbuflen -= blocksize; } } if ( inbuflen >= blocksize ) { /* Save the current IV and then encrypt the IV. */ buf_cpy( c->lastiv, c->u_iv.iv, blocksize ); nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); burn = nburn > burn ? nburn : burn; /* XOR the input with the IV and store input into IV */ buf_xor_2dst(outbuf, c->u_iv.iv, inbuf, blocksize); outbuf += blocksize; inbuf += blocksize; inbuflen -= blocksize; } if ( inbuflen ) { /* Save the current IV and then encrypt the IV. */ buf_cpy( c->lastiv, c->u_iv.iv, blocksize ); nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); burn = nburn > burn ? nburn : burn; c->unused = blocksize; /* Apply the XOR. */ c->unused -= inbuflen; buf_xor_2dst(outbuf, c->u_iv.iv, inbuf, inbuflen); outbuf += inbuflen; inbuf += inbuflen; inbuflen = 0; } if (burn > 0) _gcry_burn_stack (burn + 4 * sizeof(void *)); return 0; }
/* Run the self-tests for <block cipher>-CFB-<block size>, tests bulk CFB decryption. Returns NULL on success. */ const char * _gcry_selftest_helper_cfb (const char *cipher, gcry_cipher_setkey_t setkey_func, gcry_cipher_encrypt_t encrypt_one, gcry_cipher_bulk_cfb_dec_t bulk_cfb_dec, const int nblocks, const int blocksize, const int context_size) { int i, offs; unsigned char *ctx, *plaintext, *plaintext2, *ciphertext, *iv, *iv2, *mem; unsigned int ctx_aligned_size, memsize; // blad3master: change alignment to MSVC specific __declspec(align(16)) static const unsigned char key[16] = { 0x11,0x9A,0x00,0x7F,0xC7,0x6A,0x45,0x9F, 0x98,0xBA,0xF9,0x17,0xFE,0xDF,0x95,0x33 }; /* Allocate buffers, align first two elements to 16 bytes and latter to block size. */ ctx_aligned_size = context_size + 15; ctx_aligned_size -= ctx_aligned_size & 0xf; memsize = ctx_aligned_size + (blocksize * 2) + (blocksize * nblocks * 3) + 16; mem = xtrycalloc (1, memsize); if (!mem) return "failed to allocate memory"; offs = (16 - ((uintptr_t)mem & 15)) & 15; ctx = (void*)(mem + offs); iv = ctx + ctx_aligned_size; iv2 = iv + blocksize; plaintext = iv2 + blocksize; plaintext2 = plaintext + nblocks * blocksize; ciphertext = plaintext2 + nblocks * blocksize; /* Initialize ctx */ setkey_func (ctx, key, sizeof(key)); /* Test single block code path */ memset(iv, 0xd3, blocksize); memset(iv2, 0xd3, blocksize); for (i = 0; i < blocksize; i++) plaintext[i] = i; /* CFB manually. */ encrypt_one (ctx, ciphertext, iv); buf_xor_2dst (iv, ciphertext, plaintext, blocksize); /* CFB decrypt. */ bulk_cfb_dec (ctx, iv2, plaintext2, ciphertext, 1); if (memcmp(plaintext2, plaintext, blocksize)) { xfree(mem); #ifdef HAVE_SYSLOG syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: " "%s-CFB-%d test failed (plaintext mismatch)", cipher, blocksize * 8); #endif return "selftest for CFB failed - see syslog for details"; } if (memcmp(iv2, iv, blocksize)) { xfree(mem); #ifdef HAVE_SYSLOG syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: " "%s-CFB-%d test failed (IV mismatch)", cipher, blocksize * 8); #endif return "selftest for CFB failed - see syslog for details"; } /* Test parallelized code paths */ memset(iv, 0xe6, blocksize); memset(iv2, 0xe6, blocksize); for (i = 0; i < nblocks * blocksize; i++) plaintext[i] = i; /* Create CFB ciphertext manually. */ for (i = 0; i < nblocks * blocksize; i+=blocksize) { encrypt_one (ctx, &ciphertext[i], iv); buf_xor_2dst (iv, &ciphertext[i], &plaintext[i], blocksize); } /* Decrypt using bulk CBC and compare result. */ bulk_cfb_dec (ctx, iv2, plaintext2, ciphertext, nblocks); if (memcmp(plaintext2, plaintext, nblocks * blocksize)) { xfree(mem); #ifdef HAVE_SYSLOG syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: " "%s-CFB-%d test failed (plaintext mismatch, parallel path)", cipher, blocksize * 8); #endif return "selftest for CFB failed - see syslog for details"; } if (memcmp(iv2, iv, blocksize)) { xfree(mem); #ifdef HAVE_SYSLOG syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: " "%s-CFB-%d test failed (IV mismatch, parallel path)", cipher, blocksize * 8); #endif return "selftest for CFB failed - see syslog for details"; } xfree(mem); return NULL; }