mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
Add tests of the CRC compensation detector.
I remembered the existence of that module while I was changing the API of the CRC functions. It's still quite possibly the only code in PuTTY not written specifically _for_ PuTTY, so it definitely deserves a bit of a test suite. In order to expose it through the ptrlen-centric testcrypt system, I've added some missing 'const' in the detector module itself, but otherwise I've left the detector code as it was.
This commit is contained in:
parent
2e866e1fb7
commit
8611e2f035
4
Recipe
4
Recipe
@ -254,10 +254,10 @@ ARITH = mpint ecc
|
|||||||
SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512
|
SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512
|
||||||
+ sshrsa sshdss sshecc
|
+ sshrsa sshdss sshecc
|
||||||
+ sshdes sshblowf sshaes sshccp ssharcf
|
+ sshdes sshblowf sshaes sshccp ssharcf
|
||||||
+ sshdh sshcrc
|
+ sshdh sshcrc sshcrcda
|
||||||
SSHCOMMON = sshcommon sshrand SSHCRYPTO
|
SSHCOMMON = sshcommon sshrand SSHCRYPTO
|
||||||
+ sshverstring
|
+ sshverstring
|
||||||
+ sshcrcda sshpubk sshzlib
|
+ sshpubk sshzlib
|
||||||
+ sshmac marshal nullplug
|
+ sshmac marshal nullplug
|
||||||
+ sshgssc pgssapi wildcard ssh1censor ssh2censor ssh2bpp
|
+ sshgssc pgssapi wildcard ssh1censor ssh2censor ssh2bpp
|
||||||
+ ssh2transport ssh2transhk ssh2connection portfwd x11fwd
|
+ ssh2transport ssh2transhk ssh2connection portfwd x11fwd
|
||||||
|
5
ssh.h
5
ssh.h
@ -520,8 +520,9 @@ uint32_t crc32_update(uint32_t crc_input, ptrlen data);
|
|||||||
struct crcda_ctx;
|
struct crcda_ctx;
|
||||||
struct crcda_ctx *crcda_make_context(void);
|
struct crcda_ctx *crcda_make_context(void);
|
||||||
void crcda_free_context(struct crcda_ctx *ctx);
|
void crcda_free_context(struct crcda_ctx *ctx);
|
||||||
bool detect_attack(struct crcda_ctx *ctx, unsigned char *buf, uint32_t len,
|
bool detect_attack(struct crcda_ctx *ctx,
|
||||||
unsigned char *IV);
|
const unsigned char *buf, uint32_t len,
|
||||||
|
const unsigned char *IV);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSH2 RSA key exchange functions
|
* SSH2 RSA key exchange functions
|
||||||
|
14
sshcrcda.c
14
sshcrcda.c
@ -75,10 +75,11 @@ static void crc_update(uint32_t *a, const void *b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* detect if a block is used in a particular pattern */
|
/* detect if a block is used in a particular pattern */
|
||||||
static bool check_crc(uint8_t *S, uint8_t *buf, uint32_t len, uint8_t *IV)
|
static bool check_crc(const uint8_t *S, const uint8_t *buf,
|
||||||
|
uint32_t len, const uint8_t *IV)
|
||||||
{
|
{
|
||||||
uint32_t crc;
|
uint32_t crc;
|
||||||
uint8_t *c;
|
const uint8_t *c;
|
||||||
|
|
||||||
crc = 0;
|
crc = 0;
|
||||||
if (IV && !CMP(S, IV)) {
|
if (IV && !CMP(S, IV)) {
|
||||||
@ -98,13 +99,14 @@ static bool check_crc(uint8_t *S, uint8_t *buf, uint32_t len, uint8_t *IV)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Detect a crc32 compensation attack on a packet */
|
/* Detect a crc32 compensation attack on a packet */
|
||||||
bool detect_attack(
|
bool detect_attack(struct crcda_ctx *ctx,
|
||||||
struct crcda_ctx *ctx, uint8_t *buf, uint32_t len, uint8_t *IV)
|
const unsigned char *buf, uint32_t len,
|
||||||
|
const unsigned char *IV)
|
||||||
{
|
{
|
||||||
register uint32_t i, j;
|
register uint32_t i, j;
|
||||||
uint32_t l;
|
uint32_t l;
|
||||||
register uint8_t *c;
|
register const uint8_t *c;
|
||||||
uint8_t *d;
|
const uint8_t *d;
|
||||||
|
|
||||||
assert(!(len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
|
assert(!(len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
|
||||||
len % SSH_BLOCKSIZE != 0));
|
len % SSH_BLOCKSIZE != 0));
|
||||||
|
@ -966,6 +966,65 @@ class crypt(MyTestBase):
|
|||||||
# we're at it!
|
# we're at it!
|
||||||
self.assertEqual(shift8(i ^ prior), exp)
|
self.assertEqual(shift8(i ^ prior), exp)
|
||||||
|
|
||||||
|
def testCRCDA(self):
|
||||||
|
def pattern(badblk, otherblks, pat):
|
||||||
|
# Arrange copies of the bad block in a pattern
|
||||||
|
# corresponding to the given bit string.
|
||||||
|
retstr = ""
|
||||||
|
while pat != 0:
|
||||||
|
retstr += (badblk if pat & 1 else next(otherblks))
|
||||||
|
pat >>= 1
|
||||||
|
return retstr
|
||||||
|
|
||||||
|
def testCases(pat):
|
||||||
|
badblock = b'muhahaha' # the block we'll maliciously repeat
|
||||||
|
|
||||||
|
# Various choices of the other blocks, including all the
|
||||||
|
# same, all different, and all different but only in the
|
||||||
|
# byte at one end.
|
||||||
|
for otherblocks in [
|
||||||
|
itertools.repeat(b'GoodData'),
|
||||||
|
(struct.pack('>Q', i) for i in itertools.count()),
|
||||||
|
(struct.pack('<Q', i) for i in itertools.count())]:
|
||||||
|
yield pattern(badblock, otherblocks, pat)
|
||||||
|
|
||||||
|
def positiveTest(pat):
|
||||||
|
for data in testCases(pat):
|
||||||
|
self.assertTrue(crcda_detect(data, ""))
|
||||||
|
self.assertTrue(crcda_detect(data[8:], data[:8]))
|
||||||
|
|
||||||
|
def negativeTest(pat):
|
||||||
|
for data in testCases(pat):
|
||||||
|
self.assertFalse(crcda_detect(data, ""))
|
||||||
|
self.assertFalse(crcda_detect(data[8:], data[:8]))
|
||||||
|
|
||||||
|
# Tests of successful attack detection, derived by taking
|
||||||
|
# multiples of the CRC polynomial itself.
|
||||||
|
#
|
||||||
|
# (The CRC32 polynomial is usually written as 0xEDB88320.
|
||||||
|
# That's in bit-reversed form, but then, that's the form we
|
||||||
|
# need anyway for these patterns. But it's also missing the
|
||||||
|
# leading term - really, 0xEDB88320 is the value you get by
|
||||||
|
# reducing X^32 modulo the real poly, i.e. the value you put
|
||||||
|
# back in to the CRC to compensate for an X^32 that's just
|
||||||
|
# been shifted out. If you put that bit back on - at the
|
||||||
|
# bottom, because of the bit-reversal - you get the less
|
||||||
|
# familiar-looking 0x1db710641.)
|
||||||
|
positiveTest(0x1db710641) # the CRC polynomial P itself
|
||||||
|
positiveTest(0x26d930ac3) # (X+1) * P
|
||||||
|
positiveTest(0xbdbdf21cf) # (X^3+X^2+X+1) * P
|
||||||
|
positiveTest(0x3a66a39b653f6889d)
|
||||||
|
positiveTest(0x170db3167dd9f782b9765214c03e71a18f685b7f3)
|
||||||
|
positiveTest(0x1751997d000000000000000000000000000000001)
|
||||||
|
positiveTest(0x800000000000000000000000000000000f128a2d1)
|
||||||
|
|
||||||
|
# Tests of non-detection.
|
||||||
|
negativeTest(0x1db711a41)
|
||||||
|
negativeTest(0x3a66a39b453f6889d)
|
||||||
|
negativeTest(0x170db3167dd9f782b9765214c03e71b18f685b7f3)
|
||||||
|
negativeTest(0x1751997d000000000000000000000001000000001)
|
||||||
|
negativeTest(0x800000000000002000000000000000000f128a2d1)
|
||||||
|
|
||||||
class standard_test_vectors(MyTestBase):
|
class standard_test_vectors(MyTestBase):
|
||||||
def testAES(self):
|
def testAES(self):
|
||||||
def vector(cipher, key, plaintext, ciphertext):
|
def vector(cipher, key, plaintext, ciphertext):
|
||||||
|
13
testcrypt.c
13
testcrypt.c
@ -827,6 +827,19 @@ strbuf *des_decrypt_xdmauth_wrapper(ptrlen key, ptrlen data)
|
|||||||
}
|
}
|
||||||
#define des_decrypt_xdmauth des_decrypt_xdmauth_wrapper
|
#define des_decrypt_xdmauth des_decrypt_xdmauth_wrapper
|
||||||
|
|
||||||
|
bool crcda_detect(ptrlen packet, ptrlen iv)
|
||||||
|
{
|
||||||
|
if (iv.len != 0 && iv.len != 8)
|
||||||
|
fatal_error("crcda_detect: iv must be empty or 8 bytes long");
|
||||||
|
if (packet.len % 8 != 0)
|
||||||
|
fatal_error("crcda_detect: packet must be a multiple of 8 bytes");
|
||||||
|
struct crcda_ctx *ctx = crcda_make_context();
|
||||||
|
bool toret = detect_attack(ctx, packet.ptr, packet.len,
|
||||||
|
iv.len ? iv.ptr : NULL);
|
||||||
|
crcda_free_context(ctx);
|
||||||
|
return toret;
|
||||||
|
}
|
||||||
|
|
||||||
#define return_void(out, expression) (expression)
|
#define return_void(out, expression) (expression)
|
||||||
|
|
||||||
#define VALTYPE_TYPEDEF(n,t,f) \
|
#define VALTYPE_TYPEDEF(n,t,f) \
|
||||||
|
@ -221,6 +221,7 @@ FUNC2(val_string, des_decrypt_xdmauth, val_string_ptrlen, val_string_ptrlen)
|
|||||||
FUNC1(uint, crc32_rfc1662, val_string_ptrlen)
|
FUNC1(uint, crc32_rfc1662, val_string_ptrlen)
|
||||||
FUNC1(uint, crc32_ssh1, val_string_ptrlen)
|
FUNC1(uint, crc32_ssh1, val_string_ptrlen)
|
||||||
FUNC2(uint, crc32_update, uint, val_string_ptrlen)
|
FUNC2(uint, crc32_update, uint, val_string_ptrlen)
|
||||||
|
FUNC2(boolean, crcda_detect, val_string_ptrlen, val_string_ptrlen)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These functions aren't part of PuTTY's own API, but are additions
|
* These functions aren't part of PuTTY's own API, but are additions
|
||||||
|
Loading…
Reference in New Issue
Block a user