1
0
mirror of https://github.com/openbsd/src.git synced 2025-01-10 06:47:55 -08:00

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
This commit is contained in:
tb 2023-04-23 13:43:46 +00:00
parent ba54bc0811
commit a82bb09bce
2 changed files with 247 additions and 0 deletions

View File

@ -0,0 +1,38 @@
# $OpenBSD: Makefile,v 1.1.1.1 2023/04/23 13:43:46 tb Exp $
C2SP_TESTVECTORS = /usr/local/share/c2sp-testvectors/
.if !exists(${C2SP_TESTVECTORS}) || !exists(/usr/local/bin/go)
regress:
@echo required packages: security/c2sp-testvectors lang/go
@echo optional packages: security/openssl/*
@echo SKIPPED
.else
PROGS += cctv
SRCS_cctv =
cctv: cctv.go
go build -o $@ ${.CURDIR}/cctv.go
OSSL_LIB = /usr/local/lib/eopenssl
OSSL_INC = /usr/local/include/eopenssl
. for V in 11 30 31
. if exists(/usr/local/bin/eopenssl$V)
PROGS += cctv-openssl$V
SRCS_cctv-openssl$V =
CGO_CFLAGS_$V += -I${OSSL_INC}$V
CGO_LDFLAGS_$V += -Wl,-rpath,${OSSL_LIB}$V
CGO_LDFLAGS_$V += -L${OSSL_LIB}$V
cctv-openssl$V: cctv.go
env CGO_CFLAGS="${CGO_CFLAGS_$V}" CGO_LDFLAGS="${CGO_LDFLAGS_$V}" \
go build -o $@ ${.CURDIR}/cctv.go
. endif
. endfor
.endif
.include <bsd.regress.mk>

View File

@ -0,0 +1,209 @@
/* $OpenBSD: cctv.go,v 1.1.1.1 2023/04/23 13:43:46 tb Exp $ */
/*
* Copyright (c) 2023 Theo Buehler <tb@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// cctv runs test vectors from CCTV against libcrypto.
package main
/*
#cgo LDFLAGS: -lcrypto
#include <openssl/evp.h>
*/
import "C"
import (
"crypto/ed25519"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"unsafe"
)
const testVectorPath = "/usr/local/share/c2sp-testvectors"
const ed25519Json = "ed25519/ed25519vectors.json"
type ed25519Vectors []ed25519Vector
type ed25519Vector struct {
Number int `json:"number"`
PublicKey string `json:"key"`
Signature string `json:"sig"`
Message string `json:"msg"`
Flags Flags `json:"flags"`
}
type Flags int
const (
LowOrderR Flags = 1 << iota
LowOrderA
LowOrderComponentR
LowOrderComponentA
LowOrderResidue
NonCanonicalA
NonCanonicalR
ReencodedK
)
func (f Flags) String() string {
var flags []string
if f&LowOrderR != 0 {
flags = append(flags, "low_order_R")
}
if f&LowOrderA != 0 {
flags = append(flags, "low_order_A")
}
if f&LowOrderComponentR != 0 {
flags = append(flags, "low_order_component_R")
}
if f&LowOrderComponentA != 0 {
flags = append(flags, "low_order_component_A")
}
if f&LowOrderResidue != 0 {
flags = append(flags, "low_order_residue")
}
if f&NonCanonicalA != 0 {
flags = append(flags, "non_canonical_A")
}
if f&NonCanonicalR != 0 {
flags = append(flags, "non_canonical_R")
}
if f&ReencodedK != 0 {
flags = append(flags, "reencoded_k")
}
return fmt.Sprintf("%v", flags)
}
func (f *Flags) UnmarshalJSON(b []byte) error {
var v []string
if err := json.Unmarshal(b, &v); err != nil {
return err
}
for _, flag := range v {
switch flag {
case "low_order_A":
*f |= LowOrderA
case "low_order_R":
*f |= LowOrderR
case "low_order_component_A":
*f |= LowOrderComponentA
case "low_order_component_R":
*f |= LowOrderComponentR
case "low_order_residue":
*f |= LowOrderResidue
case "non_canonical_A":
*f |= NonCanonicalA
case "non_canonical_R":
*f |= NonCanonicalR
case "reencoded_k":
*f |= ReencodedK
default:
log.Fatalf("unknown flag %q", flag)
}
}
return nil
}
func evpEd25519Verify(pubkey, msg, sig []byte) bool {
pkey := C.EVP_PKEY_new_raw_public_key(C.EVP_PKEY_ED25519, nil, (*C.uchar)(unsafe.Pointer(&pubkey[0])), (C.size_t)(len(pubkey)))
if pkey == nil {
log.Fatalf("EVP_PKEY_new_raw_public_key failed")
}
defer C.EVP_PKEY_free(pkey)
mdctx := C.EVP_MD_CTX_new()
if mdctx == nil {
log.Fatal("EVP_MD_CTX_new failed")
}
defer C.EVP_MD_CTX_free(mdctx)
if C.EVP_DigestVerifyInit(mdctx, nil, nil, nil, pkey) != 1 {
log.Fatal("EVP_DigestVerifyInit failed")
}
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)))
if ret < 0 {
log.Fatalf("EVP_DigestVerify errored %d", ret)
}
return ret == 1
}
func runEd25519Test(tv ed25519Vector) bool {
pubkey, err := hex.DecodeString(tv.PublicKey)
if err != nil {
log.Fatalf("Failed to decode key %q: %v", tv.PublicKey, err)
}
sig, err := hex.DecodeString(tv.Signature)
if err != nil {
log.Fatalf("Failed to decode Signature %q: %v", tv.Signature, err)
}
msg := []byte(tv.Message)
// Implementations derived from "ref10" reject `LowOrderResidue` and
// `NonCanonicalR` and accept everything else.
reject := LowOrderResidue | NonCanonicalR
want_verify := (tv.Flags & reject) == 0
c_verified := evpEd25519Verify(pubkey, msg, sig)
go_verified := ed25519.Verify(pubkey, msg, sig)
success := true
if c_verified != want_verify || go_verified != want_verify {
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)
success = false
}
return success
}
func main() {
if _, err := os.Stat(testVectorPath); os.IsNotExist(err) {
fmt.Printf("package cc-testvectors is required for this regress\n")
fmt.Printf("SKIPPED\n")
os.Exit(0)
}
b, err := ioutil.ReadFile(filepath.Join(testVectorPath, ed25519Json))
if err != nil {
log.Fatalf("Failed to read test vectors: %v", err)
}
edv := &ed25519Vectors{}
if err := json.Unmarshal(b, edv); err != nil {
log.Fatalf("Failed to unmarshal JSON: %v", err)
}
success := true
for _, vector := range *edv {
if !runEd25519Test(vector) {
success = false
}
}
if !success {
os.Exit(1)
}
}