diff options
author | tb <> | 2018-10-05 21:12:43 +0000 |
---|---|---|
committer | tb <> | 2018-10-05 21:12:43 +0000 |
commit | 9b0ed526414df2e8b8ce6bc6293db69f42780270 (patch) | |
tree | c5aeb0b7f0d83b3593ce7913e9afcfe5766f9f4d | |
parent | f8454e233d03abfbf8cdc6a3468948f72b0acb67 (diff) | |
download | openbsd-9b0ed526414df2e8b8ce6bc6293db69f42780270.tar.gz openbsd-9b0ed526414df2e8b8ce6bc6293db69f42780270.tar.bz2 openbsd-9b0ed526414df2e8b8ce6bc6293db69f42780270.zip |
Run Wycheproof ECDSA Web Crypto test vectors against libcrypto.
-rw-r--r-- | src/regress/lib/libcrypto/wycheproof/wycheproof.go | 173 |
1 files changed, 166 insertions, 7 deletions
diff --git a/src/regress/lib/libcrypto/wycheproof/wycheproof.go b/src/regress/lib/libcrypto/wycheproof/wycheproof.go index 7f693d04b0..ee99050b3e 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.69 2018/10/04 18:37:07 tb Exp $ */ | 1 | /* $OpenBSD: wycheproof.go,v 1.70 2018/10/05 21:12:43 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 Theo Buehler <tb@openbsd.org> | 4 | * Copyright (c) 2018 Theo Buehler <tb@openbsd.org> |
@@ -45,6 +45,7 @@ import ( | |||
45 | "crypto/sha256" | 45 | "crypto/sha256" |
46 | "crypto/sha512" | 46 | "crypto/sha512" |
47 | "encoding/hex" | 47 | "encoding/hex" |
48 | "encoding/base64" | ||
48 | "encoding/json" | 49 | "encoding/json" |
49 | "flag" | 50 | "flag" |
50 | "fmt" | 51 | "fmt" |
@@ -63,6 +64,14 @@ var acceptableAudit = false | |||
63 | var acceptableComments map[string]int | 64 | var acceptableComments map[string]int |
64 | var acceptableFlags map[string]int | 65 | var acceptableFlags map[string]int |
65 | 66 | ||
67 | type wycheproofJWKPublic struct { | ||
68 | Crv string `json:"crv"` | ||
69 | KID string `json:"kid"` | ||
70 | KTY string `json:"kty"` | ||
71 | X string `json:"x"` | ||
72 | Y string `json:"y"` | ||
73 | } | ||
74 | |||
66 | type wycheproofTestGroupAesCbcPkcs5 struct { | 75 | type wycheproofTestGroupAesCbcPkcs5 struct { |
67 | IVSize int `json:"ivSize"` | 76 | IVSize int `json:"ivSize"` |
68 | KeySize int `json:"keySize"` | 77 | KeySize int `json:"keySize"` |
@@ -190,6 +199,16 @@ type wycheproofTestGroupECDSA struct { | |||
190 | Tests []*wycheproofTestECDSA `json:"tests"` | 199 | Tests []*wycheproofTestECDSA `json:"tests"` |
191 | } | 200 | } |
192 | 201 | ||
202 | type wycheproofTestGroupECDSAWebCrypto struct { | ||
203 | JWK *wycheproofJWKPublic `json:"jwk"` | ||
204 | Key *wycheproofECDSAKey `json:"key"` | ||
205 | KeyDER string `json:"keyDer"` | ||
206 | KeyPEM string `json:"keyPem"` | ||
207 | SHA string `json:"sha"` | ||
208 | Type string `json:"type"` | ||
209 | Tests []*wycheproofTestECDSA `json:"tests"` | ||
210 | } | ||
211 | |||
193 | type wycheproofTestRSA struct { | 212 | type wycheproofTestRSA struct { |
194 | TCID int `json:"tcId"` | 213 | TCID int `json:"tcId"` |
195 | Comment string `json:"comment"` | 214 | Comment string `json:"comment"` |
@@ -274,9 +293,13 @@ var nids = map[string]int{ | |||
274 | "brainpoolP512t1": C.NID_brainpoolP512t1, | 293 | "brainpoolP512t1": C.NID_brainpoolP512t1, |
275 | "secp224r1": C.NID_secp224r1, | 294 | "secp224r1": C.NID_secp224r1, |
276 | "secp256k1": C.NID_secp256k1, | 295 | "secp256k1": C.NID_secp256k1, |
296 | "P-256K": C.NID_secp256k1, | ||
277 | "secp256r1": C.NID_X9_62_prime256v1, // RFC 8422, Table 4, p.32 | 297 | "secp256r1": C.NID_X9_62_prime256v1, // RFC 8422, Table 4, p.32 |
298 | "P-256": C.NID_X9_62_prime256v1, | ||
278 | "secp384r1": C.NID_secp384r1, | 299 | "secp384r1": C.NID_secp384r1, |
300 | "P-384": C.NID_secp384r1, | ||
279 | "secp521r1": C.NID_secp521r1, | 301 | "secp521r1": C.NID_secp521r1, |
302 | "P-521": C.NID_secp521r1, | ||
280 | "SHA-1": C.NID_sha1, | 303 | "SHA-1": C.NID_sha1, |
281 | "SHA-224": C.NID_sha224, | 304 | "SHA-224": C.NID_sha224, |
282 | "SHA-256": C.NID_sha256, | 305 | "SHA-256": C.NID_sha256, |
@@ -1334,6 +1357,129 @@ func runECDSATestGroup(algorithm string, wtg *wycheproofTestGroupECDSA) bool { | |||
1334 | return success | 1357 | return success |
1335 | } | 1358 | } |
1336 | 1359 | ||
1360 | func runECDSAWebCryptoTest(ecKey *C.EC_KEY, nid int, h hash.Hash, wt *wycheproofTestECDSA) bool { | ||
1361 | msg, err := hex.DecodeString(wt.Msg) | ||
1362 | if err != nil { | ||
1363 | log.Fatalf("Failed to decode message %q: %v", wt.Msg, err) | ||
1364 | } | ||
1365 | |||
1366 | h.Reset() | ||
1367 | h.Write(msg) | ||
1368 | msg = h.Sum(nil) | ||
1369 | |||
1370 | msgLen := len(msg) | ||
1371 | if msgLen == 0 { | ||
1372 | msg = append(msg, 0) | ||
1373 | } | ||
1374 | |||
1375 | // DER encode the signature (so that ECDSA_verify() can decode and encode it again...) | ||
1376 | sigLen := len(wt.Sig) | ||
1377 | r := C.CString(wt.Sig[:sigLen/2]) | ||
1378 | s := C.CString(wt.Sig[sigLen/2:]) | ||
1379 | |||
1380 | cSig := C.ECDSA_SIG_new() | ||
1381 | defer C.ECDSA_SIG_free(cSig) | ||
1382 | |||
1383 | if cSig == nil { | ||
1384 | log.Fatal("ECDSA_SIG_new() failed") | ||
1385 | } | ||
1386 | if C.BN_hex2bn(&cSig.r, r) == 0 { | ||
1387 | log.Fatal("Failed to set ECDSA r") | ||
1388 | } | ||
1389 | if C.BN_hex2bn(&cSig.s, s) == 0 { | ||
1390 | log.Fatal("Failed to set ECDSA s") | ||
1391 | } | ||
1392 | C.free(unsafe.Pointer(r)) | ||
1393 | C.free(unsafe.Pointer(s)) | ||
1394 | |||
1395 | derLen := C.i2d_ECDSA_SIG(cSig, nil) | ||
1396 | if derLen == 0 { | ||
1397 | log.Fatal("i2d_ECDSA_SIG(cSig, nil) failed") | ||
1398 | } | ||
1399 | |||
1400 | cDer := (*C.uchar)(C.malloc(C.ulong(derLen))) | ||
1401 | if cDer == nil { | ||
1402 | log.Fatal("malloc failed") | ||
1403 | } | ||
1404 | p := cDer | ||
1405 | defer C.free(unsafe.Pointer(cDer)) | ||
1406 | ret := C.i2d_ECDSA_SIG(cSig, (**C.uchar)(&p)) | ||
1407 | if ret == 0 || ret != derLen { | ||
1408 | log.Fatalf("i2d_ECDSA_SIG(cSig, nil) failed, got %d, want %d", ret, derLen) | ||
1409 | } | ||
1410 | |||
1411 | ret = C.ECDSA_verify(0, (*C.uchar)(unsafe.Pointer(&msg[0])), C.int(msgLen), | ||
1412 | (*C.uchar)(unsafe.Pointer(cDer)), C.int(derLen), ecKey) | ||
1413 | |||
1414 | // XXX audit acceptable cases... | ||
1415 | success := true | ||
1416 | if (ret == 1) != (wt.Result == "valid") && wt.Result != "acceptable" { | ||
1417 | fmt.Printf("FAIL: Test case %d (%q) %v - ECDSA_verify() = %d, want %v\n", wt.TCID, wt.Comment, wt.Flags, int(ret), wt.Result) | ||
1418 | success = false | ||
1419 | } | ||
1420 | if acceptableAudit && ret == 1 && wt.Result == "acceptable" { | ||
1421 | gatherAcceptableStatistics(wt.TCID, wt.Comment, wt.Flags) | ||
1422 | } | ||
1423 | return success | ||
1424 | } | ||
1425 | |||
1426 | func runECDSAWebCryptoTestGroup(algorithm string, wtg *wycheproofTestGroupECDSAWebCrypto) bool { | ||
1427 | fmt.Printf("Running %v test group %v with curve %v, key size %d and %v...\n", algorithm, wtg.Type, wtg.Key.Curve, wtg.Key.KeySize, wtg.SHA) | ||
1428 | |||
1429 | nid, err := nidFromString(wtg.JWK.Crv) | ||
1430 | if err != nil { | ||
1431 | log.Fatalf("Failed to get nid for curve: %v", err) | ||
1432 | } | ||
1433 | ecKey := C.EC_KEY_new_by_curve_name(C.int(nid)) | ||
1434 | if ecKey == nil { | ||
1435 | log.Fatal("EC_KEY_new_by_curve_name failed") | ||
1436 | } | ||
1437 | defer C.EC_KEY_free(ecKey) | ||
1438 | |||
1439 | x, err := base64.RawURLEncoding.DecodeString(wtg.JWK.X) | ||
1440 | if err != nil { | ||
1441 | log.Fatalf("Failed to base64 decode X: %v", err) | ||
1442 | } | ||
1443 | var bnX *C.BIGNUM | ||
1444 | bnX = C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&x[0])), (C.int)(len(x)), nil) | ||
1445 | if bnX == nil { | ||
1446 | log.Fatal("Failed to decode X") | ||
1447 | } | ||
1448 | defer C.BN_free(bnX) | ||
1449 | |||
1450 | y, err := base64.RawURLEncoding.DecodeString(wtg.JWK.Y) | ||
1451 | if err != nil { | ||
1452 | log.Fatalf("Failed to base64 decode Y: %v", err) | ||
1453 | } | ||
1454 | var bnY *C.BIGNUM | ||
1455 | bnY = C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&y[0])), (C.int)(len(y)), nil) | ||
1456 | if bnY == nil { | ||
1457 | log.Fatal("Failed to decode Y") | ||
1458 | } | ||
1459 | defer C.BN_free(bnY) | ||
1460 | |||
1461 | if C.EC_KEY_set_public_key_affine_coordinates(ecKey, bnX, bnY) != 1 { | ||
1462 | log.Fatal("Failed to set EC public key") | ||
1463 | } | ||
1464 | |||
1465 | nid, err = nidFromString(wtg.SHA) | ||
1466 | if err != nil { | ||
1467 | log.Fatalf("Failed to get MD NID: %v", err) | ||
1468 | } | ||
1469 | h, err := hashFromString(wtg.SHA) | ||
1470 | if err != nil { | ||
1471 | log.Fatalf("Failed to get hash: %v", err) | ||
1472 | } | ||
1473 | |||
1474 | success := true | ||
1475 | for _, wt := range wtg.Tests { | ||
1476 | if !runECDSAWebCryptoTest(ecKey, nid, h, wt) { | ||
1477 | success = false | ||
1478 | } | ||
1479 | } | ||
1480 | return success | ||
1481 | } | ||
1482 | |||
1337 | func runRSASSATest(rsa *C.RSA, h hash.Hash, sha *C.EVP_MD, mgfSha *C.EVP_MD, sLen int, wt *wycheproofTestRSASSA) bool { | 1483 | func runRSASSATest(rsa *C.RSA, h hash.Hash, sha *C.EVP_MD, mgfSha *C.EVP_MD, sLen int, wt *wycheproofTestRSASSA) bool { |
1338 | msg, err := hex.DecodeString(wt.Msg) | 1484 | msg, err := hex.DecodeString(wt.Msg) |
1339 | if err != nil { | 1485 | if err != nil { |
@@ -1557,7 +1703,7 @@ func runX25519TestGroup(algorithm string, wtg *wycheproofTestGroupX25519) bool { | |||
1557 | return success | 1703 | return success |
1558 | } | 1704 | } |
1559 | 1705 | ||
1560 | func runTestVectors(path string) bool { | 1706 | func runTestVectors(path string, webcrypto bool) bool { |
1561 | b, err := ioutil.ReadFile(path) | 1707 | b, err := ioutil.ReadFile(path) |
1562 | if err != nil { | 1708 | if err != nil { |
1563 | log.Fatalf("Failed to read test vectors: %v", err) | 1709 | log.Fatalf("Failed to read test vectors: %v", err) |
@@ -1585,7 +1731,12 @@ func runTestVectors(path string) bool { | |||
1585 | case "ECDH": | 1731 | case "ECDH": |
1586 | wtg = &wycheproofTestGroupECDH{} | 1732 | wtg = &wycheproofTestGroupECDH{} |
1587 | case "ECDSA": | 1733 | case "ECDSA": |
1588 | wtg = &wycheproofTestGroupECDSA{} | 1734 | if webcrypto { |
1735 | wtg = &wycheproofTestGroupECDSAWebCrypto{} | ||
1736 | } else { | ||
1737 | wtg = &wycheproofTestGroupECDSA{} | ||
1738 | } | ||
1739 | |||
1589 | case "RSASSA-PSS": | 1740 | case "RSASSA-PSS": |
1590 | wtg = &wycheproofTestGroupRSASSA{} | 1741 | wtg = &wycheproofTestGroupRSASSA{} |
1591 | case "RSASig": | 1742 | case "RSASig": |
@@ -1631,8 +1782,14 @@ func runTestVectors(path string) bool { | |||
1631 | success = false | 1782 | success = false |
1632 | } | 1783 | } |
1633 | case "ECDSA": | 1784 | case "ECDSA": |
1634 | if !runECDSATestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupECDSA)) { | 1785 | if webcrypto { |
1635 | success = false | 1786 | if !runECDSAWebCryptoTestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupECDSAWebCrypto)) { |
1787 | success = false | ||
1788 | } | ||
1789 | } else { | ||
1790 | if !runECDSATestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupECDSA)) { | ||
1791 | success = false | ||
1792 | } | ||
1636 | } | 1793 | } |
1637 | case "RSASSA-PSS": | 1794 | case "RSASSA-PSS": |
1638 | if !runRSASSATestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupRSASSA)) { | 1795 | if !runRSASSATestGroup(wtv.Algorithm, wtg.(*wycheproofTestGroupRSASSA)) { |
@@ -1674,7 +1831,8 @@ func main() { | |||
1674 | {"ChaCha20-Poly1305", "chacha20_poly1305_test.json"}, | 1831 | {"ChaCha20-Poly1305", "chacha20_poly1305_test.json"}, |
1675 | {"DSA", "dsa_test.json"}, | 1832 | {"DSA", "dsa_test.json"}, |
1676 | {"ECDH", "ecdh_[^w]*test.json"}, // Skip ecdh_webcrypto_test.json for now. | 1833 | {"ECDH", "ecdh_[^w]*test.json"}, // Skip ecdh_webcrypto_test.json for now. |
1677 | {"ECDSA", "ecdsa_[^w]*test.json"}, // Skip ecdsa_webcrypto_test.json for now. | 1834 | {"ECDSA", "ecdsa_[^w]*test.json"}, |
1835 | {"ECDSAWebCrypto", "ecdsa_w*_test.json"}, | ||
1678 | {"RSA", "rsa_*test.json"}, | 1836 | {"RSA", "rsa_*test.json"}, |
1679 | {"X25519", "x25519_*test.json"}, | 1837 | {"X25519", "x25519_*test.json"}, |
1680 | } | 1838 | } |
@@ -1682,6 +1840,7 @@ func main() { | |||
1682 | success := true | 1840 | success := true |
1683 | 1841 | ||
1684 | for _, test := range tests { | 1842 | for _, test := range tests { |
1843 | webcrypto := (test.name == "ECDSAWebCrypto") | ||
1685 | tvs, err := filepath.Glob(filepath.Join(testVectorPath, test.pattern)) | 1844 | tvs, err := filepath.Glob(filepath.Join(testVectorPath, test.pattern)) |
1686 | if err != nil { | 1845 | if err != nil { |
1687 | log.Fatalf("Failed to glob %v test vectors: %v", test.name, err) | 1846 | log.Fatalf("Failed to glob %v test vectors: %v", test.name, err) |
@@ -1690,7 +1849,7 @@ func main() { | |||
1690 | log.Fatalf("Failed to find %v test vectors at %q\n", test.name, testVectorPath) | 1849 | log.Fatalf("Failed to find %v test vectors at %q\n", test.name, testVectorPath) |
1691 | } | 1850 | } |
1692 | for _, tv := range tvs { | 1851 | for _, tv := range tvs { |
1693 | if !runTestVectors(tv) { | 1852 | if !runTestVectors(tv, webcrypto) { |
1694 | success = false | 1853 | success = false |
1695 | } | 1854 | } |
1696 | } | 1855 | } |