Skip to content

Commit ba94617

Browse files
authored
Better support legacy DES customers (#1671)
### Issues: Addresses CryptoAlg-2421 ### Description of changes: No one should start using these DES functions, or continue using DES in general. However, for legacy customers that can't change this PR adds a few small utility functions and aligns AWS-LC with the behavior they expect from OpenSSL. ### Call-outs: I updated DES_set_key to perform the same checks as OpenSSL and updated internal usages to use DES_set_key_unchecked. ### Testing: Added new tests and ensured existing tests still pass. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.
1 parent 8415b37 commit ba94617

File tree

6 files changed

+142
-18
lines changed

6 files changed

+142
-18
lines changed

crypto/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ if(BUILD_TESTING)
721721
digest_extra/digest_test.cc
722722
dilithium/p_dilithium_test.cc
723723
dsa/dsa_test.cc
724+
des/des_test.cc
724725
endian_test.cc
725726
err/err_test.cc
726727
evp_extra/evp_extra_test.cc

crypto/cipher_extra/e_des.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static int des_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
7474
DES_cblock *deskey = (DES_cblock *)key;
7575
EVP_DES_KEY *dat = (EVP_DES_KEY *)ctx->cipher_data;
7676

77-
DES_set_key(deskey, &dat->ks.ks);
77+
DES_set_key_unchecked(deskey, &dat->ks.ks);
7878
return 1;
7979
}
8080

@@ -147,9 +147,9 @@ static int des_ede3_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
147147
DES_cblock *deskey = (DES_cblock *)key;
148148
DES_EDE_KEY *dat = (DES_EDE_KEY *)ctx->cipher_data;
149149

150-
DES_set_key(&deskey[0], &dat->ks.ks[0]);
151-
DES_set_key(&deskey[1], &dat->ks.ks[1]);
152-
DES_set_key(&deskey[2], &dat->ks.ks[2]);
150+
DES_set_key_unchecked(&deskey[0], &dat->ks.ks[0]);
151+
DES_set_key_unchecked(&deskey[1], &dat->ks.ks[1]);
152+
DES_set_key_unchecked(&deskey[2], &dat->ks.ks[2]);
153153

154154
return 1;
155155
}
@@ -185,9 +185,9 @@ static int des_ede_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
185185
DES_cblock *deskey = (DES_cblock *)key;
186186
DES_EDE_KEY *dat = (DES_EDE_KEY *)ctx->cipher_data;
187187

188-
DES_set_key(&deskey[0], &dat->ks.ks[0]);
189-
DES_set_key(&deskey[1], &dat->ks.ks[1]);
190-
DES_set_key(&deskey[0], &dat->ks.ks[2]);
188+
DES_set_key_unchecked(&deskey[0], &dat->ks.ks[0]);
189+
DES_set_key_unchecked(&deskey[1], &dat->ks.ks[1]);
190+
DES_set_key_unchecked(&deskey[0], &dat->ks.ks[2]);
191191

192192
return 1;
193193
}

crypto/des/des.c

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ static const uint32_t DES_SPtrans[8][64] = {
293293
((t) = ((((a) << (16 - (n))) ^ (a)) & (m)), \
294294
(a) = (a) ^ (t) ^ ((t) >> (16 - (n))))
295295

296-
void DES_set_key(const DES_cblock *key, DES_key_schedule *schedule) {
296+
void DES_set_key_unchecked(const DES_cblock *key, DES_key_schedule *schedule) {
297297
static const int shifts2[16] = {0, 0, 1, 1, 1, 1, 1, 1,
298298
0, 1, 1, 1, 1, 1, 1, 0};
299299
uint32_t c, d, t, s, t2;
@@ -349,9 +349,38 @@ void DES_set_key(const DES_cblock *key, DES_key_schedule *schedule) {
349349
}
350350
}
351351

352+
// SP 800-67r2 section 2, the last bit of each byte in DES_cblock.bytes is used
353+
// for parity. The parity bits should be set to the complement of the modulo 2
354+
// sum of the previous seven bits
355+
static int DES_check_key_parity(const DES_cblock *key) {
356+
uint8_t result = UINT8_MAX;
357+
358+
for (size_t i = 0; i < DES_KEY_SZ; i++) {
359+
uint8_t b = key->bytes[i];
360+
b ^= b >> 4;
361+
b ^= b >> 2;
362+
b ^= b >> 1;
363+
result &= constant_time_eq_8(b & 1, 1);
364+
}
365+
return result & 1;
366+
}
367+
368+
int DES_set_key(const DES_cblock *key, DES_key_schedule *schedule)
369+
{
370+
int result = 0;
371+
372+
if (!DES_check_key_parity(key)) {
373+
result = -1;
374+
}
375+
if (DES_is_weak_key(key)) {
376+
result = -2;
377+
}
378+
DES_set_key_unchecked(key, schedule);
379+
return result;
380+
}
381+
352382
int DES_key_sched(const DES_cblock *key, DES_key_schedule *schedule) {
353-
DES_set_key(key, schedule);
354-
return 1;
383+
return DES_set_key(key, schedule);
355384
}
356385

357386
static const uint8_t kOddParity[256] = {
@@ -383,6 +412,39 @@ void DES_set_odd_parity(DES_cblock *key) {
383412
}
384413
}
385414

415+
// Weak keys have unintended behaviors which may hurt the security of their use
416+
// see SP 800-67r2 section 3.3.2
417+
static const DES_cblock weak_keys[] = {
418+
// Weak keys: encryption is equal to decryption (encrypting twice produces the original plaintext)
419+
{{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}},
420+
{{0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE}},
421+
{{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E}},
422+
{{0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1}},
423+
// Semi-weak keys: encryption with one of these keys is equal to encryption with a different key
424+
{{0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE}},
425+
{{0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01}},
426+
{{0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1}},
427+
{{0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E}},
428+
{{0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1}},
429+
{{0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01}},
430+
{{0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE}},
431+
{{0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E}},
432+
{{0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E}},
433+
{{0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01}},
434+
{{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE}},
435+
{{0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1}}
436+
};
437+
438+
int DES_is_weak_key(const DES_cblock *key)
439+
{
440+
crypto_word_t result = 0;
441+
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(weak_keys); i++) {
442+
int match = CRYPTO_memcmp(&weak_keys[i], key, sizeof(DES_cblock));
443+
result |= constant_time_is_zero_w(match);
444+
}
445+
return (int)(result & 1);
446+
}
447+
386448
static void DES_encrypt1(uint32_t *data, const DES_key_schedule *ks, int enc) {
387449
uint32_t l, r, t, u;
388450

crypto/des/des_test.cc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR ISC
3+
4+
#include <openssl/des.h>
5+
6+
#include <gtest/gtest.h>
7+
8+
TEST(DESTest, WeakKeys) {
9+
// The all 2 key is not weak and has odd parity
10+
DES_cblock validKey = {{2, 2, 2, 2, 2, 2, 2, 2}};
11+
EXPECT_FALSE(DES_is_weak_key(&validKey));
12+
DES_key_schedule des;
13+
EXPECT_EQ(0, DES_set_key(&validKey, &des));
14+
15+
// Weak key example from SP 800-67r2 section 3.3.2
16+
static const DES_cblock weakKey = {{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}};
17+
EXPECT_TRUE(DES_is_weak_key(&weakKey));
18+
EXPECT_EQ(-2, DES_set_key(&weakKey, &des));
19+
}
20+
21+
// If it wasn't for MSVC this could be __builtin_popcount, if this was C++20
22+
// it could be std::popcount
23+
static int countSetBits(uint8_t n) {
24+
int count = 0;
25+
while (n) {
26+
count += n & 1;
27+
n >>= 1;
28+
}
29+
return count;
30+
}
31+
32+
TEST(DESTest, Parity) {
33+
// The all 2 key is not weak and has odd parity for each byte
34+
DES_cblock key = {{2, 2, 2, 2, 2, 2, 2, 2}};
35+
DES_key_schedule des;
36+
int result = DES_set_key(&key, &des);
37+
EXPECT_EQ(result, 0);
38+
39+
for (uint8_t i = 0; i < 255; i++) {
40+
key.bytes[0] = i;
41+
result = DES_set_key(&key, &des);
42+
int bitsSet = countSetBits(i);
43+
int oddParity = bitsSet % 2 == 1;
44+
if (oddParity) {
45+
EXPECT_EQ(result, 0);
46+
} else {
47+
EXPECT_EQ(result, -1);
48+
}
49+
}
50+
}

include/openssl/des.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,22 @@ typedef struct DES_ks {
9191
#define DES_CBC_MODE 0
9292
#define DES_PCBC_MODE 1
9393

94-
// DES_set_key performs a key schedule and initialises |schedule| with |key|.
95-
OPENSSL_EXPORT void DES_set_key(const DES_cblock *key,
96-
DES_key_schedule *schedule);
97-
98-
// DES_key_sched calls |DES_set_key| and returns 1.
94+
// DES_is_weak_key checks if |key| is a weak or semi-weak key, see SP 800-67r2
95+
// section 3.3.2
96+
OPENSSL_EXPORT int DES_is_weak_key(const DES_cblock *key);
97+
98+
// DES_set_key checks that |key| is not weak and the parity before calling
99+
// |DES_set_key_unchecked|. The key schedule is always initialized, the checks
100+
// only affect the return value:
101+
// 0: key is not weak and has odd parity
102+
// -1: key is not odd
103+
// -2: key is a weak key, the parity might also be even
104+
OPENSSL_EXPORT int DES_set_key(const DES_cblock *key, DES_key_schedule *schedule);
105+
106+
// DES_set_key_unchecked performs a key schedule and initialises |schedule| with |key|.
107+
OPENSSL_EXPORT void DES_set_key_unchecked(const DES_cblock *key, DES_key_schedule *schedule);
108+
109+
// DES_key_sched calls |DES_set_key|.
99110
OPENSSL_EXPORT int DES_key_sched(const DES_cblock *key, DES_key_schedule *schedule);
100111

101112
// DES_set_odd_parity sets the parity bits (the least-significant bits in each

util/fipstools/test_fips.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ int main(int argc, char **argv) {
276276

277277
DES_key_schedule des1, des2, des3;
278278
DES_cblock des_iv;
279-
DES_set_key(&kDESKey1, &des1);
280-
DES_set_key(&kDESKey2, &des2);
281-
DES_set_key(&kDESKey3, &des3);
279+
DES_set_key_unchecked(&kDESKey1, &des1);
280+
DES_set_key_unchecked(&kDESKey2, &des2);
281+
DES_set_key_unchecked(&kDESKey3, &des3);
282282

283283
/* 3DES Encryption */
284284
memcpy(&des_iv, &kDESIV, sizeof(des_iv));

0 commit comments

Comments
 (0)