/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/crypto/builtin/des/f_cbc.c */
/*
* Copyright (C) 1990 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
/*
* CBC functions; used only by the test programs at this time. (krb5 uses the
* functions in f_aead.c instead.)
*/
/*
* des_cbc_encrypt.c - an implementation of the DES cipher function in cbc mode
*/
#include "des_int.h"
#include "f_tables.h"
/*
* des_cbc_encrypt - {en,de}crypt a stream in CBC mode
*/
/*
* This routine performs DES cipher-block-chaining operation, either
* encrypting from cleartext to ciphertext, if encrypt != 0 or
* decrypting from ciphertext to cleartext, if encrypt == 0.
*
* The key schedule is passed as an arg, as well as the cleartext or
* ciphertext. The cleartext and ciphertext should be in host order.
*
* NOTE-- the output is ALWAYS an multiple of 8 bytes long. If not
* enough space was provided, your program will get trashed.
*
* For encryption, the cleartext string is null padded, at the end, to
* an integral multiple of eight bytes.
*
* For decryption, the ciphertext will be used in integral multiples
* of 8 bytes, but only the first "length" bytes returned into the
* cleartext.
*/
const mit_des_cblock mit_des_zeroblock /* = all zero */;
static void
des_cbc_encrypt(const mit_des_cblock *in, mit_des_cblock *out,
unsigned long length, const mit_des_key_schedule schedule,
const mit_des_cblock ivec)
{
unsigned DES_INT32 left, right;
const unsigned DES_INT32 *kp;
const unsigned char *ip;
unsigned char *op;
/*
* Get key pointer here. This won't need to be reinitialized
*/
kp = (const unsigned DES_INT32 *)schedule;
/*
* Initialize left and right with the contents of the initial
* vector.
*/
ip = ivec;
GET_HALF_BLOCK(left, ip);
GET_HALF_BLOCK(right, ip);
/*
* Suitably initialized, now work the length down 8 bytes
* at a time.
*/
ip = *in;
op = *out;
while (length > 0) {
/*
* Get more input, xor it in. If the length is
* greater than or equal to 8 this is straight
* forward. Otherwise we have to fart around.
*/
if (length >= 8) {
unsigned DES_INT32 temp;
GET_HALF_BLOCK(temp, ip);
left ^= temp;
GET_HALF_BLOCK(temp, ip);
right ^= temp;
length -= 8;
} else {
/*
* Oh, shoot. We need to pad the
* end with zeroes. Work backwards
* to do this.
*/
ip += (int) length;
switch(length) {
case 7:
right ^= (*(--ip) & FF_UINT32) << 8;
case 6:
right ^= (*(--ip) & FF_UINT32) << 16;
case 5:
right ^= (*(--ip) & FF_UINT32) << 24;
case 4:
left ^= *(--ip) & FF_UINT32;
case 3:
left ^= (*(--ip) & FF_UINT32) << 8;
case 2:
left ^= (*(--ip) & FF_UINT32) << 16;
case 1:
left ^= (*(--ip) & FF_UINT32) << 24;
break;
}
length = 0;
}
/*
* Encrypt what we have
*/
DES_DO_ENCRYPT(left, right, kp);
/*
* Copy the results out
*/
PUT_HALF_BLOCK(left, op);
PUT_HALF_BLOCK(right, op);
}
}
static void
des_cbc_decrypt(const mit_des_cblock *in, mit_des_cblock *out,
unsigned long length, const mit_des_key_schedule schedule,
const mit_des_cblock ivec)
{
unsigned DES_INT32 left, right;
const unsigned DES_INT32 *kp;
const unsigned char *ip;
unsigned char *op;
unsigned DES_INT32 ocipherl, ocipherr;
unsigned DES_INT32 cipherl, cipherr;
/*
* Get key pointer here. This won't need to be reinitialized
*/
kp = (const unsigned DES_INT32 *)schedule;
/*
* Decrypting is harder than encrypting because of
* the necessity of remembering a lot more things.
* Should think about this a little more...
*/
if (length <= 0)
return;
/*
* Prime the old cipher with ivec.
*/
ip = ivec;
GET_HALF_BLOCK(ocipherl, ip);
GET_HALF_BLOCK(ocipherr, ip);
/*
* Now do this in earnest until we run out of length.
*/
ip = *in;
op = *out;
for (;;) { /* check done inside loop */
/*
* Read a block from the input into left and
* right. Save this cipher block for later.
*/
GET_HALF_BLOCK(left, ip);
GET_HALF_BLOCK(right, ip);
cipherl = left;
cipherr = right;
/*
* Decrypt this.
*/
DES_DO_DECRYPT(left, right, kp);
/*
* Xor with the old cipher to get plain
* text. Output 8 or less bytes of this.
*/
left ^= ocipherl;
right ^= ocipherr;
if (length > 8) {
length -= 8;
PUT_HALF_BLOCK(left, op);
PUT_HALF_BLOCK(right, op);
/*
* Save current cipher block here
*/
ocipherl = cipherl;
ocipherr = cipherr;
} else {
/*
* Trouble here. Start at end of output,
* work backwards.
*/
op += (int) length;
switch(length) {
case 8:
*(--op) = (unsigned char) (right & 0xff);
case 7:
*(--op) = (unsigned char) ((right >> 8) & 0xff);
case 6:
*(--op) = (unsigned char) ((right >> 16) & 0xff);
case 5:
*(--op) = (unsigned char) ((right >> 24) & 0xff);
case 4:
*(--op) = (unsigned char) (left & 0xff);
case 3:
*(--op) = (unsigned char) ((left >> 8) & 0xff);
case 2:
*(--op) = (unsigned char) ((left >> 16) & 0xff);
case 1:
*(--op) = (unsigned char) ((left >> 24) & 0xff);
break;
}
break; /* we're done */
}
}
}
int
mit_des_cbc_encrypt(const mit_des_cblock *in, mit_des_cblock *out,
unsigned long length, const mit_des_key_schedule schedule,
const mit_des_cblock ivec, int enc)
{
/*
* Deal with encryption and decryption separately.
*/
if (enc)
des_cbc_encrypt(in, out, length, schedule, ivec);
else
des_cbc_decrypt(in, out, length, schedule, ivec);
return 0;
}