Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Tatham <anakin@pobox.com>2021-08-27 19:43:40 +0300
committerSimon Tatham <anakin@pobox.com>2021-08-27 19:43:40 +0300
commit23431f8ff454aef29f25627bae2bc08c6dd69af2 (patch)
tree8090789f6d0eaf22dc7df2dbb6273003b94f067e
parent59409d0947ec6d0dc11b4bda8296f68ff088f0f3 (diff)
Add some tests of Miller-Rabin to cryptsuite.
I'm about to rewrite the Miller-Rabin testing code, so let's start by introducing a test suite that the old version passes, and then I can make sure the new one does too.
-rw-r--r--keygen/millerrabin.c29
-rw-r--r--sshkeygen.h8
-rwxr-xr-xtest/cryptsuite.py67
-rw-r--r--test/testcrypt.py2
-rw-r--r--testcrypt.c12
-rw-r--r--testcrypt.h2
6 files changed, 112 insertions, 8 deletions
diff --git a/keygen/millerrabin.c b/keygen/millerrabin.c
index 3358bc51..19ca1bd3 100644
--- a/keygen/millerrabin.c
+++ b/keygen/millerrabin.c
@@ -135,17 +135,19 @@ void miller_rabin_free(MillerRabin *mr)
sfree(mr);
}
-struct mr_result {
- bool passed;
- bool potential_primitive_root;
-};
-
-static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *w)
+/*
+ * The main internal function that implements a single M-R test.
+ *
+ * Expects the witness integer to be in Montgomery representation.
+ * (Since in live use witnesses are invented at random, this imposes
+ * no extra cost on the callers, and saves effort in here.)
+ */
+static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *mw)
{
/*
* Compute w^q mod p.
*/
- mp_int *wqp = monty_pow(mr->mc, w, mr->q);
+ mp_int *wqp = monty_pow(mr->mc, mw, mr->q);
/*
* See if this is 1, or if it is -1, or if it becomes -1
@@ -175,6 +177,19 @@ static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *w)
return result;
}
+/*
+ * Wrapper on miller_rabin_test_inner for the convenience of
+ * testcrypt. Expects the witness integer to be literal, so we
+ * monty_import it before running the real test.
+ */
+struct mr_result miller_rabin_test(MillerRabin *mr, mp_int *w)
+{
+ mp_int *mw = monty_import(mr->mc, w);
+ struct mr_result result = miller_rabin_test_inner(mr, mw);
+ mp_free(mw);
+ return result;
+}
+
bool miller_rabin_test_random(MillerRabin *mr)
{
mp_int *mw = mp_random_in_range(mr->two, mr->pm1);
diff --git a/sshkeygen.h b/sshkeygen.h
index dc0024b9..fae6fa83 100644
--- a/sshkeygen.h
+++ b/sshkeygen.h
@@ -94,6 +94,14 @@ typedef struct MillerRabin MillerRabin;
MillerRabin *miller_rabin_new(mp_int *p);
void miller_rabin_free(MillerRabin *mr);
+/* Perform a single Miller-Rabin test, using a specified witness value.
+ * Used in the test suite. */
+struct mr_result {
+ bool passed;
+ bool potential_primitive_root;
+};
+struct mr_result miller_rabin_test(MillerRabin *mr, mp_int *w);
+
/* Perform a single Miller-Rabin test, using a random witness value. */
bool miller_rabin_test_random(MillerRabin *mr);
diff --git a/test/cryptsuite.py b/test/cryptsuite.py
index c4df264f..2993dbd4 100755
--- a/test/cryptsuite.py
+++ b/test/cryptsuite.py
@@ -1188,6 +1188,73 @@ class keygen(MyTestBase):
self.assertEqual(pockle_add_prime(po, 1, [2], 1),
'POCKLE_PRIME_SMALLER_THAN_2')
+ def testMillerRabin(self):
+ # A prime congruent to 3 mod 4, so M-R can only do one
+ # iteration: either a^{(p-1)/2} == +1, or -1. Either counts as
+ # a pass; the latter also means the number is potentially a
+ # primitive root.
+ n = 0xe76e6aaa42b5d7423aa4da5613eb21c3
+ mr = miller_rabin_new(n)
+ self.assertEqual(miller_rabin_test(mr, 2), "passed+ppr")
+ self.assertEqual(miller_rabin_test(mr, 4), "passed")
+
+ # The 'potential primitive root' test only means that M-R
+ # didn't _rule out_ the number being a primitive root, by
+ # finding that any of the powers _it tested_ less than n-1
+ # came out to be 1. In this case, 2 really is a primitive
+ # root, but since 13 | n-1, the 13th powers mod n form a
+ # multiplicative subgroup. So 2^13 is not a primitive root,
+ # and yet, M-R can't tell the difference, because it only
+ # tried the exponent (n-1)/2, not the actual counterexample
+ # (n-1)/13.
+ self.assertEqual(miller_rabin_test(mr, 2**13), "passed+ppr")
+
+ # A prime congruent to 1 mod a reasonably large power of 2, so
+ # M-R has lots of scope to have different things happen. 3 is
+ # a primitive root, so we expect that 3, 3^2, 3^4, ..., 3^256
+ # should all pass for different reasons, with only the first
+ # of them returning passed+ppr.
+ n = 0xb1b65ebe489ff0ab4597bb67c3d22d01
+ mr = miller_rabin_new(n)
+ w = 3
+ self.assertEqual(miller_rabin_test(mr, w), "passed+ppr")
+ for i in range(1, 10):
+ w = w * w % n
+ self.assertEqual(miller_rabin_test(mr, w), "passed")
+
+ # A prime with an _absurdly_ large power-of-2 factor in its
+ # multiplicative group.
+ n = 0x600000000000000000000000000000000000000000000001
+ mr = miller_rabin_new(n)
+ w = 10
+ self.assertEqual(miller_rabin_test(mr, w), "passed+ppr")
+ for i in range(1, 200):
+ w = w * w % n
+ self.assertEqual(miller_rabin_test(mr, w), "passed")
+
+ # A blatantly composite number. But we still expect to see a
+ # pass if we give the witness 1 (which will give a maximal
+ # trailing string of 1s), or -1 (which will give -1 when
+ # raised to the maximal odd factor of n-1, or indeed any other
+ # odd power).
+ n = 0x1010101010101010101010101010101
+ mr = miller_rabin_new(n)
+ self.assertEqual(miller_rabin_test(mr, 1), "passed")
+ self.assertEqual(miller_rabin_test(mr, n-1), "passed")
+ self.assertEqual(miller_rabin_test(mr, 2), "failed")
+
+ # A Carmichael number, as a proper test that M-R detects
+ # things the Fermat test would not.
+ #
+ # (Its prime factorisation is 26823115100268314289505807 *
+ # 53646230200536628579011613 * 80469345300804942868517419,
+ # which is enough to re-check its Carmichaelness.)
+ n = 0xffffffffffffffffcf8032f3e044b4a8b1b1bf0b526538eae953d90f44d65511
+ mr = miller_rabin_new(n)
+ self.assertEqual(miller_rabin_test(mr, 16), "passed")
+ assert(pow(2, n-1, n) == 1) # Fermat test would pass, but ...
+ self.assertEqual(miller_rabin_test(mr, 2), "failed") # ... this fails
+
class crypt(MyTestBase):
def testSSH1Fingerprint(self):
# Example key and reference fingerprint value generated by
diff --git a/test/testcrypt.py b/test/testcrypt.py
index 686302c8..66611ed3 100644
--- a/test/testcrypt.py
+++ b/test/testcrypt.py
@@ -235,7 +235,7 @@ def make_retval(rettype, word, unpack_strings):
elif rettype == "boolean":
assert word == b"true" or word == b"false"
return word == b"true"
- elif rettype == "pocklestatus":
+ elif rettype in {"pocklestatus", "mr_result"}:
return word.decode("ASCII")
raise TypeError("Can't deal with return value {!r} of type {!r}"
.format(word, rettype))
diff --git a/testcrypt.c b/testcrypt.c
index c5c66b4b..1b875c2b 100644
--- a/testcrypt.c
+++ b/testcrypt.c
@@ -94,6 +94,7 @@ uint64_t prng_reseed_time_ms(void)
X(pcs, PrimeCandidateSource *, pcs_free(v)) \
X(pgc, PrimeGenerationContext *, primegen_free_context(v)) \
X(pockle, Pockle *, pockle_free(v)) \
+ X(millerrabin, MillerRabin *, miller_rabin_free(v)) \
/* end of list */
typedef struct Value Value;
@@ -707,6 +708,16 @@ static void return_pocklestatus(strbuf *out, PockleStatus status)
}
}
+static void return_mr_result(strbuf *out, struct mr_result result)
+{
+ if (!result.passed)
+ strbuf_catf(out, "failed\n");
+ else if (!result.potential_primitive_root)
+ strbuf_catf(out, "passed\n");
+ else
+ strbuf_catf(out, "passed+ppr\n");
+}
+
static void return_val_string_asciz_const(strbuf *out, const char *s)
{
strbuf *sb = strbuf_new();
@@ -1370,6 +1381,7 @@ typedef key_components *TD_keycomponents;
typedef const PrimeGenerationPolicy *TD_primegenpolicy;
typedef struct mpint_list TD_mpint_list;
typedef PockleStatus TD_pocklestatus;
+typedef struct mr_result TD_mr_result;
typedef Argon2Flavour TD_argon2flavour;
typedef FingerprintType TD_fptype;
diff --git a/testcrypt.h b/testcrypt.h
index 2e6e993b..7e0ef3cf 100644
--- a/testcrypt.h
+++ b/testcrypt.h
@@ -297,6 +297,8 @@ FUNC2(void, pockle_release, val_pockle, uint)
FUNC2(pocklestatus, pockle_add_small_prime, val_pockle, val_mpint)
FUNC4(pocklestatus, pockle_add_prime, val_pockle, val_mpint, mpint_list, val_mpint)
FUNC2(val_string, pockle_mpu, val_pockle, val_mpint)
+FUNC1(val_millerrabin, miller_rabin_new, val_mpint)
+FUNC2(mr_result, miller_rabin_test, val_millerrabin, val_mpint)
/*
* Miscellaneous.