xref: /openbsd-src/regress/lib/libcrypto/c2sp/cctv.go (revision a82bb09bcef2015d7e590f2b892c5dd18443f9c7)
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}
210