/*
 * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * You can also choose to distribute this program under the terms of
 * the Unmodified Binary Distribution Licence (as given in the file
 * COPYING.UBDL), provided that you have satisfied its requirements.
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FILE_SECBOOT ( PERMITTED );

/** @file
 *
 * DES algorithm
 *
 * DES was not designed to be implemented in software, and therefore
 * contains a large number of bit permutation operations that are
 * essentially free in hardware (requiring only wires, no gates) but
 * expensive in software.
 *
 * Since DES is no longer used as a practical block cipher for large
 * volumes of data, we optimise for code size, and do not attempt to
 * obtain fast throughput.
 *
 * The algorithm is specified in NIST SP 800-67, downloadable from
 * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf
 */

#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/rotate.h>
#include <ipxe/crypto.h>
#include <ipxe/ecb.h>
#include <ipxe/cbc.h>
#include <ipxe/init.h>
#include <ipxe/des.h>

/**
 * DES shift schedule
 *
 * The DES shift schedule (ordered from round 16 down to round 1) is
 * {1,2,2,2,2,2,2,1,2,2,2,2,2,2,1,1}.  In binary, this may be
 * represented as {1,10,10,10,10,10,10,1,10,10,10,10,10,10,1,1} and
 * concatenated (without padding) to produce a single binary integer
 * 1101010101010110101010101011 (equal to 0x0d556aab in hexadecimal).
 *
 * This integer may then be consumed LSB-first, where a 1 bit
 * indicates a shift and the generation of a round key, and a 0 bit
 * indicates a shift without the generation of a round key.
 */
#define DES_SCHEDULE 0x0d556aab

/**
 * Define an element pair in a DES S-box
 *
 * @v x			Upper element of element pair
 * @v y			Lower element of element pair
 *
 * DES S-box elements are 4-bit values.  We encode two values per
 * byte, ordering the elements so that the six-bit input value may be
 * used directly as a lookup index.
 *
 * Specifically, if the input value is {r1,c3,c2,c1,c0,r0}, where
 * {r1,r0} is the table row index and {c3,c2,c1,c0} is the table
 * column index (as used in the DES specification), then:
 *
 *   - {r1,c3,c2,c1,c0} is the byte index into the table
 *
 *   - (4*r0) is the required bit shift to extract the 4-bit value
 */
#define SBYTE( x, y ) ( ( (y) << 4 ) | (x) )

/**
 * Define a row pair in a DES S-box
 *
 * @v x0..xf		Upper row of row pair
 * @v y0..yf		Lower row of row pair
 */
#define SBOX( x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf,  \
	      y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, ya, yb, yc, yd, ye, yf ) \
	SBYTE ( x0, y0 ), SBYTE ( x1, y1 ), SBYTE ( x2, y2 ), SBYTE ( x3, y3 ),\
	SBYTE ( x4, y4 ), SBYTE ( x5, y5 ), SBYTE ( x6, y6 ), SBYTE ( x7, y7 ),\
	SBYTE ( x8, y8 ), SBYTE ( x9, y9 ), SBYTE ( xa, ya ), SBYTE ( xb, yb ),\
	SBYTE ( xc, yc ), SBYTE ( xd, yd ), SBYTE ( xe, ye ), SBYTE ( xf, yf )

/** DES S-boxes S1..S8 */
static const uint8_t des_s[8][32] = { {
	/* S1 */
	SBOX ( 14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
		0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8 ),
	SBOX (  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
	       15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13 )
}, {
	/* S2 */
	SBOX ( 15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
		3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5 ),
	SBOX (  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
		13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9 )
}, {
	/* S3 */
	SBOX ( 10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
	       13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1 ),
	SBOX ( 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
		1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12 )
}, {
	/* S4 */
	SBOX (  7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
	       13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9 ),
	SBOX ( 10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
		3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14 )
}, {
	/* S5 */
	SBOX (  2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
	       14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6 ),
	SBOX (  4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
	       11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3 )
}, {
	/* S6 */
	SBOX ( 12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
	       10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8 ),
	SBOX (  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
		4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13 )
}, {
	/* S7 */
	SBOX (  4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
	       13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6 ),
	SBOX (  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
		6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12 )
}, {
	/* S8 */
	SBOX ( 13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
		1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2 ),
	SBOX (  7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
		2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11 )
} };

/**
 * Define a bit index within permuted choice 2 (PC2)
 *
 * @v bit		Bit index
 *
 * Permuted choice 2 (PC2) is used to select bits from a concatenated
 * pair of 28-bit registers ("C" and "D") as part of the key schedule.
 * We store these as 32-bit registers and so must add 4 to indexes
 * above 28.
 */
#define DES_PC2( x ) ( (x) + ( ( (x) > 28 ) ? 4 : 0 ) )

/**
 * Define six bits of permuted choice 2 (PC2)
 *
 * @v r1:r0		Bits corresponding to S-box row index
 * @v c3:c0		Bits corresponding to S-box column index
 *
 * There are 8 steps within a DES round (one step per S-box).  Each
 * step requires six bits of the round key, corresponding to the S-box
 * input value {r1,c3,c2,c1,c0,r0}, where {r1,r0} is the table row
 * index and {c3,c2,c1,c0} is the table column index.
 *
 * As an optimisation, we store the least significant of the 6 bits in
 * the sign bit of a signed 8-bit value, and the remaining 5 bits in
 * the least significant 5 bits of the 8-bit value.  See the comments
 * in des_sbox() for further details.
 */
#define DES_PC2R( r1, c3, c2, c1, c0, r0 )				\
	DES_PC2 ( r0 ), /* LSB stored in sign bit */			\
	DES_PC2 ( r0 ), /* Unused bit */				\
	DES_PC2 ( r0 ), /* Unused bit */				\
	DES_PC2 ( r1 ),	/* Remaining 5 bits */				\
	DES_PC2 ( c3 ),	/* ... */					\
	DES_PC2 ( c2 ),	/* ... */					\
	DES_PC2 ( c1 ),	/* ... */					\
	DES_PC2 ( c0 ) 	/* ... */

/**
 * A DES systematic permutation generator
 *
 * Many of the permutations used in DES comprise systematic bit
 * patterns.  We generate these permutations at runtime to save on
 * code size.
 */
struct des_generator {
	/** Permutation */
	uint8_t *permutation;
	/** Seed value */
	uint32_t seed;
};

/**
 * Define a DES permutation generator
 *
 * @v PERMUTATION	Permutation
 * @v OFFSET		Fixed input bit offset (0 or 1)
 * @v INV<n>		Input bit index bit <n> should be inverted
 * @v BIT<n>		Source bit for input bit index bit <n>
 * @ret generator	Permutation generator
 */
#define DES_GENERATOR( PERMUTATION, OFFSET, INV5, BIT5, INV4, BIT4,	\
		       INV3, BIT3, INV2, BIT2, INV1, BIT1, INV0, BIT0 )	\
	{								\
		.permutation = (PERMUTATION),				\
		.seed = ( ( (INV0) << 31 ) | ( (BIT0) << 28 ) |		\
			  ( (INV1) << 27 ) | ( (BIT1) << 24 ) |		\
			  ( (INV2) << 23 ) | ( (BIT2) << 20 ) |		\
			  ( (INV3) << 19 ) | ( (BIT3) << 16 ) |		\
			  ( (INV4) << 15 ) | ( (BIT4) << 12 ) |		\
			  ( (INV5) << 11 ) | ( (BIT5) <<  8 ) |		\
			  ( ( uint32_t ) sizeof (PERMUTATION) - 1 ) |	\
			  (OFFSET) ),					\
	}

/** DES permuted choice 1 (PC1) "C" register */
static uint8_t des_pc1c[29];

/** DES permuted choice 1 (PC1) "D" register */
static uint8_t des_pc1d[33];

/** DES permuted choice 2 (PC2) */
static const uint8_t des_pc2[65] = {
	DES_PC2R ( 14, 17, 11, 24,  1,  5 ),
	DES_PC2R (  3, 28, 15,  6, 21, 10 ),
	DES_PC2R ( 23, 19, 12,  4, 26,  8 ),
	DES_PC2R ( 16,  7, 27, 20, 13,  2 ),
	DES_PC2R ( 41, 52, 31, 37, 47, 55 ),
	DES_PC2R ( 30, 40, 51, 45, 33, 48 ),
	DES_PC2R ( 44, 49, 39, 56, 34, 53 ),
	DES_PC2R ( 46, 42, 50, 36, 29, 32 ),
	0 /* terminator */
};

/** DES initial permutation (IP) */
static uint8_t des_ip[65];

/** DES data permutation (P) */
static const uint8_t des_p[33] = {
	16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
	 2,  8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25,
	 0 /* terminator */
};

/** DES final / inverse initial permutation (FP / IP^-1) */
static uint8_t des_fp[65];

/** DES permutation generators */
static struct des_generator des_generators[] = {

	/* The DES initial permutation transforms the bit index
	 * {x5,x4,x3,x2,x1,x0}+1 into {~x2,~x1,~x0,x4,x3,~x5}+1
	 */
	DES_GENERATOR ( des_ip, 1, 1, 2, 1, 1, 1, 0, 0, 4, 0, 3, 1, 5 ),

	/* The DES final permutation transforms the bit index
	 * {x5,x4,x3,x2,x1,x0}+1 into {~x0,x2,x1,~x5,~x4,~x3}+1
	 *
	 * There is an asymmetry in the DES block diagram for the last
	 * of the 16 rounds, which is functionally equivalent to
	 * performing 16 identical rounds and then swapping the left
	 * and right halves before applying the final permutation.  We
	 * may therefore account for this asymmetry by inverting the
	 * MSB in each bit index, to point to the corresponding bit in
	 * the other half.
	 *
	 * This is equivalent to using a permutation that transforms
	 * {x5,x4,x3,x2,x1,x0}+1 into {x0,x2,x1,~x5,~x4,~x3}+1
	 */
	DES_GENERATOR ( des_fp, 1, 0, 0, 0, 2, 0, 1, 1, 5, 1, 4, 1, 3 ),

	/* The "C" half of DES permuted choice 1 (PC1) transforms the
	 * bit index {x5,x4,x3,x2,x1,x0}+1 into {~x2,~x1,~x0,x5,x4,x3}+1
	 */
	DES_GENERATOR ( des_pc1c, 1, 1, 2, 1, 1, 1, 0, 0, 5, 0, 4, 0, 3 ),

	/* The "D" half of DES permuted choice 1 (PC1) transforms the
	 * bit index {x5,x4,x3,x2,x1,x0}+1 into {~x2,~x1,~x0,~x5,~x4,~x3}+0
	 *
	 * Due to the idosyncratic design choice of using 28-bit
	 * registers in the DES key expansion schedule, the final four
	 * permutation values appear at indices [28:31] instead of
	 * [24:27].  This is adjusted for in @c des_setkey().
	 */
	DES_GENERATOR ( des_pc1d, 0, 1, 2, 1, 1, 1, 0, 1, 5, 1, 4, 1, 3 ),
};

/**
 * Generate DES permutation
 *
 * @v generator		Generator
 */
static __attribute__ (( noinline )) void
des_generate ( struct des_generator *generator ) {
	uint8_t *permutation = generator->permutation;
	uint32_t seed = generator->seed;
	unsigned int index = 0;
	uint8_t accum;
	uint8_t bit;

	/* Generate permutations
	 *
	 * This loop is optimised for code size on a
	 * register-constrained architecture such as i386.
	 */
	do {
		/* Rotate seed to access MSB's bit descriptor */
		seed = ror32 ( seed, 8 );

		/* Initialise accumulator with six flag bits */
		accum = 0xfc;

		/* Accumulate bits until all six flag bits are cleared */
		do {
			/* Extract specified bit from index.  Use a
			 * rotation instead of a shift, since this
			 * will allow the mask to be elided.
			 */
			bit = ror8 ( index, ( seed & 0x07 ) );
			seed = ror32 ( seed, 3 );

			/* Toggle bit if applicable */
			bit ^= seed;
			seed = ror32 ( seed, 1 );

			/* Add bit to accumulator and clear one flag bit */
			accum <<= 1;
			accum |= ( bit & 0x01 );

		} while ( accum & 0x80 );

		/* Add constant offset if applicable */
		accum += ( seed & 0x01 );

		/* Store permutation */
		permutation[index] = accum;

		/* Loop until reaching length (which is always even) */
	} while ( ++index < ( seed & 0xfe ) );
	DBGC2 ( permutation, "DES generated permutation %p:\n", permutation );
	DBGC2_HDA ( permutation, 0, permutation,
		    ( ( seed & 0xfe ) + 1 /* zero terminator */ ) );
}

/**
 * Initialise permutations
 */
static void des_init ( void ) {
	unsigned int i;

	/* Generate all generated permutations */
	for ( i = 0 ; i < ( sizeof ( des_generators ) /
			    sizeof ( des_generators[0] ) ) ; i++ ) {
		des_generate ( &des_generators[i] );
	}
}

/** Initialisation function */
struct init_fn des_init_fn __init_fn ( INIT_NORMAL ) = {
	.name = "des",
	.initialise = des_init,
};

/**
 * Perform bit permutation
 *
 * @v permutation	Bit permutation (zero-terminated)
 * @v in		Input value
 * @v out		Output value
 */
static void des_permute ( const uint8_t *permutation, const uint8_t *in,
			  uint8_t *out ) {
	uint8_t mask = 0x80;
	uint8_t accum = 0;
	unsigned int bit;

	/* Extract individual input bits to construct output value */
	while ( ( bit = *(permutation++) ) ) {
		bit--;
		if ( in[ bit / 8 ] & ( 0x80 >> ( bit % 8 ) ) )
			accum |= mask;
		*out = accum;
		mask = ror8 ( mask, 1 );
		if ( mask == 0x80 ) {
			out++;
			accum = 0;
		}
	}
}

/**
 * Perform DES S-box substitution
 *
 * @v in		32-bit input value (native endian)
 * @v rkey		48-bit round key
 * @ret out		32-bit output value (native endian)
 */
static uint32_t des_sbox ( uint32_t in, const union des_round_key *rkey ) {
	uint32_t out = 0;
	uint32_t lookup;
	int32_t key;
	uint8_t sub;
	unsigned int i;

	/* Perform input expansion, key addition, and S-box substitution */
	for ( i = 0 ; i < 8 ; i++ ) {

		/* Rotate input and output */
		out = rol32 ( out, 4 );
		in = rol32 ( in, 4 );

		/* Extract step key from relevant 6 bits of round key
		 *
		 * The least significant of the 6 bits (corresponding
		 * to bit r0 in the S-box lookup index) is stored in
		 * the sign bit of the step key byte.  It will
		 * therefore be propagated via sign extension to the
		 * MSB of the 32-bit step key.
		 *
		 * The remaining 5 of the 6 bits (corresponding to
		 * bits {r1,c3,c2,c1,c0} in the S-box lookup index)
		 * are stored in the least significant 5 bits of the
		 * step key byte and will end up in the least
		 * significant 5 bits of the 32-bit step key.
		 */
		key = rkey->step[i];

		/* Add step key to input to produce S-box lookup index
		 *
		 * We do not ever perform an explicit expansion of the
		 * input value from 32 to 48 bits.  Instead, we rotate
		 * the 32-bit input value by 4 bits on each step, and
		 * extract the relevant 6 bits.
		 *
		 * The least significant of the 6 bits (corresponding
		 * to bit r0 in the S-box lookup index) is currently
		 * in the MSB of the 32-bit (rotated) input value.
		 *
		 * The remaining 5 of the 6 bits (corresponding to
		 * bits {r1,c3,c2,c1,c0} in the S-box lookup index)
		 * are currently in the least significant 5 bits of
		 * the 32-bit (rotated) input value.
		 *
		 * This aligns with the placement of the bits in the
		 * step key (see above), and we can therefore perform
		 * a single XOR to add the 6-bit step key to the
		 * relevant 6 bits of the input value.
		 */
		lookup = ( in ^ key );

		/* Look up S[i][in ^ key] from S-box
		 *
		 * We have bits {r1,c3,c2,c1,c0} in the least
		 * significant 5 bits of the lookup index, and so can
		 * use the masked lookup index directly as a byte
		 * index into the relevant S-box to extract the byte
		 * containing both {r1,c3,c2,c1,c0,'0'} and
		 * {r1,c3,c2,c1,c0,'1'}.
		 *
		 * We then use the MSB of the 32-bit lookup index to
		 * extract the relevant nibble for the full lookup
		 * index {r1,c3,c2,c1,c0,r0}.
		 */
		sub = des_s[i][ lookup & 0x1f ];
		sub >>= ( ( lookup >> 29 ) & 4 );
		sub &= 0x0f;

		/* Substitute S[i][input ^ key] into output */
		out |= sub;
	}

	return out;
}

/**
 * Perform a single DES round
 *
 * @v block		DES block
 * @v rkey		48-bit round key
 */
static void des_round ( union des_block *block,
			const union des_round_key *rkey ) {
	union des_dword sbox;
	uint32_t left;
	uint32_t right;

	/* Extract left and right halves L[n-1] and R[n-1] */
	left = block->left.dword;
	right = block->right.dword;
	DBGC2 ( block, "DES L=%08x R=%08x K=%08x%08x", be32_to_cpu ( left ),
		be32_to_cpu ( right ), be32_to_cpu ( rkey->dword[0] ),
		be32_to_cpu ( rkey->dword[1] ) );

	/* L[n] = R[n-1] */
	block->left.dword = right;

	/* Calculate Feistel function f(R[n-1], K[n]) */
	sbox.dword = cpu_to_be32 ( des_sbox ( be32_to_cpu ( right ), rkey ) );
	des_permute ( des_p, sbox.byte, block->right.byte );

	/* R[n] = L[n-1] + f(R[n-1], K[n]) */
	block->right.dword ^= left;
	DBGC2 ( block, " => L=%08x R=%08x\n",
		be32_to_cpu ( block->left.dword ),
		be32_to_cpu ( block->right.dword ) );
}

/**
 * Perform all DES rounds
 *
 * @v in		Input DES block
 * @v out		Output DES block
 * @v rkey		Starting 48-bit round key
 * @v offset		Byte offset between round keys
 */
static void des_rounds ( const union des_block *in, union des_block *out,
			 const union des_round_key *rkey,
			 ssize_t offset ) {
	union des_block tmp;
	unsigned int i;

	/* Apply initial permutation */
	des_permute ( des_ip, in->byte, tmp.byte );

	/* Perform all DES rounds, consuming keys in the specified order */
	for ( i = 0 ; i < DES_ROUNDS ; i++ ) {
		des_round ( &tmp, rkey );
		rkey = ( ( ( void * ) rkey ) + offset );
	}

	/* Apply final permutation */
	DBGC ( &tmp, "DES %scrypted %08x%08x => ",
	       ( ( offset > 0 ) ? "en" : "de" ), be32_to_cpu ( in->dword[0] ),
	       be32_to_cpu ( in->dword[1] ) );
	des_permute ( des_fp, tmp.byte, out->byte );
	DBGC ( &tmp, "%08x%08x\n", be32_to_cpu ( out->dword[0] ),
	       be32_to_cpu ( out->dword[1] ) );
}

/**
 * Rotate 28-bit word
 *
 * @v dword		28-bit dword value
 * @ret dword		Rotated 28-bit dword value
 */
static uint32_t des_rol28 ( uint32_t dword ) {
	int32_t sdword;

	/* Convert to native-endian */
	sdword = be32_to_cpu ( dword );

	/* Signed shift right by 4 places to copy bit 31 to bits 27:31 */
	sdword >>= 4;

	/* Rotate left */
	sdword = rol32 ( sdword, 1 );

	/* Shift left by 4 places to restore bit positions */
	sdword <<= 4;

	/* Convert back to big-endian */
	dword = cpu_to_be32 ( sdword );

	return dword;
}

/**
 * Set key
 *
 * @v ctx		Context
 * @v key		Key
 * @v keylen		Key length
 * @ret rc		Return status code
 */
static int des_setkey ( void *ctx, const void *key, size_t keylen ) {
	struct des_context *des = ctx;
	union des_round_key *rkey = des->rkey;
	union des_block reg;
	uint32_t schedule;

	/* Validate key length */
	if ( keylen != DES_BLOCKSIZE )
		return -EINVAL;
	DBGC ( des, "DES %p new key:\n", des );
	DBGC_HDA ( des, 0, key, keylen );

	/* Apply permuted choice 1 */
	des_permute ( des_pc1c, key, reg.c.byte );
	des_permute ( des_pc1d, key, reg.d.byte );
	reg.d.byte[3] <<= 4; /* see comment for @c des_pc1d */
	DBGC2 ( des, "DES %p C[ 0]=%07x D[ 0]=%07x\n",
		des, ( be32_to_cpu ( reg.c.dword ) >> 4 ),
		( be32_to_cpu ( reg.d.dword ) >> 4 ) );

	/* Generate round keys */
	for ( schedule = DES_SCHEDULE ; schedule ; schedule >>= 1 ) {

		/* Shift 28-bit words */
		reg.c.dword = des_rol28 ( reg.c.dword );
		reg.d.dword = des_rol28 ( reg.d.dword );

		/* Skip rounds according to shift schedule */
		if ( ! ( schedule & 1 ) )
			continue;

		/* Apply permuted choice 2 */
		des_permute ( des_pc2, reg.byte, rkey->byte );
		DBGC2 ( des, "DES %p C[%2zd]=%07x D[%2zd]=%07x K[%2zd]="
			"%08x%08x\n", des, ( ( rkey - des->rkey ) + 1 ),
			( be32_to_cpu ( reg.c.dword ) >> 4 ),
			( ( rkey - des->rkey ) + 1 ),
			( be32_to_cpu ( reg.d.dword ) >> 4 ),
			( ( rkey - des->rkey ) + 1 ),
			be32_to_cpu ( rkey->dword[0] ),
			be32_to_cpu ( rkey->dword[1] ) );

		/* Move to next key */
		rkey++;
	}

	/* Sanity check */
	assert ( rkey == &des->rkey[DES_ROUNDS] );

	return 0;
}

/**
 * Encrypt data
 *
 * @v ctx		Context
 * @v src		Data to encrypt
 * @v dst		Buffer for encrypted data
 * @v len		Length of data
 */
static void des_encrypt ( void *ctx, const void *src, void *dst, size_t len ) {
	struct des_context *des = ctx;

	/* Sanity check */
	assert ( len == DES_BLOCKSIZE );

	/* Cipher using keys in forward direction */
	des_rounds ( src, dst, &des->rkey[0], sizeof ( des->rkey[0] ) );
}

/**
 * Decrypt data
 *
 * @v ctx		Context
 * @v src		Data to decrypt
 * @v dst		Buffer for decrypted data
 * @v len		Length of data
 */
static void des_decrypt ( void *ctx, const void *src, void *dst, size_t len ) {
	struct des_context *des = ctx;

	/* Sanity check */
	assert ( len == DES_BLOCKSIZE );

	/* Cipher using keys in reverse direction */
	des_rounds ( src, dst, &des->rkey[ DES_ROUNDS - 1 ],
		     -sizeof ( des->rkey[0] ) );
}

/** Basic DES algorithm */
struct cipher_algorithm des_algorithm = {
	.name = "des",
	.ctxsize = sizeof ( struct des_context ),
	.blocksize = DES_BLOCKSIZE,
	.alignsize = 0,
	.authsize = 0,
	.setkey = des_setkey,
	.setiv = cipher_null_setiv,
	.encrypt = des_encrypt,
	.decrypt = des_decrypt,
	.auth = cipher_null_auth,
};

/* DES in Electronic Codebook mode */
ECB_CIPHER ( des_ecb, des_ecb_algorithm,
	     des_algorithm, struct des_context, DES_BLOCKSIZE );

/* DES in Cipher Block Chaining mode */
CBC_CIPHER ( des_cbc, des_cbc_algorithm,
	     des_algorithm, struct des_context, DES_BLOCKSIZE );
