/* $OpenBSD: cts128.c,v 1.5 2015/07/19 18:27:26 miod Exp $ */ /* ==================================================================== * Copyright (c) 2008 The OpenSSL Project. All rights reserved. * * Rights for redistribution and usage in source and binary * forms are granted according to the OpenSSL license. */ #include <openssl/crypto.h> #include "modes_lcl.h" #include <string.h> #ifndef MODES_DEBUG # ifndef NDEBUG # define NDEBUG # endif #endif /* * Trouble with Ciphertext Stealing, CTS, mode is that there is no * common official specification, but couple of cipher/application * specific ones: RFC2040 and RFC3962. Then there is 'Proposal to * Extend CBC Mode By "Ciphertext Stealing"' at NIST site, which * deviates from mentioned RFCs. Most notably it allows input to be * of block length and it doesn't flip the order of the last two * blocks. CTS is being discussed even in ECB context, but it's not * adopted for any known application. This implementation provides * two interfaces: one compliant with above mentioned RFCs and one * compliant with the NIST proposal, both extending CBC mode. */ size_t CRYPTO_cts128_encrypt_block(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], block128_f block) { size_t residue, n; if (len <= 16) return 0; if ((residue=len%16) == 0) residue = 16; len -= residue; CRYPTO_cbc128_encrypt(in,out,len,key,ivec,block); in += len; out += len; for (n=0; n<residue; ++n) ivec[n] ^= in[n]; (*block)(ivec,ivec,key); memcpy(out,out-16,residue); memcpy(out-16,ivec,16); return len+residue; } size_t CRYPTO_nistcts128_encrypt_block(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], block128_f block) { size_t residue, n; if (len < 16) return 0; residue=len%16; len -= residue; CRYPTO_cbc128_encrypt(in,out,len,key,ivec,block); if (residue==0) return len; in += len; out += len; for (n=0; n<residue; ++n) ivec[n] ^= in[n]; (*block)(ivec,ivec,key); memcpy(out-16+residue,ivec,16); return len+residue; } size_t CRYPTO_cts128_encrypt(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], cbc128_f cbc) { size_t residue; union { size_t align; unsigned char c[16]; } tmp; if (len <= 16) return 0; if ((residue=len%16) == 0) residue = 16; len -= residue; (*cbc)(in,out,len,key,ivec,1); in += len; out += len; memset(tmp.c,0,sizeof(tmp)); memcpy(tmp.c,in,residue); memcpy(out,out-16,residue); (*cbc)(tmp.c,out-16,16,key,ivec,1); return len+residue; } size_t CRYPTO_nistcts128_encrypt(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], cbc128_f cbc) { size_t residue; union { size_t align; unsigned char c[16]; } tmp; if (len < 16) return 0; residue=len%16; len -= residue; (*cbc)(in,out,len,key,ivec,1); if (residue==0) return len; in += len; out += len; memset(tmp.c,0,sizeof(tmp)); memcpy(tmp.c,in,residue); (*cbc)(tmp.c,out-16+residue,16,key,ivec,1); return len+residue; } size_t CRYPTO_cts128_decrypt_block(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], block128_f block) { size_t residue, n; union { size_t align; unsigned char c[32]; } tmp; if (len<=16) return 0; if ((residue=len%16) == 0) residue = 16; len -= 16+residue; if (len) { CRYPTO_cbc128_decrypt(in,out,len,key,ivec,block); in += len; out += len; } (*block)(in,tmp.c+16,key); memcpy(tmp.c,tmp.c+16,16); memcpy(tmp.c,in+16,residue); (*block)(tmp.c,tmp.c,key); for(n=0; n<16; ++n) { unsigned char c = in[n]; out[n] = tmp.c[n] ^ ivec[n]; ivec[n] = c; } for(residue+=16; n<residue; ++n) out[n] = tmp.c[n] ^ in[n]; return 16+len+residue; } size_t CRYPTO_nistcts128_decrypt_block(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], block128_f block) { size_t residue, n; union { size_t align; unsigned char c[32]; } tmp; if (len<16) return 0; residue=len%16; if (residue==0) { CRYPTO_cbc128_decrypt(in,out,len,key,ivec,block); return len; } len -= 16+residue; if (len) { CRYPTO_cbc128_decrypt(in,out,len,key,ivec,block); in += len; out += len; } (*block)(in+residue,tmp.c+16,key); memcpy(tmp.c,tmp.c+16,16); memcpy(tmp.c,in,residue); (*block)(tmp.c,tmp.c,key); for(n=0; n<16; ++n) { unsigned char c = in[n]; out[n] = tmp.c[n] ^ ivec[n]; ivec[n] = in[n+residue]; tmp.c[n] = c; } for(residue+=16; n<residue; ++n) out[n] = tmp.c[n] ^ tmp.c[n-16]; return 16+len+residue; } size_t CRYPTO_cts128_decrypt(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], cbc128_f cbc) { size_t residue; union { size_t align; unsigned char c[32]; } tmp; if (len<=16) return 0; if ((residue=len%16) == 0) residue = 16; len -= 16+residue; if (len) { (*cbc)(in,out,len,key,ivec,0); in += len; out += len; } memset(tmp.c,0,sizeof(tmp)); /* this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0] */ (*cbc)(in,tmp.c,16,key,tmp.c+16,0); memcpy(tmp.c,in+16,residue); (*cbc)(tmp.c,tmp.c,32,key,ivec,0); memcpy(out,tmp.c,16+residue); return 16+len+residue; } size_t CRYPTO_nistcts128_decrypt(const unsigned char *in, unsigned char *out, size_t len, const void *key, unsigned char ivec[16], cbc128_f cbc) { size_t residue; union { size_t align; unsigned char c[32]; } tmp; if (len<16) return 0; residue=len%16; if (residue==0) { (*cbc)(in,out,len,key,ivec,0); return len; } len -= 16+residue; if (len) { (*cbc)(in,out,len,key,ivec,0); in += len; out += len; } memset(tmp.c,0,sizeof(tmp)); /* this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0] */ (*cbc)(in+residue,tmp.c,16,key,tmp.c+16,0); memcpy(tmp.c,in,residue); (*cbc)(tmp.c,tmp.c,32,key,ivec,0); memcpy(out,tmp.c,16+residue); return 16+len+residue; }