268 lines
5.9 KiB
C
268 lines
5.9 KiB
C
|
/* $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;
|
||
|
}
|