xref: /openbsd-src/regress/lib/libcrypto/c2sp/cctv.go (revision a82bb09bcef2015d7e590f2b892c5dd18443f9c7)
1*a82bb09bStb/*	$OpenBSD: cctv.go,v 1.1.1.1 2023/04/23 13:43:46 tb Exp $ */
2*a82bb09bStb
3*a82bb09bStb/*
4*a82bb09bStb * Copyright (c) 2023 Theo Buehler <tb@openbsd.org>
5*a82bb09bStb *
6*a82bb09bStb * Permission to use, copy, modify, and distribute this software for any
7*a82bb09bStb * purpose with or without fee is hereby granted, provided that the above
8*a82bb09bStb * copyright notice and this permission notice appear in all copies.
9*a82bb09bStb *
10*a82bb09bStb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*a82bb09bStb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*a82bb09bStb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*a82bb09bStb * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*a82bb09bStb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*a82bb09bStb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*a82bb09bStb * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*a82bb09bStb */
18*a82bb09bStb
19*a82bb09bStb// cctv runs test vectors from CCTV against libcrypto.
20*a82bb09bStbpackage main
21*a82bb09bStb
22*a82bb09bStb/*
23*a82bb09bStb#cgo LDFLAGS: -lcrypto
24*a82bb09bStb
25*a82bb09bStb#include <openssl/evp.h>
26*a82bb09bStb*/
27*a82bb09bStbimport "C"
28*a82bb09bStb
29*a82bb09bStbimport (
30*a82bb09bStb	"crypto/ed25519"
31*a82bb09bStb	"encoding/hex"
32*a82bb09bStb	"encoding/json"
33*a82bb09bStb	"fmt"
34*a82bb09bStb	"io/ioutil"
35*a82bb09bStb	"log"
36*a82bb09bStb	"os"
37*a82bb09bStb	"path/filepath"
38*a82bb09bStb	"unsafe"
39*a82bb09bStb)
40*a82bb09bStb
41*a82bb09bStbconst testVectorPath = "/usr/local/share/c2sp-testvectors"
42*a82bb09bStbconst ed25519Json = "ed25519/ed25519vectors.json"
43*a82bb09bStb
44*a82bb09bStbtype ed25519Vectors []ed25519Vector
45*a82bb09bStb
46*a82bb09bStbtype ed25519Vector struct {
47*a82bb09bStb	Number    int    `json:"number"`
48*a82bb09bStb	PublicKey string `json:"key"`
49*a82bb09bStb	Signature string `json:"sig"`
50*a82bb09bStb	Message   string `json:"msg"`
51*a82bb09bStb	Flags     Flags  `json:"flags"`
52*a82bb09bStb}
53*a82bb09bStb
54*a82bb09bStbtype Flags int
55*a82bb09bStb
56*a82bb09bStbconst (
57*a82bb09bStb	LowOrderR Flags = 1 << iota
58*a82bb09bStb	LowOrderA
59*a82bb09bStb	LowOrderComponentR
60*a82bb09bStb	LowOrderComponentA
61*a82bb09bStb	LowOrderResidue
62*a82bb09bStb	NonCanonicalA
63*a82bb09bStb	NonCanonicalR
64*a82bb09bStb	ReencodedK
65*a82bb09bStb)
66*a82bb09bStb
67*a82bb09bStbfunc (f Flags) String() string {
68*a82bb09bStb	var flags []string
69*a82bb09bStb	if f&LowOrderR != 0 {
70*a82bb09bStb		flags = append(flags, "low_order_R")
71*a82bb09bStb	}
72*a82bb09bStb	if f&LowOrderA != 0 {
73*a82bb09bStb		flags = append(flags, "low_order_A")
74*a82bb09bStb	}
75*a82bb09bStb	if f&LowOrderComponentR != 0 {
76*a82bb09bStb		flags = append(flags, "low_order_component_R")
77*a82bb09bStb	}
78*a82bb09bStb	if f&LowOrderComponentA != 0 {
79*a82bb09bStb		flags = append(flags, "low_order_component_A")
80*a82bb09bStb	}
81*a82bb09bStb	if f&LowOrderResidue != 0 {
82*a82bb09bStb		flags = append(flags, "low_order_residue")
83*a82bb09bStb	}
84*a82bb09bStb	if f&NonCanonicalA != 0 {
85*a82bb09bStb		flags = append(flags, "non_canonical_A")
86*a82bb09bStb	}
87*a82bb09bStb	if f&NonCanonicalR != 0 {
88*a82bb09bStb		flags = append(flags, "non_canonical_R")
89*a82bb09bStb	}
90*a82bb09bStb	if f&ReencodedK != 0 {
91*a82bb09bStb		flags = append(flags, "reencoded_k")
92*a82bb09bStb	}
93*a82bb09bStb	return fmt.Sprintf("%v", flags)
94*a82bb09bStb}
95*a82bb09bStb
96*a82bb09bStbfunc (f *Flags) UnmarshalJSON(b []byte) error {
97*a82bb09bStb	var v []string
98*a82bb09bStb
99*a82bb09bStb	if err := json.Unmarshal(b, &v); err != nil {
100*a82bb09bStb		return err
101*a82bb09bStb	}
102*a82bb09bStb	for _, flag := range v {
103*a82bb09bStb		switch flag {
104*a82bb09bStb		case "low_order_A":
105*a82bb09bStb			*f |= LowOrderA
106*a82bb09bStb		case "low_order_R":
107*a82bb09bStb			*f |= LowOrderR
108*a82bb09bStb		case "low_order_component_A":
109*a82bb09bStb			*f |= LowOrderComponentA
110*a82bb09bStb		case "low_order_component_R":
111*a82bb09bStb			*f |= LowOrderComponentR
112*a82bb09bStb		case "low_order_residue":
113*a82bb09bStb			*f |= LowOrderResidue
114*a82bb09bStb		case "non_canonical_A":
115*a82bb09bStb			*f |= NonCanonicalA
116*a82bb09bStb		case "non_canonical_R":
117*a82bb09bStb			*f |= NonCanonicalR
118*a82bb09bStb		case "reencoded_k":
119*a82bb09bStb			*f |= ReencodedK
120*a82bb09bStb		default:
121*a82bb09bStb			log.Fatalf("unknown flag %q", flag)
122*a82bb09bStb		}
123*a82bb09bStb	}
124*a82bb09bStb
125*a82bb09bStb	return nil
126*a82bb09bStb}
127*a82bb09bStb
128*a82bb09bStbfunc evpEd25519Verify(pubkey, msg, sig []byte) bool {
129*a82bb09bStb	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*a82bb09bStb	if pkey == nil {
131*a82bb09bStb		log.Fatalf("EVP_PKEY_new_raw_public_key failed")
132*a82bb09bStb	}
133*a82bb09bStb	defer C.EVP_PKEY_free(pkey)
134*a82bb09bStb
135*a82bb09bStb	mdctx := C.EVP_MD_CTX_new()
136*a82bb09bStb	if mdctx == nil {
137*a82bb09bStb		log.Fatal("EVP_MD_CTX_new failed")
138*a82bb09bStb	}
139*a82bb09bStb	defer C.EVP_MD_CTX_free(mdctx)
140*a82bb09bStb
141*a82bb09bStb	if C.EVP_DigestVerifyInit(mdctx, nil, nil, nil, pkey) != 1 {
142*a82bb09bStb		log.Fatal("EVP_DigestVerifyInit failed")
143*a82bb09bStb	}
144*a82bb09bStb	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*a82bb09bStb	if ret < 0 {
146*a82bb09bStb		log.Fatalf("EVP_DigestVerify errored %d", ret)
147*a82bb09bStb	}
148*a82bb09bStb
149*a82bb09bStb	return ret == 1
150*a82bb09bStb}
151*a82bb09bStb
152*a82bb09bStbfunc runEd25519Test(tv ed25519Vector) bool {
153*a82bb09bStb	pubkey, err := hex.DecodeString(tv.PublicKey)
154*a82bb09bStb	if err != nil {
155*a82bb09bStb		log.Fatalf("Failed to decode key %q: %v", tv.PublicKey, err)
156*a82bb09bStb	}
157*a82bb09bStb
158*a82bb09bStb	sig, err := hex.DecodeString(tv.Signature)
159*a82bb09bStb	if err != nil {
160*a82bb09bStb		log.Fatalf("Failed to decode Signature %q: %v", tv.Signature, err)
161*a82bb09bStb	}
162*a82bb09bStb
163*a82bb09bStb	msg := []byte(tv.Message)
164*a82bb09bStb
165*a82bb09bStb	// Implementations derived from "ref10" reject `LowOrderResidue` and
166*a82bb09bStb	// `NonCanonicalR` and accept everything else.
167*a82bb09bStb	reject := LowOrderResidue | NonCanonicalR
168*a82bb09bStb	want_verify := (tv.Flags & reject) == 0
169*a82bb09bStb
170*a82bb09bStb	c_verified := evpEd25519Verify(pubkey, msg, sig)
171*a82bb09bStb	go_verified := ed25519.Verify(pubkey, msg, sig)
172*a82bb09bStb
173*a82bb09bStb	success := true
174*a82bb09bStb	if c_verified != want_verify || go_verified != want_verify {
175*a82bb09bStb		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*a82bb09bStb		success = false
177*a82bb09bStb	}
178*a82bb09bStb	return success
179*a82bb09bStb}
180*a82bb09bStb
181*a82bb09bStbfunc main() {
182*a82bb09bStb	if _, err := os.Stat(testVectorPath); os.IsNotExist(err) {
183*a82bb09bStb		fmt.Printf("package cc-testvectors is required for this regress\n")
184*a82bb09bStb		fmt.Printf("SKIPPED\n")
185*a82bb09bStb		os.Exit(0)
186*a82bb09bStb	}
187*a82bb09bStb
188*a82bb09bStb	b, err := ioutil.ReadFile(filepath.Join(testVectorPath, ed25519Json))
189*a82bb09bStb	if err != nil {
190*a82bb09bStb		log.Fatalf("Failed to read test vectors: %v", err)
191*a82bb09bStb	}
192*a82bb09bStb
193*a82bb09bStb	edv := &ed25519Vectors{}
194*a82bb09bStb	if err := json.Unmarshal(b, edv); err != nil {
195*a82bb09bStb		log.Fatalf("Failed to unmarshal JSON: %v", err)
196*a82bb09bStb	}
197*a82bb09bStb
198*a82bb09bStb	success := true
199*a82bb09bStb
200*a82bb09bStb	for _, vector := range *edv {
201*a82bb09bStb		if !runEd25519Test(vector) {
202*a82bb09bStb			success = false
203*a82bb09bStb		}
204*a82bb09bStb	}
205*a82bb09bStb
206*a82bb09bStb	if !success {
207*a82bb09bStb		os.Exit(1)
208*a82bb09bStb	}
209*a82bb09bStb}
210