summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2024-02-07 22:20:20 +0100
committerMichael Brown2024-02-08 00:32:10 +0100
commite7ae51b0d75d9b9925748743b91405c99e5c7fec (patch)
tree00a1509a96ee9c40647abcde9b9bbfe4ec54c437
parent[test] Remove dummy initialisation vector for ECB-mode AES tests (diff)
downloadipxe-e7ae51b0d75d9b9925748743b91405c99e5c7fec.tar.gz
ipxe-e7ae51b0d75d9b9925748743b91405c99e5c7fec.tar.xz
ipxe-e7ae51b0d75d9b9925748743b91405c99e5c7fec.zip
[crypto] Add implementation of the DES cipher
The DES block cipher dates back to the 1970s. It is no longer relevant for use in TLS cipher suites, but it is still used by the MS-CHAPv2 authentication protocol which remains unfortunately common for 802.1x port authentication. Add an implementation of the DES block cipher, complete with the extremely comprehensive test vectors published by NBS (the precursor to NIST) in the form of an utterly adorable typewritten and hand-drawn paper document. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/crypto/des.c695
-rw-r--r--src/include/ipxe/des.h91
-rw-r--r--src/include/ipxe/errfile.h1
-rw-r--r--src/tests/des_test.c898
-rw-r--r--src/tests/tests.c1
5 files changed, 1686 insertions, 0 deletions
diff --git a/src/crypto/des.c b/src/crypto/des.c
new file mode 100644
index 00000000..6918bec3
--- /dev/null
+++ b/src/crypto/des.c
@@ -0,0 +1,695 @@
+/*
+ * 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
+ *
+ * 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 ) = {
+ .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 );
diff --git a/src/include/ipxe/des.h b/src/include/ipxe/des.h
new file mode 100644
index 00000000..755a90ea
--- /dev/null
+++ b/src/include/ipxe/des.h
@@ -0,0 +1,91 @@
+#ifndef _IPXE_DES_H
+#define _IPXE_DES_H
+
+/** @file
+ *
+ * DES algorithm
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/crypto.h>
+
+/** A DES 32-bit dword value
+ *
+ * DES views data as 64-bit big-endian values, typically handled as a
+ * most-significant "left" half and a least-significant "right" half.
+ */
+union des_dword {
+ /** Raw bytes */
+ uint8_t byte[4];
+ /** 32-bit big-endian dword */
+ uint32_t dword;
+};
+
+/** A DES 64-bit block */
+union des_block {
+ /** Raw bytes */
+ uint8_t byte[8];
+ /** 32-bit big-endian dwords */
+ uint32_t dword[2];
+ /** Named left and right halves */
+ struct {
+ /** Left (most significant) half */
+ union des_dword left;
+ /** Right (least significant) half */
+ union des_dword right;
+ };
+ /** Named "C" and "D" halves */
+ struct {
+ /** "C" (most significant) half */
+ union des_dword c;
+ /** "D" (least significant) half */
+ union des_dword d;
+ };
+};
+
+/** DES blocksize */
+#define DES_BLOCKSIZE sizeof ( union des_block )
+
+/** A DES round key
+ *
+ * A DES round key is a 48-bit value, consumed as 8 groups of 6 bits.
+ * We store these as 8 separate bytes, for simplicity of consumption.
+ */
+union des_round_key {
+ /** Raw bytes */
+ uint8_t byte[8];
+ /** 32-bit big-endian dwords */
+ uint32_t dword[2];
+ /** 6-bit step key byte
+ *
+ * There are 8 steps within a DES round (one step per S-box).
+ * Each step requires six bits of the round key.
+ *
+ * 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.
+ */
+ int8_t step[8];
+};
+
+/** Number of DES rounds */
+#define DES_ROUNDS 16
+
+/** DES context */
+struct des_context {
+ /** Round keys */
+ union des_round_key rkey[DES_ROUNDS];
+};
+
+/** DES context size */
+#define DES_CTX_SIZE sizeof ( struct des_context )
+
+extern struct cipher_algorithm des_algorithm;
+extern struct cipher_algorithm des_ecb_algorithm;
+extern struct cipher_algorithm des_cbc_algorithm;
+
+#endif /* _IPXE_DES_H */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 060a42a3..f7a00dbe 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -408,6 +408,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_efi_shim ( ERRFILE_OTHER | 0x005d0000 )
#define ERRFILE_efi_settings ( ERRFILE_OTHER | 0x005e0000 )
#define ERRFILE_x25519 ( ERRFILE_OTHER | 0x005f0000 )
+#define ERRFILE_des ( ERRFILE_OTHER | 0x00600000 )
/** @} */
diff --git a/src/tests/des_test.c b/src/tests/des_test.c
new file mode 100644
index 00000000..ffafbd81
--- /dev/null
+++ b/src/tests/des_test.c
@@ -0,0 +1,898 @@
+/*
+ * 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
+ *
+ * DES tests
+ *
+ * These test vectors are originally provided by NBS (the precursor of
+ * NIST) in SP 500-20, downloadable as a scan of the typewritten
+ * original from:
+ *
+ * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nbsspecialpublication500-20e1980.pdf
+ */
+
+/* Forcibly enable assertions */
+#undef NDEBUG
+
+#include <assert.h>
+#include <ipxe/des.h>
+#include <ipxe/test.h>
+#include "cipher_test.h"
+
+/** Define a DES 64-bit test value */
+#define DES_VALUE(value) { \
+ ( ( ( ( uint64_t ) (value) ) >> 56 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 48 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 40 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 32 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 24 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 16 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 8 ) & 0xff ), \
+ ( ( ( ( uint64_t ) (value) ) >> 0 ) & 0xff ) \
+ }
+
+/** Define a DES test */
+#define DES_TEST( name, key, plaintext, ciphertext ) \
+ CIPHER_TEST ( name, &des_ecb_algorithm, DES_VALUE ( key ), \
+ IV(), ADDITIONAL(), DES_VALUE ( plaintext ), \
+ DES_VALUE ( ciphertext ), AUTH() )
+
+/* Sample round outputs (page 9) */
+DES_TEST ( des_round_sample,
+ 0x10316e028c8f3b4a, 0x0000000000000000, 0x82dcbafbdeab6602 );
+
+/* Test 1: Initial permutation and expansion tests
+ *
+ * "Set Key=0 and encrypt the 64-bit data vectors e[i]: i=1,...,64"
+ *
+ * Appendix B, page 28 ("IP and E test")
+ */
+DES_TEST ( des_test1_1,
+ 0x0101010101010101, 0x8000000000000000, 0x95f8a5e5dd31d900 );
+DES_TEST ( des_test1_2,
+ 0x0101010101010101, 0x4000000000000000, 0xdd7f121ca5015619 );
+DES_TEST ( des_test1_3,
+ 0x0101010101010101, 0x2000000000000000, 0x2e8653104f3834ea );
+DES_TEST ( des_test1_4,
+ 0x0101010101010101, 0x1000000000000000, 0x4bd388ff6cd81d4f );
+DES_TEST ( des_test1_5,
+ 0x0101010101010101, 0x0800000000000000, 0x20b9e767b2fb1456 );
+DES_TEST ( des_test1_6,
+ 0x0101010101010101, 0x0400000000000000, 0x55579380d77138ef );
+DES_TEST ( des_test1_7,
+ 0x0101010101010101, 0x0200000000000000, 0x6cc5defaaf04512f );
+DES_TEST ( des_test1_8,
+ 0x0101010101010101, 0x0100000000000000, 0x0d9f279ba5d87260 );
+DES_TEST ( des_test1_9,
+ 0x0101010101010101, 0x0080000000000000, 0xd9031b0271bd5a0a );
+DES_TEST ( des_test1_10,
+ 0x0101010101010101, 0x0040000000000000, 0x424250b37c3dd951 );
+DES_TEST ( des_test1_11,
+ 0x0101010101010101, 0x0020000000000000, 0xb8061b7ecd9a21e5 );
+DES_TEST ( des_test1_12,
+ 0x0101010101010101, 0x0010000000000000, 0xf15d0f286b65bd28 );
+DES_TEST ( des_test1_13,
+ 0x0101010101010101, 0x0008000000000000, 0xadd0cc8d6e5deba1 );
+DES_TEST ( des_test1_14,
+ 0x0101010101010101, 0x0004000000000000, 0xe6d5f82752ad63d1 );
+DES_TEST ( des_test1_15,
+ 0x0101010101010101, 0x0002000000000000, 0xecbfe3bd3f591a5e );
+DES_TEST ( des_test1_16,
+ 0x0101010101010101, 0x0001000000000000, 0xf356834379d165cd );
+DES_TEST ( des_test1_17,
+ 0x0101010101010101, 0x0000800000000000, 0x2b9f982f20037fa9 );
+DES_TEST ( des_test1_18,
+ 0x0101010101010101, 0x0000400000000000, 0x889de068a16f0be6 );
+DES_TEST ( des_test1_19,
+ 0x0101010101010101, 0x0000200000000000, 0xe19e275d846a1298 );
+DES_TEST ( des_test1_20,
+ 0x0101010101010101, 0x0000100000000000, 0x329a8ed523d71aec );
+DES_TEST ( des_test1_21,
+ 0x0101010101010101, 0x0000080000000000, 0xe7fce22557d23c97 );
+DES_TEST ( des_test1_22,
+ 0x0101010101010101, 0x0000040000000000, 0x12a9f5817ff2d65d );
+DES_TEST ( des_test1_23,
+ 0x0101010101010101, 0x0000020000000000, 0xa484c3ad38dc9c19 );
+DES_TEST ( des_test1_24,
+ 0x0101010101010101, 0x0000010000000000, 0xfbe00a8a1ef8ad72 );
+DES_TEST ( des_test1_25,
+ 0x0101010101010101, 0x0000008000000000, 0x750d079407521363 );
+DES_TEST ( des_test1_26,
+ 0x0101010101010101, 0x0000004000000000, 0x64feed9c724c2faf );
+DES_TEST ( des_test1_27,
+ 0x0101010101010101, 0x0000002000000000, 0xf02b263b328e2b60 );
+DES_TEST ( des_test1_28,
+ 0x0101010101010101, 0x0000001000000000, 0x9d64555a9a10b852 );
+DES_TEST ( des_test1_29,
+ 0x0101010101010101, 0x0000000800000000, 0xd106ff0bed5255d7 );
+DES_TEST ( des_test1_30,
+ 0x0101010101010101, 0x0000000400000000, 0xe1652c6b138c64a5 );
+DES_TEST ( des_test1_31,
+ 0x0101010101010101, 0x0000000200000000, 0xe428581186ec8f46 );
+DES_TEST ( des_test1_32,
+ 0x0101010101010101, 0x0000000100000000, 0xaeb5f5ede22d1a36 );
+DES_TEST ( des_test1_33,
+ 0x0101010101010101, 0x0000000080000000, 0xe943d7568aec0c5c );
+DES_TEST ( des_test1_34,
+ 0x0101010101010101, 0x0000000040000000, 0xdf98c8276f54b04b );
+DES_TEST ( des_test1_35,
+ 0x0101010101010101, 0x0000000020000000, 0xb160e4680f6c696f );
+DES_TEST ( des_test1_36,
+ 0x0101010101010101, 0x0000000010000000, 0xfa0752b07d9c4ab8 );
+DES_TEST ( des_test1_37,
+ 0x0101010101010101, 0x0000000008000000, 0xca3a2b036dbc8502 );
+DES_TEST ( des_test1_38,
+ 0x0101010101010101, 0x0000000004000000, 0x5e0905517bb59bcf );
+DES_TEST ( des_test1_39,
+ 0x0101010101010101, 0x0000000002000000, 0x814eeb3b91d90726 );
+DES_TEST ( des_test1_40,
+ 0x0101010101010101, 0x0000000001000000, 0x4d49db1532919c9f );
+DES_TEST ( des_test1_41,
+ 0x0101010101010101, 0x0000000000800000, 0x25eb5fc3f8cf0621 );
+DES_TEST ( des_test1_42,
+ 0x0101010101010101, 0x0000000000400000, 0xab6a20c0620d1c6f );
+DES_TEST ( des_test1_43,
+ 0x0101010101010101, 0x0000000000200000, 0x79e90dbc98f92cca );
+DES_TEST ( des_test1_44,
+ 0x0101010101010101, 0x0000000000100000, 0x866ecedd8072bb0e );
+DES_TEST ( des_test1_45,
+ 0x0101010101010101, 0x0000000000080000, 0x8b54536f2f3e64a8 );
+DES_TEST ( des_test1_46,
+ 0x0101010101010101, 0x0000000000040000, 0xea51d3975595b86b );
+DES_TEST ( des_test1_47,
+ 0x0101010101010101, 0x0000000000020000, 0xcaffc6ac4542de31 );
+DES_TEST ( des_test1_48,
+ 0x0101010101010101, 0x0000000000010000, 0x8dd45a2ddf90796c );
+DES_TEST ( des_test1_49,
+ 0x0101010101010101, 0x0000000000008000, 0x1029d55e880ec2d0 );
+DES_TEST ( des_test1_50,
+ 0x0101010101010101, 0x0000000000004000, 0x5d86cb23639dbea9 );
+DES_TEST ( des_test1_51,
+ 0x0101010101010101, 0x0000000000002000, 0x1d1ca853ae7c0c5f );
+DES_TEST ( des_test1_52,
+ 0x0101010101010101, 0x0000000000001000, 0xce332329248f3228 );
+DES_TEST ( des_test1_53,
+ 0x0101010101010101, 0x0000000000000800, 0x8405d1abe24fb942 );
+DES_TEST ( des_test1_54,
+ 0x0101010101010101, 0x0000000000000400, 0xe643d78090ca4207 );
+DES_TEST ( des_test1_55,
+ 0x0101010101010101, 0x0000000000000200, 0x48221b9937748a23 );
+DES_TEST ( des_test1_56,
+ 0x0101010101010101, 0x0000000000000100, 0xdd7c0bbd61fafd54 );
+DES_TEST ( des_test1_57,
+ 0x0101010101010101, 0x0000000000000080, 0x2fbc291a570db5c4 );
+DES_TEST ( des_test1_58,
+ 0x0101010101010101, 0x0000000000000040, 0xe07c30d7e4e26e12 );
+DES_TEST ( des_test1_59,
+ 0x0101010101010101, 0x0000000000000020, 0x0953e2258e8e90a1 );
+DES_TEST ( des_test1_60,
+ 0x0101010101010101, 0x0000000000000010, 0x5b711bc4ceebf2ee );
+DES_TEST ( des_test1_61,
+ 0x0101010101010101, 0x0000000000000008, 0xcc083f1e6d9e85f6 );
+DES_TEST ( des_test1_62,
+ 0x0101010101010101, 0x0000000000000004, 0xd2fd8867d50d2dfe );
+DES_TEST ( des_test1_63,
+ 0x0101010101010101, 0x0000000000000002, 0x06e7ea22ce92708f );
+DES_TEST ( des_test1_64,
+ 0x0101010101010101, 0x0000000000000001, 0x166b40b44aba4bd6 );
+
+/* Test 2: Inverse permutation and expansion tests
+ *
+ * "Set Key=0 and encrypt the results c[i] obtained in Test 1"
+ *
+ * Appendix B, page 28 ("IP and E test")
+ */
+DES_TEST ( des_test2_1,
+ 0x0101010101010101, 0x95f8a5e5dd31d900, 0x8000000000000000 );
+DES_TEST ( des_test2_2,
+ 0x0101010101010101, 0xdd7f121ca5015619, 0x4000000000000000 );
+DES_TEST ( des_test2_3,
+ 0x0101010101010101, 0x2e8653104f3834ea, 0x2000000000000000 );
+DES_TEST ( des_test2_4,
+ 0x0101010101010101, 0x4bd388ff6cd81d4f, 0x1000000000000000 );
+DES_TEST ( des_test2_5,
+ 0x0101010101010101, 0x20b9e767b2fb1456, 0x0800000000000000 );
+DES_TEST ( des_test2_6,
+ 0x0101010101010101, 0x55579380d77138ef, 0x0400000000000000 );
+DES_TEST ( des_test2_7,
+ 0x0101010101010101, 0x6cc5defaaf04512f, 0x0200000000000000 );
+DES_TEST ( des_test2_8,
+ 0x0101010101010101, 0x0d9f279ba5d87260, 0x0100000000000000 );
+DES_TEST ( des_test2_9,
+ 0x0101010101010101, 0xd9031b0271bd5a0a, 0x0080000000000000 );
+DES_TEST ( des_test2_10,
+ 0x0101010101010101, 0x424250b37c3dd951, 0x0040000000000000 );
+DES_TEST ( des_test2_11,
+ 0x0101010101010101, 0xb8061b7ecd9a21e5, 0x0020000000000000 );
+DES_TEST ( des_test2_12,
+ 0x0101010101010101, 0xf15d0f286b65bd28, 0x0010000000000000 );
+DES_TEST ( des_test2_13,
+ 0x0101010101010101, 0xadd0cc8d6e5deba1, 0x0008000000000000 );
+DES_TEST ( des_test2_14,
+ 0x0101010101010101, 0xe6d5f82752ad63d1, 0x0004000000000000 );
+DES_TEST ( des_test2_15,
+ 0x0101010101010101, 0xecbfe3bd3f591a5e, 0x0002000000000000 );
+DES_TEST ( des_test2_16,
+ 0x0101010101010101, 0xf356834379d165cd, 0x0001000000000000 );
+DES_TEST ( des_test2_17,
+ 0x0101010101010101, 0x2b9f982f20037fa9, 0x0000800000000000 );
+DES_TEST ( des_test2_18,
+ 0x0101010101010101, 0x889de068a16f0be6, 0x0000400000000000 );
+DES_TEST ( des_test2_19,
+ 0x0101010101010101, 0xe19e275d846a1298, 0x0000200000000000 );
+DES_TEST ( des_test2_20,
+ 0x0101010101010101, 0x329a8ed523d71aec, 0x0000100000000000 );
+DES_TEST ( des_test2_21,
+ 0x0101010101010101, 0xe7fce22557d23c97, 0x0000080000000000 );
+DES_TEST ( des_test2_22,
+ 0x0101010101010101, 0x12a9f5817ff2d65d, 0x0000040000000000 );
+DES_TEST ( des_test2_23,
+ 0x0101010101010101, 0xa484c3ad38dc9c19, 0x0000020000000000 );
+DES_TEST ( des_test2_24,
+ 0x0101010101010101, 0xfbe00a8a1ef8ad72, 0x0000010000000000 );
+DES_TEST ( des_test2_25,
+ 0x0101010101010101, 0x750d079407521363, 0x0000008000000000 );
+DES_TEST ( des_test2_26,
+ 0x0101010101010101, 0x64feed9c724c2faf, 0x0000004000000000 );
+DES_TEST ( des_test2_27,
+ 0x0101010101010101, 0xf02b263b328e2b60, 0x0000002000000000 );
+DES_TEST ( des_test2_28,
+ 0x0101010101010101, 0x9d64555a9a10b852, 0x0000001000000000 );
+DES_TEST ( des_test2_29,
+ 0x0101010101010101, 0xd106ff0bed5255d7, 0x0000000800000000 );
+DES_TEST ( des_test2_30,
+ 0x0101010101010101, 0xe1652c6b138c64a5, 0x0000000400000000 );
+DES_TEST ( des_test2_31,
+ 0x0101010101010101, 0xe428581186ec8f46, 0x0000000200000000 );
+DES_TEST ( des_test2_32,
+ 0x0101010101010101, 0xaeb5f5ede22d1a36, 0x0000000100000000 );
+DES_TEST ( des_test2_33,
+ 0x0101010101010101, 0xe943d7568aec0c5c, 0x0000000080000000 );
+DES_TEST ( des_test2_34,
+ 0x0101010101010101, 0xdf98c8276f54b04b, 0x0000000040000000 );
+DES_TEST ( des_test2_35,
+ 0x0101010101010101, 0xb160e4680f6c696f, 0x0000000020000000 );
+DES_TEST ( des_test2_36,
+ 0x0101010101010101, 0xfa0752b07d9c4ab8, 0x0000000010000000 );
+DES_TEST ( des_test2_37,
+ 0x0101010101010101, 0xca3a2b036dbc8502, 0x0000000008000000 );
+DES_TEST ( des_test2_38,
+ 0x0101010101010101, 0x5e0905517bb59bcf, 0x0000000004000000 );
+DES_TEST ( des_test2_39,
+ 0x0101010101010101, 0x814eeb3b91d90726, 0x0000000002000000 );
+DES_TEST ( des_test2_40,
+ 0x0101010101010101, 0x4d49db1532919c9f, 0x0000000001000000 );
+DES_TEST ( des_test2_41,
+ 0x0101010101010101, 0x25eb5fc3f8cf0621, 0x0000000000800000 );
+DES_TEST ( des_test2_42,
+ 0x0101010101010101, 0xab6a20c0620d1c6f, 0x0000000000400000 );
+DES_TEST ( des_test2_43,
+ 0x0101010101010101, 0x79e90dbc98f92cca, 0x0000000000200000 );
+DES_TEST ( des_test2_44,
+ 0x0101010101010101, 0x866ecedd8072bb0e, 0x0000000000100000 );
+DES_TEST ( des_test2_45,
+ 0x0101010101010101, 0x8b54536f2f3e64a8, 0x0000000000080000 );
+DES_TEST ( des_test2_46,
+ 0x0101010101010101, 0xea51d3975595b86b, 0x0000000000040000 );
+DES_TEST ( des_test2_47,
+ 0x0101010101010101, 0xcaffc6ac4542de31, 0x0000000000020000 );
+DES_TEST ( des_test2_48,
+ 0x0101010101010101, 0x8dd45a2ddf90796c, 0x0000000000010000 );
+DES_TEST ( des_test2_49,
+ 0x0101010101010101, 0x1029d55e880ec2d0, 0x0000000000008000 );
+DES_TEST ( des_test2_50,
+ 0x0101010101010101, 0x5d86cb23639dbea9, 0x0000000000004000 );
+DES_TEST ( des_test2_51,
+ 0x0101010101010101, 0x1d1ca853ae7c0c5f, 0x0000000000002000 );
+DES_TEST ( des_test2_52,
+ 0x0101010101010101, 0xce332329248f3228, 0x0000000000001000 );
+DES_TEST ( des_test2_53,
+ 0x0101010101010101, 0x8405d1abe24fb942, 0x0000000000000800 );
+DES_TEST ( des_test2_54,
+ 0x0101010101010101, 0xe643d78090ca4207, 0x0000000000000400 );
+DES_TEST ( des_test2_55,
+ 0x0101010101010101, 0x48221b9937748a23, 0x0000000000000200 );
+DES_TEST ( des_test2_56,
+ 0x0101010101010101, 0xdd7c0bbd61fafd54, 0x0000000000000100 );
+DES_TEST ( des_test2_57,
+ 0x0101010101010101, 0x2fbc291a570db5c4, 0x0000000000000080 );
+DES_TEST ( des_test2_58,
+ 0x0101010101010101, 0xe07c30d7e4e26e12, 0x0000000000000040 );
+DES_TEST ( des_test2_59,
+ 0x0101010101010101, 0x0953e2258e8e90a1, 0x0000000000000020 );
+DES_TEST ( des_test2_60,
+ 0x0101010101010101, 0x5b711bc4ceebf2ee, 0x0000000000000010 );
+DES_TEST ( des_test2_61,
+ 0x0101010101010101, 0xcc083f1e6d9e85f6, 0x0000000000000008 );
+DES_TEST ( des_test2_62,
+ 0x0101010101010101, 0xd2fd8867d50d2dfe, 0x0000000000000004 );
+DES_TEST ( des_test2_63,
+ 0x0101010101010101, 0x06e7ea22ce92708f, 0x0000000000000002 );
+DES_TEST ( des_test2_64,
+ 0x0101010101010101, 0x166b40b44aba4bd6, 0x0000000000000001 );
+
+/* Test 3: Data permutation tests
+ *
+ * "Set the plaintext to zero and process the 32 keys in PTEST"
+ *
+ * Appendix B, page 32 ("PTEST")
+ */
+DES_TEST ( des_test3_1,
+ 0x1046913489980131, 0x0000000000000000, 0x88d55e54f54c97b4 );
+DES_TEST ( des_test3_2,
+ 0x1007103489988020, 0x0000000000000000, 0x0c0cc00c83ea48fd );
+DES_TEST ( des_test3_3,
+ 0x10071034c8980120, 0x0000000000000000, 0x83bc8ef3a6570183 );
+DES_TEST ( des_test3_4,
+ 0x1046103489988020, 0x0000000000000000, 0xdf725dcad94ea2e9 );
+DES_TEST ( des_test3_5,
+ 0x1086911519190101, 0x0000000000000000, 0xe652b53b550be8b0 );
+DES_TEST ( des_test3_6,
+ 0x1086911519580101, 0x0000000000000000, 0xaf527120c485cbb0 );
+DES_TEST ( des_test3_7,
+ 0x5107b01519580101, 0x0000000000000000, 0x0f04ce393db926d5 );
+DES_TEST ( des_test3_8,
+ 0x1007b01519190101, 0x0000000000000000, 0xc9f00ffc74079067 );
+DES_TEST ( des_test3_9,
+ 0x3107915498080101, 0x0000000000000000, 0x7cfd82a593252b4e );
+DES_TEST ( des_test3_10,
+ 0x3107919498080101, 0x0000000000000000, 0xcb49a2f9e91363e3 );
+DES_TEST ( des_test3_11,
+ 0x10079115b9080140, 0x0000000000000000, 0x00b588be70d23f56 );
+DES_TEST ( des_test3_12,
+ 0x3107911598080140, 0x0000000000000000, 0x406a9a6ab43399ae );
+DES_TEST ( des_test3_13,
+ 0x1007d01589980101, 0x0000000000000000, 0x6cb773611dca9ada );
+DES_TEST ( des_test3_14,
+ 0x9107911589980101, 0x0000000000000000, 0x67fd21c17dbb5d70 );
+DES_TEST ( des_test3_15,
+ 0x9107d01589190101, 0x0000000000000000, 0x9592cb4110430787 );
+DES_TEST ( des_test3_16,
+ 0x1007d01598980120, 0x0000000000000000, 0xa6b7ff68a318ddd3 );
+DES_TEST ( des_test3_17,
+ 0x1007940498190101, 0x0000000000000000, 0x4d102196c914ca16 );
+DES_TEST ( des_test3_18,
+ 0x0107910491190401, 0x0000000000000000, 0x2dfa9f4573594965 );
+DES_TEST ( des_test3_19,
+ 0x0107910491190101, 0x0000000000000000, 0xb46604816c0e0774 );
+DES_TEST ( des_test3_20,
+ 0x0107940491190401, 0x0000000000000000, 0x6e7e6221a4f34e87 );
+DES_TEST ( des_test3_21,
+ 0x19079210981a0101, 0x0000000000000000, 0xaa85e74643233199 );
+DES_TEST ( des_test3_22,
+ 0x1007911998190801, 0x0000000000000000, 0x2e5a19db4d1962d6 );
+DES_TEST ( des_test3_23,
+ 0x10079119981a0801, 0x0000000000000000, 0x23a866a809d30894 );
+DES_TEST ( des_test3_24,
+ 0x1007921098190101, 0x0000000000000000, 0xd812d961f017d320 );
+DES_TEST ( des_test3_25,
+ 0x100791159819010b, 0x0000000000000000, 0x055605816e58608f );
+DES_TEST ( des_test3_26,
+ 0x1004801598190101, 0x0000000000000000, 0xabd88e8b1b7716f1 );
+DES_TEST ( des_test3_27,
+ 0x1004801598190102, 0x0000000000000000, 0x537ac95be69da1e1 );
+DES_TEST ( des_test3_28,
+ 0x1004801598190108, 0x0000000000000000, 0xaed0f6ae3c25cdd8 );
+DES_TEST ( des_test3_29,
+ 0x1002911498100104, 0x0000000000000000, 0xb3e35a5ee53e7b8d );
+DES_TEST ( des_test3_30,
+ 0x1002911598190104, 0x0000000000000000, 0x61c79c71921a2ef8 );
+DES_TEST ( des_test3_31,
+ 0x1002911598100201, 0x0000000000000000, 0xe2f5728f0995013c );
+DES_TEST ( des_test3_32,
+ 0x1002911698100101, 0x0000000000000000, 0x1aeac39a61f0a464 );
+
+/* Test 4: Key permutation tests
+ *
+ * "Set Data=0 and use the keys e[i]: i=1,...,64 ignoring i=8,16,...,64"
+ *
+ * Test 4 part 1 is the forward direction as described above. Test 4
+ * part 2 ("set data=c[i] from part 1 ... then decipher") is carried
+ * out for us automatically, since CIPHER_TEST() performs both
+ * encryption and decryption tests.
+ *
+ * Appendix B, page 30 ("PC1 and PC2 test")
+ */
+DES_TEST ( des_test4_1,
+ 0x8001010101010101, 0x0000000000000000, 0x95a8d72813daa94d );
+DES_TEST ( des_test4_2,
+ 0x4001010101010101, 0x0000000000000000, 0x0eec1487dd8c26d5 );
+DES_TEST ( des_test4_3,
+ 0x2001010101010101, 0x0000000000000000, 0x7ad16ffb79c45926 );
+DES_TEST ( des_test4_4,
+ 0x1001010101010101, 0x0000000000000000, 0xd3746294ca6a6cf3 );
+DES_TEST ( des_test4_5,
+ 0x0801010101010101, 0x0000000000000000, 0x809f5f873c1fd761 );
+DES_TEST ( des_test4_6,
+ 0x0401010101010101, 0x0000000000000000, 0xc02faffec989d1fc );
+DES_TEST ( des_test4_7,
+ 0x0201010101010101, 0x0000000000000000, 0x4615aa1d33e72f10 );
+DES_TEST ( des_test4_8,
+ 0x0180010101010101, 0x0000000000000000, 0x2055123350c00858 );
+DES_TEST ( des_test4_9,
+ 0x0140010101010101, 0x0000000000000000, 0xdf3b99d6577397c8 );
+DES_TEST ( des_test4_10,
+ 0x0120010101010101, 0x0000000000000000, 0x31fe17369b5288c9 );
+DES_TEST ( des_test4_11,
+ 0x0110010101010101, 0x0000000000000000, 0xdfdd3cc64dae1642 );
+DES_TEST ( des_test4_12,
+ 0x0108010101010101, 0x0000000000000000, 0x178c83ce2b399d94 );
+DES_TEST ( des_test4_13,
+ 0x0104010101010101, 0x0000000000000000, 0x50f636324a9b7f80 );
+DES_TEST ( des_test4_14,
+ 0x0102010101010101, 0x0000000000000000, 0xa8468ee3bc18f06d );
+DES_TEST ( des_test4_15,
+ 0x0101800101010101, 0x0000000000000000, 0xa2dc9e92fd3cde92 );
+DES_TEST ( des_test4_16,
+ 0x0101400101010101, 0x0000000000000000, 0xcac09f797d031287 );
+DES_TEST ( des_test4_17,
+ 0x0101200101010101, 0x0000000000000000, 0x90ba680b22aeb525 );
+DES_TEST ( des_test4_18,
+ 0x0101100101010101, 0x0000000000000000, 0xce7a24f350e280b6 );
+DES_TEST ( des_test4_19,
+ 0x0101080101010101, 0x0000000000000000, 0x882bff0aa01a0b87 );
+DES_TEST ( des_test4_20,
+ 0x0101040101010101, 0x0000000000000000, 0x25610288924511c2 );
+DES_TEST ( des_test4_21,
+ 0x0101020101010101, 0x0000000000000000, 0xc71516c29c75d170 );
+DES_TEST ( des_test4_22,
+ 0x0101018001010101, 0x0000000000000000, 0x5199c29a52c9f059 );
+DES_TEST ( des_test4_23,
+ 0x0101014001010101, 0x0000000000000000, 0xc22f0a294a71f29f );
+DES_TEST ( des_test4_24,
+ 0x0101012001010101, 0x0000000000000000, 0xee371483714c02ea );
+DES_TEST ( des_test4_25,
+ 0x0101011001010101, 0x0000000000000000, 0xa81fbd448f9e522f );
+DES_TEST ( des_test4_26,
+ 0x0101010801010101, 0x0000000000000000, 0x4f644c92e192dfed );
+DES_TEST ( des_test4_27,
+ 0x0101010401010101, 0x0000000000000000, 0x1afa9a66a6df92ae );
+DES_TEST ( des_test4_28,
+ 0x0101010201010101, 0x0000000000000000, 0xb3c1cc715cb879d8 );
+DES_TEST ( des_test4_29,
+ 0x0101010180010101, 0x0000000000000000, 0x19d032e64ab0bd8b );
+DES_TEST ( des_test4_30,
+ 0x0101010140010101, 0x0000000000000000, 0x3cfaa7a7dc8720dc );
+DES_TEST ( des_test4_31,
+ 0x0101010120010101, 0x0000000000000000, 0xb7265f7f447ac6f3 );
+DES_TEST ( des_test4_32,
+ 0x0101010110010101, 0x0000000000000000, 0x9db73b3c0d163f54 );
+DES_TEST ( des_test4_33,
+ 0x0101010108010101, 0x0000000000000000, 0x8181b65babf4a975 );
+DES_TEST ( des_test4_34,
+ 0x0101010104010101, 0x0000000000000000, 0x93c9b64042eaa240 );
+DES_TEST ( des_test4_35,
+ 0x0101010102010101, 0x0000000000000000, 0x5570530829705592 );
+DES_TEST ( des_test4_36,
+ 0x0101010101800101, 0x0000000000000000, 0x8638809e878787a0 );
+DES_TEST ( des_test4_37,
+ 0x0101010101400101, 0x0000000000000000, 0x41b9a79af79ac208 );
+DES_TEST ( des_test4_38,
+ 0x0101010101200101, 0x0000000000000000, 0x7a9be42f2009a892 );
+DES_TEST ( des_test4_39,
+ 0x0101010101100101, 0x0000000000000000, 0x29038d56ba6d2745 );
+DES_TEST ( des_test4_40,
+ 0x0101010101080101, 0x0000000000000000, 0x5495c6abf1e5df51 );
+DES_TEST ( des_test4_41,
+ 0x0101010101040101, 0x0000000000000000, 0xae13dbd561488933 );
+DES_TEST ( des_test4_42,
+ 0x0101010101020101, 0x0000000000000000, 0x024d1ffa8904e389 );
+DES_TEST ( des_test4_43,
+ 0x0101010101018001, 0x0000000000000000, 0xd1399712f99bf02e );
+DES_TEST ( des_test4_44,
+ 0x0101010101014001, 0x0000000000000000, 0x14c1d7c1cffec79e );
+DES_TEST ( des_test4_45,
+ 0x0101010101012001, 0x0000000000000000, 0x1de5279dae3bed6f );
+DES_TEST ( des_test4_46,
+ 0x0101010101011001, 0x0000000000000000, 0xe941a33f85501303 );
+DES_TEST ( des_test4_47,
+ 0x0101010101010801, 0x0000000000000000, 0xda99dbbc9a03f379 );
+DES_TEST ( des_test4_48,
+ 0x0101010101010401, 0x0000000000000000, 0xb7fc92f91d8e92e9 );
+DES_TEST ( des_test4_49,
+ 0x0101010101010201, 0x0000000000000000, 0xae8e5caa3ca04e85 );
+DES_TEST ( des_test4_50,
+ 0x0101010101010180, 0x0000000000000000, 0x9cc62df43b6eed74 );
+DES_TEST ( des_test4_51,
+ 0x0101010101010140, 0x0000000000000000, 0xd863dbb5c59a91a0 );
+DES_TEST ( des_test4_52,
+ 0x0101010101010120, 0x0000000000000000, 0xa1ab2190545b91d7 );
+DES_TEST ( des_test4_53,
+ 0x0101010101010110, 0x0000000000000000, 0x0875041e64c570f7 );
+DES_TEST ( des_test4_54,
+ 0x0101010101010108, 0x0000000000000000, 0x5a594528bebef1cc );
+DES_TEST ( des_test4_55,
+ 0x0101010101010104, 0x0000000000000000, 0xfcdb3291de21f0c0 );
+DES_TEST ( des_test4_56,
+ 0x0101010101010102, 0x0000000000000000, 0x869efd7f9f265a09 );
+
+/* Test 5: S-box tests
+ *
+ * "Set Data and Key equal to the inputs defined in the Substitution
+ * Table test"
+ *
+ * Appendix B, page 33 ("19 key data pairs which exercise every S-box entry")
+ */
+DES_TEST ( des_test5_1,
+ 0x7ca110454a1a6e57, 0x01a1d6d039776742, 0x690f5b0d9a26939b );
+DES_TEST ( des_test5_2,
+ 0x0131d9619dc1376e, 0x5cd54ca83def57da, 0x7a389d10354bd271 );
+DES_TEST ( des_test5_3,
+ 0x07a1133e4a0b2686, 0x0248d43806f67172, 0x868ebb51cab4599a );
+DES_TEST ( des_test5_4,
+ 0x3849674c2602319e, 0x51454b582ddf440a, 0x7178876e01f19b2a );
+DES_TEST ( des_test5_5,
+ 0x04b915ba43feb5b6, 0x42fd443059577fa2, 0xaf37fb421f8c4095 );
+DES_TEST ( des_test5_6,
+ 0x0113b970fd34f2ce, 0x059b5e0851cf143a, 0x86a560f10ec6d85b );
+DES_TEST ( des_test5_7,
+ 0x0170f175468fb5e6, 0x0756d8e0774761d2, 0x0cd3da020021dc09 );
+DES_TEST ( des_test5_8,
+ 0x43297fad38e373fe, 0x762514b829bf486a, 0xea676b2cb7db2b7a );
+DES_TEST ( des_test5_9,
+ 0x07a7137045da2a16, 0x3bdd119049372802, 0xdfd64a815caf1a0f );
+DES_TEST ( des_test5_10,
+ 0x04689104c2fd3b2f, 0x26955f6835af609a, 0x5c513c9c4886c088 );
+DES_TEST ( des_test5_11,
+ 0x37d06bb516cb7546, 0x164d5e404f275232, 0x0a2aeeae3ff4ab77 );
+DES_TEST ( des_test5_12,
+ 0x1f08260d1ac2465e, 0x6b056e18759f5cca, 0xef1bf03e5dfa575a );
+DES_TEST ( des_test5_13,
+ 0x584023641aba6176, 0x004bd6ef09176062, 0x88bf0db6d70dee56 );
+DES_TEST ( des_test5_14,
+ 0x025816164629b007, 0x480d39006ee762f2, 0xa1f9915541020b56 );
+DES_TEST ( des_test5_15,
+ 0x49793ebc79b3258f, 0x437540c8698f3cfa, 0x6fbf1cafcffd0556 );
+DES_TEST ( des_test5_16,
+ 0x4fb05e1515ab73a7, 0x072d43a077075292, 0x2f22e49bab7ca1ac );
+DES_TEST ( des_test5_17,
+ 0x49e95d6d4ca229bf, 0x02fe55778117f12a, 0x5a6b612cc26cce4a );
+DES_TEST ( des_test5_18,
+ 0x018310dc409b26d6, 0x1d9d5c5018f728c2, 0x5f4c038ed12b2e41 );
+DES_TEST ( des_test5_19,
+ 0x1c587f1c13924fef, 0x305532286d6f295a, 0x63fac0d034d9f793 );
+
+/* Unofficial tests
+ *
+ * The official tests are all exactly one block in length. Add some
+ * multi-block tests (generated in Python).
+ */
+CIPHER_TEST ( des_unofficial_ecb, &des_ecb_algorithm,
+ KEY ( 0x6e, 0x6f, 0x70, 0x61, 0x72, 0x69, 0x74, 0x79 ),
+ IV(), ADDITIONAL(),
+ PLAINTEXT ( 0x53, 0x6f, 0x20, 0x63, 0x75, 0x74, 0x65, 0x20,
+ 0x74, 0x6f, 0x20, 0x73, 0x65, 0x65, 0x20, 0x61,
+ 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77,
+ 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x6f,
+ 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x74,
+ 0x75, 0x61, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x21, 0x21 ),
+ CIPHERTEXT ( 0x1a, 0x02, 0x17, 0xcb, 0x93, 0xa3, 0xd2, 0xf2,
+ 0xf9, 0x45, 0x71, 0x1c, 0x33, 0xb1, 0x5c, 0xa4,
+ 0x8b, 0x6b, 0x11, 0x7a, 0x7c, 0x86, 0x7c, 0x7f,
+ 0x9f, 0x56, 0x61, 0x46, 0x7f, 0xa6, 0xae, 0xf1,
+ 0x49, 0xf7, 0x53, 0xe0, 0xbc, 0x15, 0x6a, 0x30,
+ 0xe7, 0xf8, 0xf3, 0x29, 0x11, 0xd8, 0x7d, 0x04,
+ 0x62, 0x5a, 0xaa, 0xa1, 0x89, 0x61, 0x4c, 0xf6,
+ 0x5a, 0x47, 0x3b, 0xc6, 0x04, 0x15, 0xce, 0xf6 ),
+ AUTH() );
+CIPHER_TEST ( des_unofficial_cbc, &des_cbc_algorithm,
+ KEY ( 0x6e, 0x6f, 0x70, 0x61, 0x72, 0x69, 0x74, 0x79 ),
+ IV ( 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 ),
+ ADDITIONAL(),
+ PLAINTEXT ( 0x53, 0x6f, 0x20, 0x63, 0x75, 0x74, 0x65, 0x20,
+ 0x74, 0x6f, 0x20, 0x73, 0x65, 0x65, 0x20, 0x61,
+ 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77,
+ 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x6f,
+ 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x74,
+ 0x75, 0x61, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x21, 0x21 ),
+ CIPHERTEXT ( 0x4c, 0x5f, 0x62, 0xfc, 0xf4, 0x93, 0x09, 0xb5,
+ 0x1d, 0x52, 0x25, 0xec, 0xc7, 0x42, 0x3c, 0x29,
+ 0x33, 0x67, 0xf5, 0xe9, 0xd6, 0x3c, 0x27, 0x5b,
+ 0x49, 0x69, 0xc5, 0xa9, 0x08, 0xa3, 0x14, 0x66,
+ 0x3c, 0x95, 0x33, 0x30, 0xcf, 0x3c, 0x7c, 0xaf,
+ 0xa3, 0xe4, 0xf8, 0x2e, 0xc3, 0x55, 0x57, 0x81,
+ 0x33, 0xd9, 0x90, 0xe2, 0x99, 0xdc, 0x32, 0x10,
+ 0x13, 0x21, 0xb6, 0xc1, 0x6b, 0x0f, 0x22, 0xa9 ),
+ AUTH() );
+
+/**
+ * Perform DES self-test
+ *
+ */
+static void des_test_exec ( void ) {
+
+ /* Sample round outputs (page 9) */
+ cipher_ok ( &des_round_sample );
+
+ /* Test 1: Initial permutation and expansion tests */
+ cipher_ok ( &des_test1_1 );
+ cipher_ok ( &des_test1_2 );
+ cipher_ok ( &des_test1_3 );
+ cipher_ok ( &des_test1_4 );
+ cipher_ok ( &des_test1_5 );
+ cipher_ok ( &des_test1_6 );
+ cipher_ok ( &des_test1_7 );
+ cipher_ok ( &des_test1_8 );
+ cipher_ok ( &des_test1_9 );
+ cipher_ok ( &des_test1_10 );
+ cipher_ok ( &des_test1_11 );
+ cipher_ok ( &des_test1_12 );
+ cipher_ok ( &des_test1_13 );
+ cipher_ok ( &des_test1_14 );
+ cipher_ok ( &des_test1_15 );
+ cipher_ok ( &des_test1_16 );
+ cipher_ok ( &des_test1_17 );
+ cipher_ok ( &des_test1_18 );
+ cipher_ok ( &des_test1_19 );
+ cipher_ok ( &des_test1_20 );
+ cipher_ok ( &des_test1_21 );
+ cipher_ok ( &des_test1_22 );
+ cipher_ok ( &des_test1_23 );
+ cipher_ok ( &des_test1_24 );
+ cipher_ok ( &des_test1_25 );
+ cipher_ok ( &des_test1_26 );
+ cipher_ok ( &des_test1_27 );
+ cipher_ok ( &des_test1_28 );
+ cipher_ok ( &des_test1_29 );
+ cipher_ok ( &des_test1_30 );
+ cipher_ok ( &des_test1_31 );
+ cipher_ok ( &des_test1_32 );
+ cipher_ok ( &des_test1_33 );
+ cipher_ok ( &des_test1_34 );
+ cipher_ok ( &des_test1_35 );
+ cipher_ok ( &des_test1_36 );
+ cipher_ok ( &des_test1_37 );
+ cipher_ok ( &des_test1_38 );
+ cipher_ok ( &des_test1_39 );
+ cipher_ok ( &des_test1_40 );
+ cipher_ok ( &des_test1_41 );
+ cipher_ok ( &des_test1_42 );
+ cipher_ok ( &des_test1_43 );
+ cipher_ok ( &des_test1_44 );
+ cipher_ok ( &des_test1_45 );
+ cipher_ok ( &des_test1_46 );
+ cipher_ok ( &des_test1_47 );
+ cipher_ok ( &des_test1_48 );
+ cipher_ok ( &des_test1_49 );
+ cipher_ok ( &des_test1_50 );
+ cipher_ok ( &des_test1_51 );
+ cipher_ok ( &des_test1_52 );
+ cipher_ok ( &des_test1_53 );
+ cipher_ok ( &des_test1_54 );
+ cipher_ok ( &des_test1_55 );
+ cipher_ok ( &des_test1_56 );
+ cipher_ok ( &des_test1_57 );
+ cipher_ok ( &des_test1_58 );
+ cipher_ok ( &des_test1_59 );
+ cipher_ok ( &des_test1_60 );
+ cipher_ok ( &des_test1_61 );
+ cipher_ok ( &des_test1_62 );
+ cipher_ok ( &des_test1_63 );
+ cipher_ok ( &des_test1_64 );
+
+ /* Test 2: Inverse permutation and expansion tests */
+ cipher_ok ( &des_test2_1 );
+ cipher_ok ( &des_test2_2 );
+ cipher_ok ( &des_test2_3 );
+ cipher_ok ( &des_test2_4 );
+ cipher_ok ( &des_test2_5 );
+ cipher_ok ( &des_test2_6 );
+ cipher_ok ( &des_test2_7 );
+ cipher_ok ( &des_test2_8 );
+ cipher_ok ( &des_test2_9 );
+ cipher_ok ( &des_test2_10 );
+ cipher_ok ( &des_test2_11 );
+ cipher_ok ( &des_test2_12 );
+ cipher_ok ( &des_test2_13 );
+ cipher_ok ( &des_test2_14 );
+ cipher_ok ( &des_test2_15 );
+ cipher_ok ( &des_test2_16 );
+ cipher_ok ( &des_test2_17 );
+ cipher_ok ( &des_test2_18 );
+ cipher_ok ( &des_test2_19 );
+ cipher_ok ( &des_test2_20 );
+ cipher_ok ( &des_test2_21 );
+ cipher_ok ( &des_test2_22 );
+ cipher_ok ( &des_test2_23 );
+ cipher_ok ( &des_test2_24 );
+ cipher_ok ( &des_test2_25 );
+ cipher_ok ( &des_test2_26 );
+ cipher_ok ( &des_test2_27 );
+ cipher_ok ( &des_test2_28 );
+ cipher_ok ( &des_test2_29 );
+ cipher_ok ( &des_test2_30 );
+ cipher_ok ( &des_test2_31 );
+ cipher_ok ( &des_test2_32 );
+ cipher_ok ( &des_test2_33 );
+ cipher_ok ( &des_test2_34 );
+ cipher_ok ( &des_test2_35 );
+ cipher_ok ( &des_test2_36 );
+ cipher_ok ( &des_test2_37 );
+ cipher_ok ( &des_test2_38 );
+ cipher_ok ( &des_test2_39 );
+ cipher_ok ( &des_test2_40 );
+ cipher_ok ( &des_test2_41 );
+ cipher_ok ( &des_test2_42 );
+ cipher_ok ( &des_test2_43 );
+ cipher_ok ( &des_test2_44 );
+ cipher_ok ( &des_test2_45 );
+ cipher_ok ( &des_test2_46 );
+ cipher_ok ( &des_test2_47 );
+ cipher_ok ( &des_test2_48 );
+ cipher_ok ( &des_test2_49 );
+ cipher_ok ( &des_test2_50 );
+ cipher_ok ( &des_test2_51 );
+ cipher_ok ( &des_test2_52 );
+ cipher_ok ( &des_test2_53 );
+ cipher_ok ( &des_test2_54 );
+ cipher_ok ( &des_test2_55 );
+ cipher_ok ( &des_test2_56 );
+ cipher_ok ( &des_test2_57 );
+ cipher_ok ( &des_test2_58 );
+ cipher_ok ( &des_test2_59 );
+ cipher_ok ( &des_test2_60 );
+ cipher_ok ( &des_test2_61 );
+ cipher_ok ( &des_test2_62 );
+ cipher_ok ( &des_test2_63 );
+ cipher_ok ( &des_test2_64 );
+
+ /* Test 3: Data permutation tests */
+ cipher_ok ( &des_test3_1 );
+ cipher_ok ( &des_test3_2 );
+ cipher_ok ( &des_test3_3 );
+ cipher_ok ( &des_test3_4 );
+ cipher_ok ( &des_test3_5 );
+ cipher_ok ( &des_test3_6 );
+ cipher_ok ( &des_test3_7 );
+ cipher_ok ( &des_test3_8 );
+ cipher_ok ( &des_test3_9 );
+ cipher_ok ( &des_test3_10 );
+ cipher_ok ( &des_test3_11 );
+ cipher_ok ( &des_test3_12 );
+ cipher_ok ( &des_test3_13 );
+ cipher_ok ( &des_test3_14 );
+ cipher_ok ( &des_test3_15 );
+ cipher_ok ( &des_test3_16 );
+ cipher_ok ( &des_test3_17 );
+ cipher_ok ( &des_test3_18 );
+ cipher_ok ( &des_test3_19 );
+ cipher_ok ( &des_test3_20 );
+ cipher_ok ( &des_test3_21 );
+ cipher_ok ( &des_test3_22 );
+ cipher_ok ( &des_test3_23 );
+ cipher_ok ( &des_test3_24 );
+ cipher_ok ( &des_test3_25 );
+ cipher_ok ( &des_test3_26 );
+ cipher_ok ( &des_test3_27 );
+ cipher_ok ( &des_test3_28 );
+ cipher_ok ( &des_test3_29 );
+ cipher_ok ( &des_test3_30 );
+ cipher_ok ( &des_test3_31 );
+ cipher_ok ( &des_test3_32 );
+
+ /* Test 4: Key permutation tests */
+ cipher_ok ( &des_test4_1 );
+ cipher_ok ( &des_test4_2 );
+ cipher_ok ( &des_test4_3 );
+ cipher_ok ( &des_test4_4 );
+ cipher_ok ( &des_test4_5 );
+ cipher_ok ( &des_test4_6 );
+ cipher_ok ( &des_test4_7 );
+ cipher_ok ( &des_test4_8 );
+ cipher_ok ( &des_test4_9 );
+ cipher_ok ( &des_test4_10 );
+ cipher_ok ( &des_test4_11 );
+ cipher_ok ( &des_test4_12 );
+ cipher_ok ( &des_test4_13 );
+ cipher_ok ( &des_test4_14 );
+ cipher_ok ( &des_test4_15 );
+ cipher_ok ( &des_test4_16 );
+ cipher_ok ( &des_test4_17 );
+ cipher_ok ( &des_test4_18 );
+ cipher_ok ( &des_test4_19 );
+ cipher_ok ( &des_test4_20 );
+ cipher_ok ( &des_test4_21 );
+ cipher_ok ( &des_test4_22 );
+ cipher_ok ( &des_test4_23 );
+ cipher_ok ( &des_test4_24 );
+ cipher_ok ( &des_test4_25 );
+ cipher_ok ( &des_test4_26 );
+ cipher_ok ( &des_test4_27 );
+ cipher_ok ( &des_test4_28 );
+ cipher_ok ( &des_test4_29 );
+ cipher_ok ( &des_test4_30 );
+ cipher_ok ( &des_test4_31 );
+ cipher_ok ( &des_test4_32 );
+ cipher_ok ( &des_test4_33 );
+ cipher_ok ( &des_test4_34 );
+ cipher_ok ( &des_test4_35 );
+ cipher_ok ( &des_test4_36 );
+ cipher_ok ( &des_test4_37 );
+ cipher_ok ( &des_test4_38 );
+ cipher_ok ( &des_test4_39 );
+ cipher_ok ( &des_test4_40 );
+ cipher_ok ( &des_test4_41 );
+ cipher_ok ( &des_test4_42 );
+ cipher_ok ( &des_test4_43 );
+ cipher_ok ( &des_test4_44 );
+ cipher_ok ( &des_test4_45 );
+ cipher_ok ( &des_test4_46 );
+ cipher_ok ( &des_test4_47 );
+ cipher_ok ( &des_test4_48 );
+ cipher_ok ( &des_test4_49 );
+ cipher_ok ( &des_test4_50 );
+ cipher_ok ( &des_test4_51 );
+ cipher_ok ( &des_test4_52 );
+ cipher_ok ( &des_test4_53 );
+ cipher_ok ( &des_test4_54 );
+ cipher_ok ( &des_test4_55 );
+ cipher_ok ( &des_test4_56 );
+
+ /* Test 5: S-box tests */
+ cipher_ok ( &des_test5_1 );
+ cipher_ok ( &des_test5_2 );
+ cipher_ok ( &des_test5_3 );
+ cipher_ok ( &des_test5_4 );
+ cipher_ok ( &des_test5_5 );
+ cipher_ok ( &des_test5_6 );
+ cipher_ok ( &des_test5_7 );
+ cipher_ok ( &des_test5_8 );
+ cipher_ok ( &des_test5_9 );
+ cipher_ok ( &des_test5_10 );
+ cipher_ok ( &des_test5_11 );
+ cipher_ok ( &des_test5_12 );
+ cipher_ok ( &des_test5_13 );
+ cipher_ok ( &des_test5_14 );
+ cipher_ok ( &des_test5_15 );
+ cipher_ok ( &des_test5_16 );
+ cipher_ok ( &des_test5_17 );
+ cipher_ok ( &des_test5_18 );
+ cipher_ok ( &des_test5_19 );
+
+ /* Multi-block tests */
+ cipher_ok ( &des_unofficial_ecb );
+ cipher_ok ( &des_unofficial_cbc );
+
+ /* Speed tests */
+ DBG ( "DES-ECB encryption required %ld cycles per byte\n",
+ cipher_cost_encrypt ( &des_ecb_algorithm, 8 ) );
+ DBG ( "DES-ECB decryption required %ld cycles per byte\n",
+ cipher_cost_decrypt ( &des_ecb_algorithm, 8 ) );
+ DBG ( "DES-CBC encryption required %ld cycles per byte\n",
+ cipher_cost_encrypt ( &des_cbc_algorithm, 8 ) );
+ DBG ( "DES-CBC decryption required %ld cycles per byte\n",
+ cipher_cost_decrypt ( &des_cbc_algorithm, 8 ) );
+}
+
+/** DES self-test */
+struct self_test des_test __self_test = {
+ .name = "des",
+ .exec = des_test_exec,
+};
diff --git a/src/tests/tests.c b/src/tests/tests.c
index 41c199c0..282d2eb6 100644
--- a/src/tests/tests.c
+++ b/src/tests/tests.c
@@ -82,3 +82,4 @@ REQUIRE_OBJECT ( dhe_test );
REQUIRE_OBJECT ( gcm_test );
REQUIRE_OBJECT ( nap_test );
REQUIRE_OBJECT ( x25519_test );
+REQUIRE_OBJECT ( des_test );