diff options
| author | tb <> | 2022-07-07 20:01:20 +0000 |
|---|---|---|
| committer | tb <> | 2022-07-07 20:01:20 +0000 |
| commit | 002ae27954b38334a7719d7d2952c9c39bfb91a4 (patch) | |
| tree | 676c18a467cb0fc9fdc91292b157f3fe2fe38905 /src | |
| parent | 2b09100ba4d091bcf9d98062713b8967920569fd (diff) | |
| download | openbsd-002ae27954b38334a7719d7d2952c9c39bfb91a4.tar.gz openbsd-002ae27954b38334a7719d7d2952c9c39bfb91a4.tar.bz2 openbsd-002ae27954b38334a7719d7d2952c9c39bfb91a4.zip | |
Add support for primality checking
Project Wycheproof's primality_tests.json contain a set of 280 numbers
that trigger edge cases in Miller-Rabin and related checks. libcrypto's
Miller-Rabin test is known to be rather poor, hopefully we will soon see
a diff on tech that improves on this.
This extends the Go test in the usual way and also adds a perl script
that allows testing on non-Go architectures.
Deliberately not yet linked to regress since the tests are flaky with
the current BN_is_prime_ex() implementatation.
Diffstat (limited to 'src')
4 files changed, 218 insertions, 10 deletions
diff --git a/src/regress/lib/libcrypto/wycheproof/Makefile b/src/regress/lib/libcrypto/wycheproof/Makefile index 0fcde086f7..2e1d16b165 100644 --- a/src/regress/lib/libcrypto/wycheproof/Makefile +++ b/src/regress/lib/libcrypto/wycheproof/Makefile | |||
| @@ -1,17 +1,21 @@ | |||
| 1 | # $OpenBSD: Makefile,v 1.3 2019/04/24 20:25:19 bluhm Exp $ | 1 | # $OpenBSD: Makefile,v 1.4 2022/07/07 20:01:20 tb Exp $ |
| 2 | 2 | ||
| 3 | .if ! (make(clean) || make(cleandir) || make(obj)) | 3 | WYCHEPROOF_TESTVECTORS = /usr/local/share/wycheproof/testvectors/ |
| 4 | GO_VERSION != sh -c "(go version) 2>/dev/null || true" | ||
| 5 | .endif | ||
| 6 | 4 | ||
| 7 | .if empty(GO_VERSION) | 5 | .if !exists(${WYCHEPROOF_TESTVECTORS}) |
| 8 | regress: | 6 | regress: |
| 9 | @echo package go is required for this regress | 7 | @echo package wycheproof-testvectors is required for this regress |
| 8 | @echo package go should be installed if available | ||
| 10 | @echo SKIPPED | 9 | @echo SKIPPED |
| 11 | .endif | 10 | .else |
| 11 | |||
| 12 | # REGRESS_TARGETS += regress-wycheproof-primes | ||
| 13 | |||
| 14 | . if exists(/usr/local/bin/go) | ||
| 12 | 15 | ||
| 13 | CLEANFILES+=wycheproof | 16 | REGRESS_TARGETS += regress-wycheproof |
| 14 | REGRESS_TARGETS=regress-wycheproof | 17 | |
| 18 | CLEANFILES += wycheproof | ||
| 15 | 19 | ||
| 16 | audit: wycheproof | 20 | audit: wycheproof |
| 17 | ./wycheproof -v | 21 | ./wycheproof -v |
| @@ -24,4 +28,25 @@ regress-wycheproof: wycheproof | |||
| 24 | 28 | ||
| 25 | .PHONY: audit | 29 | .PHONY: audit |
| 26 | 30 | ||
| 31 | . endif | ||
| 32 | |||
| 33 | PROGS += wycheproof-primes | ||
| 34 | |||
| 35 | LDADD = -lcrypto | ||
| 36 | DPADD = ${LIBCRYPTO} | ||
| 37 | CFLAGS = -I${.CURDIR} -I${.OBJDIR} | ||
| 38 | |||
| 39 | primality_testcases.h: wycheproof-json.pl ${WYCHEPROOF_TESTVECTORS}/primality_test.json | ||
| 40 | perl ${.CURDIR}/wycheproof-json.pl > $@.tmp | ||
| 41 | mv -f $@.tmp $@ | ||
| 42 | |||
| 43 | wycheproof-primes: wycheproof-primes.c primality_testcases.h | ||
| 44 | |||
| 45 | regress-wycheproof-primes: primality_testcases.h wycheproof-primes | ||
| 46 | ./wycheproof-primes | ||
| 47 | |||
| 48 | CLEANFILES += primality_testcases.h | ||
| 49 | |||
| 50 | .endif | ||
| 51 | |||
| 27 | .include <bsd.regress.mk> | 52 | .include <bsd.regress.mk> |
diff --git a/src/regress/lib/libcrypto/wycheproof/wycheproof-json.pl b/src/regress/lib/libcrypto/wycheproof/wycheproof-json.pl new file mode 100644 index 0000000000..01fa66f7f7 --- /dev/null +++ b/src/regress/lib/libcrypto/wycheproof/wycheproof-json.pl | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | # $OpenBSD: wycheproof-json.pl,v 1.1 2022/07/07 20:01:20 tb Exp $ | ||
| 2 | |||
| 3 | # Copyright (c) 2022 Joel Sing <jsing@openbsd.org> | ||
| 4 | # Copyright (c) 2022 Theo Buehler <tb@openbsd.org> | ||
| 5 | # | ||
| 6 | # Permission to use, copy, modify, and distribute this software for any | ||
| 7 | # purpose with or without fee is hereby granted, provided that the above | ||
| 8 | # copyright notice and this permission notice appear in all copies. | ||
| 9 | # | ||
| 10 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 14 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 15 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 16 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 17 | |||
| 18 | use JSON::PP; | ||
| 19 | |||
| 20 | $test_vector_path = "/usr/local/share/wycheproof/testvectors"; | ||
| 21 | |||
| 22 | open JSON, "$test_vector_path/primality_test.json" or die; | ||
| 23 | @json = <JSON>; | ||
| 24 | close JSON; | ||
| 25 | |||
| 26 | $tv = JSON::PP::decode_json(join "\n", @json); | ||
| 27 | $test_groups = %$tv{"testGroups"}; | ||
| 28 | |||
| 29 | my $wycheproof_struct = <<"EOL"; | ||
| 30 | struct wycheproof_testcase { | ||
| 31 | int id; | ||
| 32 | const char *value; | ||
| 33 | int acceptable; | ||
| 34 | int result; | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct wycheproof_testcase testcases[] = { | ||
| 38 | EOL | ||
| 39 | |||
| 40 | print $wycheproof_struct; | ||
| 41 | |||
| 42 | foreach $test_group (@$test_groups) { | ||
| 43 | $test_group_type = %$test_group{"type"}; | ||
| 44 | $test_group_tests = %$test_group{"tests"}; | ||
| 45 | |||
| 46 | foreach $test_case (@$test_group_tests) { | ||
| 47 | %tc = %$test_case; | ||
| 48 | |||
| 49 | $tc_id = $tc{"tcId"}; | ||
| 50 | $tc_value = $tc{"value"}; | ||
| 51 | $tc_result = $tc{"result"}; | ||
| 52 | $tc_flags = @{$tc{"flags"}}; | ||
| 53 | |||
| 54 | my $result = $tc_result eq "valid" ? 1 : 0; | ||
| 55 | |||
| 56 | print "\t{\n"; | ||
| 57 | print "\t\t.id = $tc_id,\n"; | ||
| 58 | print "\t\t.value = \"$tc_value\",\n"; | ||
| 59 | print "\t\t.result = $result,\n"; | ||
| 60 | |||
| 61 | if ($tc_result eq "acceptable") { | ||
| 62 | print "\t\t.acceptable = 1,\n"; | ||
| 63 | } | ||
| 64 | |||
| 65 | print "\t},\n"; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | print "};\n\n"; | ||
| 70 | |||
| 71 | print "#define N_TESTS (sizeof(testcases) / sizeof(testcases[0]))\n" | ||
diff --git a/src/regress/lib/libcrypto/wycheproof/wycheproof-primes.c b/src/regress/lib/libcrypto/wycheproof/wycheproof-primes.c new file mode 100644 index 0000000000..669531d135 --- /dev/null +++ b/src/regress/lib/libcrypto/wycheproof/wycheproof-primes.c | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | /* $OpenBSD: wycheproof-primes.c,v 1.1 2022/07/07 20:01:20 tb Exp $ */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> | ||
| 4 | * | ||
| 5 | * Permission to use, copy, modify, and distribute this software for any | ||
| 6 | * purpose with or without fee is hereby granted, provided that the above | ||
| 7 | * copyright notice and this permission notice appear in all copies. | ||
| 8 | * | ||
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <err.h> | ||
| 19 | #include <stdio.h> | ||
| 20 | |||
| 21 | #include <openssl/bn.h> | ||
| 22 | |||
| 23 | #include "primality_testcases.h" | ||
| 24 | |||
| 25 | int | ||
| 26 | primality_test(struct wycheproof_testcase *test) | ||
| 27 | { | ||
| 28 | BIGNUM *value = NULL; | ||
| 29 | int ret; | ||
| 30 | int failed = 1; | ||
| 31 | |||
| 32 | if (!BN_hex2bn(&value, test->value)) | ||
| 33 | errx(1, "%d: failed to set value \"%s\"", test->id, test->value); | ||
| 34 | |||
| 35 | if ((ret = BN_is_prime_ex(value, BN_prime_checks, NULL, NULL)) < 0) | ||
| 36 | errx(1, "%d: BN_is_prime_ex errored", test->id); | ||
| 37 | |||
| 38 | if (ret != test->result && !test->acceptable) { | ||
| 39 | fprintf(stderr, "%d failed, want %d, got %d\n", test->id, | ||
| 40 | test->result, ret); | ||
| 41 | goto err; | ||
| 42 | } | ||
| 43 | |||
| 44 | failed = 0; | ||
| 45 | err: | ||
| 46 | BN_free(value); | ||
| 47 | |||
| 48 | return failed; | ||
| 49 | } | ||
| 50 | |||
| 51 | int | ||
| 52 | main(void) | ||
| 53 | { | ||
| 54 | size_t i; | ||
| 55 | int failed = 0; | ||
| 56 | |||
| 57 | for (i = 0; i < N_TESTS; i++) | ||
| 58 | failed |= primality_test(&testcases[i]); | ||
| 59 | |||
| 60 | printf("%s\n", failed ? "FAILED" : "SUCCESS"); | ||
| 61 | |||
| 62 | return failed; | ||
| 63 | } | ||
diff --git a/src/regress/lib/libcrypto/wycheproof/wycheproof.go b/src/regress/lib/libcrypto/wycheproof/wycheproof.go index bd45a733b4..a638d0fdd9 100644 --- a/src/regress/lib/libcrypto/wycheproof/wycheproof.go +++ b/src/regress/lib/libcrypto/wycheproof/wycheproof.go | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: wycheproof.go,v 1.126 2022/05/05 18:34:27 tb Exp $ */ | 1 | /* $OpenBSD: wycheproof.go,v 1.127 2022/07/07 20:01:20 tb Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2018 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2018 Joel Sing <jsing@openbsd.org> |
| 4 | * Copyright (c) 2018,2019,2022 Theo Buehler <tb@openbsd.org> | 4 | * Copyright (c) 2018,2019,2022 Theo Buehler <tb@openbsd.org> |
| @@ -349,6 +349,19 @@ type wycheproofTestGroupKW struct { | |||
| 349 | Tests []*wycheproofTestKW `json:"tests"` | 349 | Tests []*wycheproofTestKW `json:"tests"` |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | type wycheproofTestPrimality struct { | ||
| 353 | TCID int `json:"tcId"` | ||
| 354 | Comment string `json:"comment"` | ||
| 355 | Value string `json:"value"` | ||
| 356 | Result string `json:"result"` | ||
| 357 | Flags []string `json:"flags"` | ||
| 358 | } | ||
| 359 | |||
| 360 | type wycheproofTestGroupPrimality struct { | ||
| 361 | Type string `json:"type"` | ||
| 362 | Tests []*wycheproofTestPrimality `json:"tests"` | ||
| 363 | } | ||
| 364 | |||
| 352 | type wycheproofTestRSA struct { | 365 | type wycheproofTestRSA struct { |
| 353 | TCID int `json:"tcId"` | 366 | TCID int `json:"tcId"` |
| 354 | Comment string `json:"comment"` | 367 | Comment string `json:"comment"` |
| @@ -2223,6 +2236,35 @@ func runKWTestGroup(algorithm string, wtg *wycheproofTestGroupKW) bool { | |||
| 2223 | return success | 2236 | return success |
| 2224 | } | 2237 | } |
| 2225 | 2238 | ||
| 2239 | func runPrimalityTest(wt *wycheproofTestPrimality) bool { | ||
| 2240 | var bnValue *C.BIGNUM | ||
| 2241 | value := C.CString(wt.Value) | ||
| 2242 | if C.BN_hex2bn(&bnValue, value) == 0 { | ||
| 2243 | log.Fatal("Failed to set bnValue") | ||
| 2244 | } | ||
| 2245 | C.free(unsafe.Pointer(value)) | ||
| 2246 | defer C.BN_free(bnValue) | ||
| 2247 | |||
| 2248 | ret := C.BN_is_prime_ex(bnValue, C.BN_prime_checks, (*C.BN_CTX)(unsafe.Pointer(nil)), (*C.BN_GENCB)(unsafe.Pointer(nil))) | ||
| 2249 | success := wt.Result == "acceptable" || (ret == 0 && wt.Result == "invalid") || (ret == 1 && wt.Result == "valid") | ||
| 2250 | if !success { | ||
| 2251 | fmt.Printf("FAIL: Test case %d (%q) %v failed - got %d, want %v\n", wt.TCID, wt.Comment, wt.Flags, ret, wt.Result) | ||
| 2252 | } | ||
| 2253 | return success | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | func runPrimalityTestGroup(algorithm string, wtg *wycheproofTestGroupPrimality) bool { | ||
| 2257 | fmt.Printf("Running %v test group...\n", algorithm) | ||
| 2258 | |||
| 2259 | success := true | ||
| 2260 | for _, wt := range wtg.Tests { | ||
| 2261 | if !runPrimalityTest(wt) { | ||
| 2262 | success = false | ||
| 2263 | } | ||
| 2264 | } | ||
| 2265 | return success | ||
| 2266 | } | ||
| 2267 | |||
| 2226 | func runRsaesOaepTest(rsa *C.RSA, sha *C.EVP_MD, mgfSha *C.EVP_MD, wt *wycheproofTestRsaes) bool { | 2268 | func runRsaesOaepTest(rsa *C.RSA, sha *C.EVP_MD, mgfSha *C.EVP_MD, wt *wycheproofTestRsaes) bool { |
| 2227 | ct, err := hex.DecodeString(wt.CT) | 2269 | ct, err := hex.DecodeString(wt.CT) |
| 2228 | if err != nil { | 2270 | if err != nil { |
| @@ -2733,6 +2775,8 @@ func runTestVectors(path string, variant testVariant) bool { | |||
| 2733 | wtg = &wycheproofTestGroupHmac{} | 2775 | wtg = &wycheproofTestGroupHmac{} |
| 2734 | case "KW": | 2776 | case "KW": |
| 2735 | wtg = &wycheproofTestGroupKW{} | 2777 | wtg = &wycheproofTestGroupKW{} |
| 2778 | case "PrimalityTest": | ||
| 2779 | wtg = &wycheproofTestGroupPrimality{} | ||
| 2736 | case "RSAES-OAEP": | 2780 | case "RSAES-OAEP": |
| 2737 | wtg = &wycheproofTestGroupRsaesOaep{} | 2781 | wtg = &wycheproofTestGroupRsaesOaep{} |
| 2738 | case "RSAES-PKCS1-v1_5": | 2782 | case "RSAES-PKCS1-v1_5": |
| @@ -2812,6 +2856,10 @@ func runTestVectors(path string, variant testVariant) bool { | |||
| 2812 | if !runKWTestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupKW)) { | 2856 | if !runKWTestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupKW)) { |
| 2813 | success = false | 2857 | success = false |
| 2814 | } | 2858 | } |
| 2859 | case "PrimalityTest": | ||
| 2860 | if !runPrimalityTestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupPrimality)) { | ||
| 2861 | success = false | ||
| 2862 | } | ||
| 2815 | case "RSAES-OAEP": | 2863 | case "RSAES-OAEP": |
| 2816 | if !runRsaesOaepTestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupRsaesOaep)) { | 2864 | if !runRsaesOaepTestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupRsaesOaep)) { |
| 2817 | success = false | 2865 | success = false |
| @@ -2875,6 +2923,7 @@ func main() { | |||
| 2875 | {"HKDF", "hkdf_sha*_test.json", Normal}, | 2923 | {"HKDF", "hkdf_sha*_test.json", Normal}, |
| 2876 | {"HMAC", "hmac_sha*_test.json", Normal}, | 2924 | {"HMAC", "hmac_sha*_test.json", Normal}, |
| 2877 | {"KW", "kw_test.json", Normal}, | 2925 | {"KW", "kw_test.json", Normal}, |
| 2926 | {"Primality test", "primality_test.json", Skip}, // XXX | ||
| 2878 | {"RSA", "rsa_*test.json", Normal}, | 2927 | {"RSA", "rsa_*test.json", Normal}, |
| 2879 | {"X25519", "x25519_test.json", Normal}, | 2928 | {"X25519", "x25519_test.json", Normal}, |
| 2880 | {"X25519 ASN", "x25519_asn_test.json", Skip}, | 2929 | {"X25519 ASN", "x25519_asn_test.json", Skip}, |
