summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjsing <>2018-07-25 18:04:09 +0000
committerjsing <>2018-07-25 18:04:09 +0000
commitaf3e529bbd783d0a1d715079e42ad70fef429f98 (patch)
tree385d65b7864847a3cf0d341541a5495cc1a483e0
parente4bd885463ebff1f974f8541b3fa34e19ac2c163 (diff)
downloadopenbsd-af3e529bbd783d0a1d715079e42ad70fef429f98.tar.gz
openbsd-af3e529bbd783d0a1d715079e42ad70fef429f98.tar.bz2
openbsd-af3e529bbd783d0a1d715079e42ad70fef429f98.zip
Provide a harness that runs test vectors from Project Wycheproof against
libcrypto. Initially this just covers RSA signatures, but can be extended to cover other cryptographic algorithms. This regress requires the go and wycheproof-testvector packages to be installed, with the regress being skipped otherwise. Discussed with beck@ and tb@
-rw-r--r--src/regress/lib/libcrypto/Makefile3
-rw-r--r--src/regress/lib/libcrypto/wycheproof/Makefile18
-rw-r--r--src/regress/lib/libcrypto/wycheproof/wycheproof.go233
3 files changed, 253 insertions, 1 deletions
diff --git a/src/regress/lib/libcrypto/Makefile b/src/regress/lib/libcrypto/Makefile
index b357b3be88..38e3304eb4 100644
--- a/src/regress/lib/libcrypto/Makefile
+++ b/src/regress/lib/libcrypto/Makefile
@@ -1,4 +1,4 @@
1# $OpenBSD: Makefile,v 1.31 2018/05/15 15:11:15 tb Exp $ 1# $OpenBSD: Makefile,v 1.32 2018/07/25 18:04:09 jsing Exp $
2 2
3SUBDIR= \ 3SUBDIR= \
4 aead \ 4 aead \
@@ -44,6 +44,7 @@ SUBDIR= \
44 sha256 \ 44 sha256 \
45 sha512 \ 45 sha512 \
46 utf8 \ 46 utf8 \
47 wycheproof \
47 x509 48 x509
48 49
49install: 50install:
diff --git a/src/regress/lib/libcrypto/wycheproof/Makefile b/src/regress/lib/libcrypto/wycheproof/Makefile
new file mode 100644
index 0000000000..e7338cb1f0
--- /dev/null
+++ b/src/regress/lib/libcrypto/wycheproof/Makefile
@@ -0,0 +1,18 @@
1# $OpenBSD: Makefile,v 1.1 2018/07/25 18:04:09 jsing Exp $
2
3GO_VERSION != sh -c "(go version) 2>/dev/null || true"
4
5.if empty(GO_VERSION)
6regress:
7 @echo package go is required for this regress
8 @echo SKIPPED
9.endif
10
11CLEANFILES+=wycheproof
12REGRESS_TARGETS=regress-wycheproof
13
14regress-wycheproof:
15 go build -o wycheproof ${.CURDIR}/wycheproof.go
16 ./wycheproof
17
18.include <bsd.regress.mk>
diff --git a/src/regress/lib/libcrypto/wycheproof/wycheproof.go b/src/regress/lib/libcrypto/wycheproof/wycheproof.go
new file mode 100644
index 0000000000..ee53195f02
--- /dev/null
+++ b/src/regress/lib/libcrypto/wycheproof/wycheproof.go
@@ -0,0 +1,233 @@
1/* $OpenBSD: wycheproof.go,v 1.1 2018/07/25 18:04:09 jsing Exp $ */
2/*
3 * Copyright (c) 2018 Joel Sing <jsing@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// Wycheproof runs test vectors from Project Wycheproof against libcrypto.
19package main
20
21/*
22#cgo LDFLAGS: -lcrypto
23
24#include <openssl/bn.h>
25#include <openssl/objects.h>
26#include <openssl/rsa.h>
27*/
28import "C"
29
30import (
31 "crypto/sha1"
32 "crypto/sha256"
33 "crypto/sha512"
34 "encoding/hex"
35 "encoding/json"
36 "fmt"
37 "hash"
38 "io/ioutil"
39 "log"
40 "os"
41 "path/filepath"
42 "unsafe"
43)
44
45const testVectorPath = "/usr/local/share/wycheproof/testvectors"
46
47type wycheproofTest struct {
48 TCID int `json:"tcId"`
49 Comment string `json:"comment"`
50 Msg string `json:"msg"`
51 Sig string `json:"sig"`
52 Padding string `json:"padding"`
53 Result string `json:"result"`
54 Flags []string `json:"flags"`
55}
56
57type wycheproofTestGroup struct {
58 E string `json:"e"`
59 KeyASN string `json:"keyAsn"`
60 KeyDER string `json:"keyDer"`
61 KeyPEM string `json:"keyPem"`
62 KeySize int `json:"keysize"`
63 N string `json:"n"`
64 SHA string `json:"sha"`
65 Type string `json:"type"`
66 Tests []*wycheproofTest `json:"tests"`
67}
68
69type wycheproofTestVectors struct {
70 Algorithm string `json:"algorithm"`
71 GeneratorVersion string `json:"generatorVersion"`
72 Notes map[string]string `json:"notes"`
73 NumberOfTests int `json:"numberOfTests"`
74 // Header
75 TestGroups []*wycheproofTestGroup `json:"testGroups"`
76}
77
78func nidFromString(ns string) (int, error) {
79 switch ns {
80 case "SHA-1":
81 return C.NID_sha1, nil
82 case "SHA-224":
83 return C.NID_sha224, nil
84 case "SHA-256":
85 return C.NID_sha256, nil
86 case "SHA-384":
87 return C.NID_sha384, nil
88 case "SHA-512":
89 return C.NID_sha512, nil
90 default:
91 return -1, fmt.Errorf("unknown NID %q", ns)
92 }
93}
94
95func hashFromString(hs string) (hash.Hash, error) {
96 switch hs {
97 case "SHA-1":
98 return sha1.New(), nil
99 case "SHA-224":
100 return sha256.New224(), nil
101 case "SHA-256":
102 return sha256.New(), nil
103 case "SHA-384":
104 return sha512.New384(), nil
105 case "SHA-512":
106 return sha512.New(), nil
107 default:
108 return nil, fmt.Errorf("unknown hash %q", hs)
109 }
110}
111
112func runRSATest(rsa *C.RSA, nid int, h hash.Hash, wt *wycheproofTest) bool {
113 msg, err := hex.DecodeString(wt.Msg)
114 if err != nil {
115 log.Fatalf("Failed to decode message %q: %v", wt.Msg, err)
116 }
117
118 h.Reset()
119 h.Write(msg)
120 msg = h.Sum(nil)
121
122 sig, err := hex.DecodeString(wt.Sig)
123 if err != nil {
124 log.Fatalf("Failed to decode signature %q: %v", wt.Sig, err)
125 }
126
127 msgLen, sigLen := len(msg), len(sig)
128 if msgLen == 0 {
129 msg = append(msg, 0)
130 }
131 if sigLen == 0 {
132 sig = append(sig, 0)
133 }
134
135 ret := C.RSA_verify(C.int(nid), (*C.uchar)(unsafe.Pointer(&msg[0])), C.uint(msgLen),
136 (*C.uchar)(unsafe.Pointer(&sig[0])), C.uint(sigLen), rsa)
137
138 // XXX audit acceptable cases...
139 succeeded := true
140 if (ret == 1) != (wt.Result == "valid") && wt.Result != "acceptable" {
141 fmt.Printf("FAIL: Test case %d - RSA_verify() = %d, want %v\n", wt.TCID, int(ret), wt.Result)
142 succeeded = false
143 }
144 return succeeded
145}
146
147func runRSATestGroup(wtg *wycheproofTestGroup) bool {
148 fmt.Printf("Running RSA test group %v with key size %d and %v...\n", wtg.Type, wtg.KeySize, wtg.SHA)
149
150 rsa := C.RSA_new()
151 if rsa == nil {
152 log.Fatal("RSA_new failed")
153 }
154 defer C.RSA_free(rsa)
155
156 e := C.CString(wtg.E)
157 if C.BN_hex2bn(&rsa.e, e) == 0 {
158 log.Fatalf("Failed to set RSA e")
159 }
160 C.free(unsafe.Pointer(e))
161
162 n := C.CString(wtg.N)
163 if C.BN_hex2bn(&rsa.n, n) == 0 {
164 log.Fatalf("Failed to set RSA n")
165 }
166 C.free(unsafe.Pointer(n))
167
168 nid, err := nidFromString(wtg.SHA)
169 if err != nil {
170 log.Fatalf("Failed to get MD NID: %v", err)
171 }
172 h, err := hashFromString(wtg.SHA)
173 if err != nil {
174 log.Fatalf("Failed to get hash: %v", err)
175 }
176
177 succeeded := true
178 for _, wt := range wtg.Tests {
179 if !runRSATest(rsa, nid, h, wt) {
180 succeeded = false
181 }
182 }
183 return succeeded
184}
185
186func runRSATestVectors(path string) bool {
187 b, err := ioutil.ReadFile(path)
188 if err != nil {
189 log.Fatalf("Failed to read test vectors: %v", err)
190 }
191 wtv := &wycheproofTestVectors{}
192 if err := json.Unmarshal(b, wtv); err != nil {
193 log.Fatalf("Failed to unmarshal JSON: %v", err)
194 }
195 fmt.Printf("Loaded Wycheproof test vectors for %v with %d tests\n", wtv.Algorithm, wtv.NumberOfTests)
196
197 succeeded := true
198 for _, wtg := range wtv.TestGroups {
199 if !runRSATestGroup(wtg) {
200 succeeded = false
201 }
202 }
203 return succeeded
204}
205
206func main() {
207 if _, err := os.Stat(testVectorPath); os.IsNotExist(err) {
208 fmt.Printf("package wycheproof-testvectors is required for this regress\n")
209 fmt.Printf("SKIPPING\n")
210 os.Exit(0)
211 }
212
213 tvs, err := filepath.Glob(filepath.Join(testVectorPath, "*.json"))
214 if err != nil || len(tvs) == 0 {
215 log.Fatalf("Failed to find test vectors at %q\n", testVectorPath)
216 }
217
218 succeeded := true
219
220 tvs, err = filepath.Glob(filepath.Join(testVectorPath, "rsa_signature_*test.json"))
221 if err != nil {
222 log.Fatalf("Failed to find RSA test vectors: %v", err)
223 }
224 for _, tv := range tvs {
225 if !runRSATestVectors(tv) {
226 succeeded = false
227 }
228 }
229
230 if !succeeded {
231 os.Exit(1)
232 }
233}