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