📄 serpent-reference.c
字号:
/*
$Id: serpent-reference.c,v 1.42 1998/06/10 13:50:31 fms Exp $
This is a reference implementation of the Serpent cipher invented by Ross
Anderson, Eli Biham, Lars Knudsen. It is written for the human reader
more than for the machine and, as such, it is optimised for clarity
rather than speed. ("Premature optimisation is the root of all evil.")
# This file is part of the C reference implementation of Serpent.
#
# Written by Frank Stajano,
# Olivetti Oracle Research Laboratory <http://www.orl.co.uk/~fms/> and
# Cambridge University Computer Laboratory <http://www.cl.cam.ac.uk/~fms27/>.
#
# (c) 1998 Olivetti Oracle Research Laboratory (ORL)
#
# Original (Python) Serpent reference development started on 1998 02 12.
# C implementation development started on 1998 03 04.
#
# Serpent cipher invented by Ross Anderson, Eli Biham, Lars Knudsen.
# Serpent is a candidate for the Advanced Encryption Standard.
*/
/* -------------------------------------------------- */
#include "serpent-api.h"
#include "serpent-tables.h"
#include "serpent-reference.h"
#include "serpent-aux.h"
/* -------------------------------------------------- */
EMBED_RCS(serpent_reference_c,
"$Id: serpent-reference.c,v 1.42 1998/06/10 13:50:31 fms Exp $")
/* NIST API functions */
int makeKey(keyInstance *key, BYTE direction, int keyLen,
char *keyMaterial) {
/*
Initializes a keyInstance with the following information. The
fields followed by (*) get their value copied verbatim from the input
parameters to this function. The others are calculated from them.
direction (*): DIR_ENCRYPT or DIR_DECRYPT
keyLen (*): The key length (in bits) of the key
keyMaterial (*): a string with the raw key information
(BITS_PER_BLOCK/4 ASCII characters representing the hex values for
the key).
userKey: the long (256 bit) form of the key in little-endian binary format.
KHat: a series of subkeys generated from the user key.
Parameters:
key: a structure that holds the keyInstance information
direction: the key is being setup for encryption or decryption
keyLen: an integer value that indicates the length of the key in bits.
keyMaterial: the raw key information (keyLen/4 ASCII characters
representing the hex values for the key). For example,
"0123456789abcdef0123456789abcdef" is the string for a key with the
binary value:
0000000100100011010001010110011110001001101010111100...
Returns:
TRUE - on success
BAD_KEY_DIR - direction is invalid (e.g., unknown value)
BAD_KEY_MAT - keyMaterial is invalid (e.g., wrong length)
*/
int result;
if ((keyLen%BITS_PER_WORD)
|| (keyLen > BITS_PER_KEY)
|| (keyLen < BITS_PER_SHORTEST_KEY)) {
return BAD_KEY_MAT;
}
key->direction = direction;
key->keyLen = keyLen;
if (keyMaterial) {
strncpy(key->keyMaterial, keyMaterial, HEX_DIGITS_PER_KEY + 1);
}
result = stringToWords(key->keyMaterial, key->userKey, WORDS_PER_KEY);
if (result != TRUE) {
return BAD_KEY_MAT;
}
if (keyLen < BITS_PER_KEY) {
shortToLongKey(key->userKey, keyLen);
}
makeSubkeys(key->userKey, key->KHat);
return TRUE;
}
int cipherInit(cipherInstance* cipher, BYTE mode, char* IV) {
/*
Initializes the cipher with the mode and, if present, sets the
Initialization Vector. If any algorithm specific setup is necessary,
cipherInit() must take care of that as well. The IV parameter passed to
cipherInit() is an ASCII hex string representation of the IV, i.e. the
IV passed as a parameter will typically be 32 bytes long. The IV field
of the cipherInstance structure is the binary value of the IV, i.e. it
will typically be 16 bytes long. Algorithm specific parameters must be
loaded into the cipherInstance structure before calling
cipherInit(). For example, if the algorithm can use other block sizes
than 128-bits, a field should be added to the cipherInstance structure
and the value being used should be loaded into the cipher parameter
before calling cipherInit().
Parameters:
cipher - the cipherInstance being loaded
mode - the operation mode of this cipher (this is one of
MODE_ECB, MODE_CBC, or MODE_CFB1)
IV - the cipher initialization vector, necessary for some modes
Returns:
TRUE - on success
BAD_CIPHER_MODE - the mode passed is unknown.
*/
int result;
cipher->mode = mode;
switch (mode) {
case MODE_ECB:
return TRUE;
case MODE_CFB1:
case MODE_CBC:
/* Convert the initialisation vector from ascii to bin and store it
in cipher. */
result = stringToWords(IV, (WORD*) cipher->IV, WORDS_PER_IV);
if (result != TRUE) {
return BAD_IV;
}
return TRUE;
default:
return BAD_CIPHER_MODE;
}
}
int blockEncrypt(cipherInstance* cipher, keyInstance* key, BYTE* input, int
inputLen, BYTE* outBuffer) {
/* Uses the cipherInstance object and the keyInstance object to encrypt
one block of data in the input buffer. The output (the encrypted data)
is returned in outBuffer, which is the same size as inputLen. The
routine returns the number of bits enciphered. inputLen will typically
be 128 bits, but some algorithms may handle additional block
sizes. Additionally, it is acceptable to use this routine to encrypt
multiple "blocks" of data with one call. For example, if your algorithm
has a block size of 128 bits, it is acceptable to pass n*128 bits to
blockEncrypt().
Parameters:
cipher - the cipherInstance to be used
key - the ciphering key
input - the input buffer
inputLen - the input length, in bits
outBuffer - contains the encrypted data
Returns:
The number of bits ciphered, or
BAD_CIPHER_STATE - cipher in bad state (e.g., not initialized)
BAD_KEY_MAT - direction not set for DIR_ENCRYPT
*/
/* Why does NIST insist on having both a key->direction indicator and
separate encrypt and decrypt calls? This redundancy just opens the
door to inconsistency and confusion, which would be avoided if the
caller only had one way to specify the desired direction.
Note also that, in case of inconsistency, we return BAD_KEY_MAT as
requested, though it would make more sense to return BAD_KEY_DIR.
*/
if (key->direction != DIR_ENCRYPT) {
return BAD_KEY_MAT;
}
return blockEncryptOrDecrypt(cipher, key, input, inputLen, outBuffer);
}
int blockDecrypt(cipherInstance* cipher, keyInstance* key, BYTE* input, int
inputLen, BYTE* outBuffer) {
/* Uses the cipherInstance object and the keyInstance object to decrypt
one block of data in the input buffer. The output (the decrypted data)
is returned in outBuffer, which is the same size as inputLen. The
routine returns the number of bits deciphered. inputLen will typically
be 128 bits, but some algorithms may handle additional block
sizes. Additionally, it is acceptable to use this routine to decrypt
multiple "blocks" of data with one call. For example, if your
algorithm has a block size of 128 bits, it is acceptable to pass n*128
bits to blockDecrypt().
Parameters:
cipher - the cipherInstance to be used
key - the ciphering key
input - the input buffer
inputLen - the input length, in bits
outBuffer - contains the decrypted data
Returns:
The number of bits deciphered, or
BAD_CIPHER_STATE - cipher in bad state (e.g., not initialized)
BAD_KEY_MAT - direction not set for DIR_DECRYPT
*/
if (key->direction != DIR_DECRYPT) {
return BAD_KEY_MAT; /* see comments in blockEncrypt */
}
return blockEncryptOrDecrypt(cipher, key, input, inputLen, outBuffer);
}
/* -------------------------------------------------- */
/* Stuff called by the NIST API */
int blockEncryptOrDecrypt(cipherInstance* cipher, keyInstance* key, BYTE*
input, int inputLen, BYTE* outBuffer) {
int i, numBlocks, bytesLeftOver, result;
numBlocks = inputLen / BITS_PER_BLOCK;
bytesLeftOver = inputLen % BITS_PER_BLOCK;
/* I'd like to return an error if this is != 0, but it's not clear to me
from the NIST spec whether I'm allowed to or not. So I'll settle for
just not processing the leftover bits (and the caller will have to
check for himself whether the nmber of bytes processed is or isn't the
same as inputLen). */
for (i=0; i<numBlocks; i++) {
result = doOneBlock((WORD*)(input + i*WORDS_PER_BLOCK),
(WORD*)(outBuffer + i*WORDS_PER_BLOCK), cipher, key);
if (result != TRUE) {
return BAD_CIPHER_STATE;
}
}
return numBlocks*BITS_PER_BLOCK;
}
int doOneBlock(BLOCK input, BLOCK output,
cipherInstance* cipher, keyInstance* key) {
/* Encrypt or decrypt one block, given by 'input', and put the result in
'output'. 'cipher' points to the cipher instance to be used
(containing among other things the initialisation vector for CBC) and
'key' points to the key instance to be used. Return TRUE if all goes
well, BAD_CIPHER_MODE if the required encryption or decryption mode is
not supported, */
int i;
BIT plainTextBit, cipherTextBit;
BLOCK temp;
switch (key->direction) {
case DIR_ENCRYPT:
switch (cipher->mode) {
case MODE_ECB:
encryptGivenKHat(input, key->KHat, output);
break;
case MODE_CBC:
for (i=0; i < WORDS_PER_BLOCK; i++) {
temp[i] = input[i] ^ ((WORD*) cipher->IV)[i];
}
encryptGivenKHat(temp, key->KHat, output);
for (i=0; i < WORDS_PER_BLOCK; i++) {
((WORD*) (cipher->IV))[i] = output[i];
}
break;
case MODE_CFB1:
/* We use cipher->IV as the shift register. New data comes in at
the LSB end and old data is dropped out of the MSB end. This
is what is traditionally called a "left shift", though in our
case we are actually shifting towards the right (MSB end)
since we store multiword quantities in little-endian
format.
The first bit of the plaintext to be encrypted is bit 0
(LSB). This is stored in bit 0 of the output ciphertext
block. Similarly, every bit of the plaintext is stored, after
encryption, in the corresponding bit of the output block.
Note that, due to the way it's called, this routine will
always encrypt an entire block, though in theory MODE_CFB1
could also encrypt a non-round number of bits. */
for (i=0; i<BITS_PER_BLOCK; i++) {
encryptGivenKHat((WORD*)(cipher->IV), key->KHat, temp);
plainTextBit = getBit(input, i);
cipherTextBit = getBit(temp, BITS_PER_BLOCK-1) ^ plainTextBit;
setBit(output, i, cipherTextBit);
shiftBlockLeft((WORD*)(cipher->IV), cipherTextBit);
}
break;
default:
return BAD_CIPHER_MODE;
}
break;
case DIR_DECRYPT:
switch (cipher->mode) {
case MODE_ECB:
decryptGivenKHat(input, key->KHat, output);
break;
case MODE_CBC:
decryptGivenKHat(input, key->KHat, temp);
for (i=0; i < WORDS_PER_BLOCK; i++) {
output[i] = temp[i] ^ ((WORD*) cipher->IV)[i];
}
for (i=0; i < WORDS_PER_BLOCK; i++) {
((WORD*) cipher->IV)[i] = input[i];
}
break;
case MODE_CFB1:
/* The comments on the encryption side apply. See above. */
for (i=0; i<BITS_PER_BLOCK; i++) {
encryptGivenKHat((WORD*)(cipher->IV), key->KHat, temp);
/* NB: yes, in CFB the cipher is used in encryption mode even
when decrypting. */
cipherTextBit = getBit(input, i);
plainTextBit = getBit(temp, BITS_PER_BLOCK-1) ^ cipherTextBit;
setBit(output, i, plainTextBit);
shiftBlockLeft((WORD*)(cipher->IV), cipherTextBit);
}
break;
default:
return BAD_CIPHER_MODE;
}
break;
default:
return BAD_KEY_DIR;
}
return TRUE;
}
/* -------------------------------------------------- */
/* Auxiliary bit-twiddling stuff */
void setBit(WORD x[], int p, BIT v) {
/* Set the bit at position 'p' of little-endian word array 'x' to 'v'. */
if (v) {
x[p/BITS_PER_WORD] |= ((WORD) 0x1 << p%BITS_PER_WORD);
} else {
x[p/BITS_PER_WORD] &= ~((WORD) 0x1 << p%BITS_PER_WORD);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -