F-6427 - Reject RC2 cipher ops when no key is set

The RC2 encrypt and decrypt operations used the expanded key schedule
without checking that a key had ever been configured. On a zeroed or
otherwise unkeyed context the ECB ops ran over an all-zero schedule and
returned success, and the CBC wrappers inherited the same behavior, so
a caller who skipped wc_Rc2SetKey received ciphertext under an
unintended key with no error signalled.

Guard wc_Rc2EcbEncrypt and wc_Rc2EcbDecrypt on a zero keylen and return
MISSING_KEY when no key has been set. The CBC wrappers call these and
propagate the error. Mirrors the existing 3DES keySet guard.

Add a regression test covering the unkeyed path for all four ops.
This commit is contained in:
Tobias Frauenschläger
2026-06-29 09:14:51 +02:00
parent 5cf136d15a
commit 7562ae5e37
3 changed files with 49 additions and 8 deletions
+31
View File
@@ -228,6 +228,37 @@ int test_wc_Rc2CbcEncryptDecrypt(void)
} /* END test_wc_Rc2CbcEncryptDecrypt */
/*
* Cipher operations must fail safely when no key has been configured.
*/
int test_wc_Rc2_MissingKey(void)
{
EXPECT_DECLS;
#ifdef WC_RC2
Rc2 rc2;
byte out[RC2_BLOCK_SIZE];
byte in[RC2_BLOCK_SIZE];
XMEMSET(out, 0, sizeof(out));
XMEMSET(in, 0, sizeof(in));
/* Zeroed context, never keyed: every op must reject with MISSING_KEY
* rather than ciphering under an all-zero key schedule. */
XMEMSET(&rc2, 0, sizeof(rc2));
ExpectIntEQ(wc_Rc2EcbEncrypt(&rc2, out, in, RC2_BLOCK_SIZE),
WC_NO_ERR_TRACE(MISSING_KEY));
ExpectIntEQ(wc_Rc2EcbDecrypt(&rc2, out, in, RC2_BLOCK_SIZE),
WC_NO_ERR_TRACE(MISSING_KEY));
ExpectIntEQ(wc_Rc2CbcEncrypt(&rc2, out, in, RC2_BLOCK_SIZE),
WC_NO_ERR_TRACE(MISSING_KEY));
ExpectIntEQ(wc_Rc2CbcDecrypt(&rc2, out, in, RC2_BLOCK_SIZE),
WC_NO_ERR_TRACE(MISSING_KEY));
#endif
return EXPECT_RESULT();
} /* END test_wc_Rc2_MissingKey */
#define MC_CIPHER_TEST_COUNT 100
#define MC_RC2_MAX_DATA_SZ 1024
+2
View File
@@ -28,6 +28,7 @@ int test_wc_Rc2SetKey(void);
int test_wc_Rc2SetIV(void);
int test_wc_Rc2EcbEncryptDecrypt(void);
int test_wc_Rc2CbcEncryptDecrypt(void);
int test_wc_Rc2_MissingKey(void);
int test_wc_Rc2Cbc_MonteCarlo(void);
int test_wc_Rc2Free(void);
@@ -36,6 +37,7 @@ int test_wc_Rc2Free(void);
TEST_DECL_GROUP("rc2", test_wc_Rc2SetIV), \
TEST_DECL_GROUP("rc2", test_wc_Rc2EcbEncryptDecrypt), \
TEST_DECL_GROUP("rc2", test_wc_Rc2CbcEncryptDecrypt), \
TEST_DECL_GROUP("rc2", test_wc_Rc2_MissingKey), \
TEST_DECL_GROUP("rc2", test_wc_Rc2Cbc_MonteCarlo), \
TEST_DECL_GROUP("rc2", test_wc_Rc2Free)
+16 -8
View File
@@ -171,6 +171,10 @@ int wc_Rc2EcbEncrypt(Rc2* rc2, byte* out, const byte* in, word32 sz)
return BUFFER_E;
}
if (rc2->keylen == 0) {
return MISSING_KEY;
}
r10 = (word16)((word16)in[1] << 8) | in[0]; /* R[0] */
r32 = (word16)((word16)in[3] << 8) | in[2]; /* R[1] */
r54 = (word16)((word16)in[5] << 8) | in[4]; /* R[2] */
@@ -236,6 +240,10 @@ int wc_Rc2EcbDecrypt(Rc2* rc2, byte* out, const byte* in, word32 sz)
return BUFFER_E;
}
if (rc2->keylen == 0) {
return MISSING_KEY;
}
r0 = (word16)((word16)in[1] << 8) | in[0];
r1 = (word16)((word16)in[3] << 8) | in[2];
r2 = (word16)((word16)in[5] << 8) | in[4];
@@ -285,14 +293,14 @@ int wc_Rc2CbcEncrypt(Rc2* rc2, byte* out, const byte* in, word32 sz)
return BAD_FUNC_ARG;
}
if (sz == 0) {
return 0;
}
if (sz % RC2_BLOCK_SIZE != 0) {
return BAD_LENGTH_E;
}
if (rc2->keylen == 0) {
return MISSING_KEY;
}
blocks = sz / RC2_BLOCK_SIZE;
while (blocks--) {
@@ -320,14 +328,14 @@ int wc_Rc2CbcDecrypt(Rc2* rc2, byte* out, const byte* in, word32 sz)
return BAD_FUNC_ARG;
}
if (sz == 0) {
return 0;
}
if (sz % RC2_BLOCK_SIZE != 0) {
return BAD_LENGTH_E;
}
if (rc2->keylen == 0) {
return MISSING_KEY;
}
blocks = sz / RC2_BLOCK_SIZE;
while (blocks--) {