summaryrefslogtreecommitdiff
path: root/src/regress
diff options
context:
space:
mode:
authortb <>2023-04-23 13:43:46 +0000
committertb <>2023-04-23 13:43:46 +0000
commite0170f8a4e83f5afc4e0b30f2a206b7ebb402434 (patch)
treef4a2405bb6ac11d15457577b0b927021b45a6e34 /src/regress
parentfa02f32a30ed74321ed3cbae490733b1665e329c (diff)
downloadopenbsd-e0170f8a4e83f5afc4e0b30f2a206b7ebb402434.tar.gz
openbsd-e0170f8a4e83f5afc4e0b30f2a206b7ebb402434.tar.bz2
openbsd-e0170f8a4e83f5afc4e0b30f2a206b7ebb402434.zip
Import C2SP/CCTV test
This currently only covers Ed25519 using the c2sp-testvectors package and checks that our Ed25519 implementation behaves as expected from a "ref10" implementation. This test has Go and c2sp-testvectors as a hard dependency. It will optionally pick up any OpenSSL package installed on the system and test that as well. https://github.com/C2SP/CCTV https://github.com/C2SP/CCTV/tree/main/ed25519
Diffstat (limited to 'src/regress')
-rw-r--r--src/regress/lib/libcrypto/c2sp/Makefile38
-rw-r--r--src/regress/lib/libcrypto/c2sp/cctv.go209
2 files changed, 247 insertions, 0 deletions
diff --git a/src/regress/lib/libcrypto/c2sp/Makefile b/src/regress/lib/libcrypto/c2sp/Makefile
new file mode 100644
index 0000000000..aec5409161
--- /dev/null
+++ b/src/regress/lib/libcrypto/c2sp/Makefile
@@ -0,0 +1,38 @@
1# $OpenBSD: Makefile,v 1.1.1.1 2023/04/23 13:43:46 tb Exp $
2
3C2SP_TESTVECTORS = /usr/local/share/c2sp-testvectors/
4
5.if !exists(${C2SP_TESTVECTORS}) || !exists(/usr/local/bin/go)
6regress:
7 @echo required packages: security/c2sp-testvectors lang/go
8 @echo optional packages: security/openssl/*
9 @echo SKIPPED
10.else
11
12PROGS += cctv
13SRCS_cctv =
14
15cctv: cctv.go
16 go build -o $@ ${.CURDIR}/cctv.go
17
18OSSL_LIB = /usr/local/lib/eopenssl
19OSSL_INC = /usr/local/include/eopenssl
20
21. for V in 11 30 31
22. if exists(/usr/local/bin/eopenssl$V)
23PROGS += cctv-openssl$V
24SRCS_cctv-openssl$V =
25
26CGO_CFLAGS_$V += -I${OSSL_INC}$V
27CGO_LDFLAGS_$V += -Wl,-rpath,${OSSL_LIB}$V
28CGO_LDFLAGS_$V += -L${OSSL_LIB}$V
29
30cctv-openssl$V: cctv.go
31 env CGO_CFLAGS="${CGO_CFLAGS_$V}" CGO_LDFLAGS="${CGO_LDFLAGS_$V}" \
32 go build -o $@ ${.CURDIR}/cctv.go
33. endif
34. endfor
35
36.endif
37
38.include <bsd.regress.mk>
diff --git a/src/regress/lib/libcrypto/c2sp/cctv.go b/src/regress/lib/libcrypto/c2sp/cctv.go
new file mode 100644
index 0000000000..ae6efc2ee9
--- /dev/null
+++ b/src/regress/lib/libcrypto/c2sp/cctv.go
@@ -0,0 +1,209 @@
1/* $OpenBSD: cctv.go,v 1.1.1.1 2023/04/23 13:43:46 tb Exp $ */
2
3/*
4 * Copyright (c) 2023 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
19// cctv runs test vectors from CCTV against libcrypto.
20package main
21
22/*
23#cgo LDFLAGS: -lcrypto
24
25#include <openssl/evp.h>
26*/
27import "C"
28
29import (
30 "crypto/ed25519"
31 "encoding/hex"
32 "encoding/json"
33 "fmt"
34 "io/ioutil"
35 "log"
36 "os"
37 "path/filepath"
38 "unsafe"
39)
40
41const testVectorPath = "/usr/local/share/c2sp-testvectors"
42const ed25519Json = "ed25519/ed25519vectors.json"
43
44type ed25519Vectors []ed25519Vector
45
46type ed25519Vector struct {
47 Number int `json:"number"`
48 PublicKey string `json:"key"`
49 Signature string `json:"sig"`
50 Message string `json:"msg"`
51 Flags Flags `json:"flags"`
52}
53
54type Flags int
55
56const (
57 LowOrderR Flags = 1 << iota
58 LowOrderA
59 LowOrderComponentR
60 LowOrderComponentA
61 LowOrderResidue
62 NonCanonicalA
63 NonCanonicalR
64 ReencodedK
65)
66
67func (f Flags) String() string {
68 var flags []string
69 if f&LowOrderR != 0 {
70 flags = append(flags, "low_order_R")
71 }
72 if f&LowOrderA != 0 {
73 flags = append(flags, "low_order_A")
74 }
75 if f&LowOrderComponentR != 0 {
76 flags = append(flags, "low_order_component_R")
77 }
78 if f&LowOrderComponentA != 0 {
79 flags = append(flags, "low_order_component_A")
80 }
81 if f&LowOrderResidue != 0 {
82 flags = append(flags, "low_order_residue")
83 }
84 if f&NonCanonicalA != 0 {
85 flags = append(flags, "non_canonical_A")
86 }
87 if f&NonCanonicalR != 0 {
88 flags = append(flags, "non_canonical_R")
89 }
90 if f&ReencodedK != 0 {
91 flags = append(flags, "reencoded_k")
92 }
93 return fmt.Sprintf("%v", flags)
94}
95
96func (f *Flags) UnmarshalJSON(b []byte) error {
97 var v []string
98
99 if err := json.Unmarshal(b, &v); err != nil {
100 return err
101 }
102 for _, flag := range v {
103 switch flag {
104 case "low_order_A":
105 *f |= LowOrderA
106 case "low_order_R":
107 *f |= LowOrderR
108 case "low_order_component_A":
109 *f |= LowOrderComponentA
110 case "low_order_component_R":
111 *f |= LowOrderComponentR
112 case "low_order_residue":
113 *f |= LowOrderResidue
114 case "non_canonical_A":
115 *f |= NonCanonicalA
116 case "non_canonical_R":
117 *f |= NonCanonicalR
118 case "reencoded_k":
119 *f |= ReencodedK
120 default:
121 log.Fatalf("unknown flag %q", flag)
122 }
123 }
124
125 return nil
126}
127
128func evpEd25519Verify(pubkey, msg, sig []byte) bool {
129 pkey := C.EVP_PKEY_new_raw_public_key(C.EVP_PKEY_ED25519, nil, (*C.uchar)(unsafe.Pointer(&pubkey[0])), (C.size_t)(len(pubkey)))
130 if pkey == nil {
131 log.Fatalf("EVP_PKEY_new_raw_public_key failed")
132 }
133 defer C.EVP_PKEY_free(pkey)
134
135 mdctx := C.EVP_MD_CTX_new()
136 if mdctx == nil {
137 log.Fatal("EVP_MD_CTX_new failed")
138 }
139 defer C.EVP_MD_CTX_free(mdctx)
140
141 if C.EVP_DigestVerifyInit(mdctx, nil, nil, nil, pkey) != 1 {
142 log.Fatal("EVP_DigestVerifyInit failed")
143 }
144 ret := C.EVP_DigestVerify(mdctx, (*C.uchar)(unsafe.Pointer(&sig[0])), (C.size_t)(len(sig)), (*C.uchar)(unsafe.Pointer(&msg[0])), (C.size_t)(len(msg)))
145 if ret < 0 {
146 log.Fatalf("EVP_DigestVerify errored %d", ret)
147 }
148
149 return ret == 1
150}
151
152func runEd25519Test(tv ed25519Vector) bool {
153 pubkey, err := hex.DecodeString(tv.PublicKey)
154 if err != nil {
155 log.Fatalf("Failed to decode key %q: %v", tv.PublicKey, err)
156 }
157
158 sig, err := hex.DecodeString(tv.Signature)
159 if err != nil {
160 log.Fatalf("Failed to decode Signature %q: %v", tv.Signature, err)
161 }
162
163 msg := []byte(tv.Message)
164
165 // Implementations derived from "ref10" reject `LowOrderResidue` and
166 // `NonCanonicalR` and accept everything else.
167 reject := LowOrderResidue | NonCanonicalR
168 want_verify := (tv.Flags & reject) == 0
169
170 c_verified := evpEd25519Verify(pubkey, msg, sig)
171 go_verified := ed25519.Verify(pubkey, msg, sig)
172
173 success := true
174 if c_verified != want_verify || go_verified != want_verify {
175 fmt.Printf("FAIL: Test case %d (flags: %v) - C: %t, want: %t, go: %t\n", tv.Number, tv.Flags, c_verified, want_verify, go_verified)
176 success = false
177 }
178 return success
179}
180
181func main() {
182 if _, err := os.Stat(testVectorPath); os.IsNotExist(err) {
183 fmt.Printf("package cc-testvectors is required for this regress\n")
184 fmt.Printf("SKIPPED\n")
185 os.Exit(0)
186 }
187
188 b, err := ioutil.ReadFile(filepath.Join(testVectorPath, ed25519Json))
189 if err != nil {
190 log.Fatalf("Failed to read test vectors: %v", err)
191 }
192
193 edv := &ed25519Vectors{}
194 if err := json.Unmarshal(b, edv); err != nil {
195 log.Fatalf("Failed to unmarshal JSON: %v", err)
196 }
197
198 success := true
199
200 for _, vector := range *edv {
201 if !runEd25519Test(vector) {
202 success = false
203 }
204 }
205
206 if !success {
207 os.Exit(1)
208 }
209}