-/* $OpenBSD: wycheproof.go,v 1.5 2018/08/10 16:22:58 jsing Exp $ */
+/* $OpenBSD: wycheproof.go,v 1.6 2018/08/20 17:06:18 tb Exp $ */
/*
* Copyright (c) 2018 Joel Sing <jsing@openbsd.org>
*
#include <openssl/curve25519.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/rsa.h>
*/
const testVectorPath = "/usr/local/share/wycheproof/testvectors"
+type wycheproofTestGroupChaCha20Poly1305 struct {
+ IVSize int `json:"ivSize"`
+ KeySize int `json:"keySize"`
+ TagSize int `json:"tagSize"`
+ Type string `json:"type"`
+ Tests []*wycheproofTestChaCha20Poly1305 `json:"tests"`
+}
+
+type wycheproofTestChaCha20Poly1305 struct {
+ TCID int `json:"tcId"`
+ Comment string `json:"comment"`
+ Key string `json:"key"`
+ IV string `json:"iv"`
+ AAD string `json:"aad"`
+ Msg string `json:"msg"`
+ CT string `json:"ct"`
+ Tag string `json:"tag"`
+ Result string `json:"result"`
+ Flags []string `json:"flags"`
+}
+
type wycheproofECDSAKey struct {
Curve string `json:"curve"`
KeySize int `json:"keySize"`
}
}
+func runChaCha20Poly1305Test(iv_len int, key_len int, tag_len int, wt *wycheproofTestChaCha20Poly1305) bool {
+ aead := C.EVP_aead_chacha20_poly1305()
+ if aead == nil {
+ log.Fatal("EVP_aead_chacha20_poly1305 failed")
+ }
+
+ key, err := hex.DecodeString(wt.Key)
+ if err != nil {
+ log.Fatalf("Failed to decode key %q: %v", wt.Key, err)
+ }
+ if key_len != len(key) {
+ log.Fatalf("Key length mismatch: got %d, want %d", len(key), key_len)
+ }
+
+ var ctx C.EVP_AEAD_CTX
+ if C.EVP_AEAD_CTX_init((*C.EVP_AEAD_CTX)(unsafe.Pointer(&ctx)), aead, (*C.uchar)(unsafe.Pointer(&key[0])), C.size_t(key_len), C.size_t(tag_len), nil) != 1 {
+ log.Fatalf("Failed to initialize AEAD context")
+ }
+
+ iv, err := hex.DecodeString(wt.IV)
+ if err != nil {
+ log.Fatalf("Failed to decode key %q: %v", wt.IV, err)
+ }
+ if iv_len != len(iv) {
+ log.Fatalf("IV length mismatch: got %d, want %d", len(iv), iv_len)
+ }
+ aad, err := hex.DecodeString(wt.AAD)
+ if err != nil {
+ log.Fatalf("Failed to decode AAD %q: %v", wt.AAD, err)
+ }
+ msg, err := hex.DecodeString(wt.Msg)
+ if err != nil {
+ log.Fatalf("Failed to decode msg %q: %v", wt.Msg, err)
+ }
+
+ ivLen, aadLen, msgLen := len(iv), len(aad), len(msg)
+ if ivLen == 0 {
+ iv = append(iv, 0)
+ }
+ if aadLen == 0 {
+ aad = append(aad, 0)
+ }
+ if msgLen == 0 {
+ msg = append(msg, 0)
+ }
+
+ maxOutLen := msgLen + tag_len
+ out := make([]byte, maxOutLen)
+ var outLen C.size_t
+
+ ret := C.EVP_AEAD_CTX_seal((*C.EVP_AEAD_CTX)(unsafe.Pointer(&ctx)), (*C.uint8_t)(unsafe.Pointer(&out[0])), (*C.size_t)(unsafe.Pointer(&outLen)), C.size_t(maxOutLen), (*C.uint8_t)(unsafe.Pointer(&iv[0])), C.size_t(ivLen), (*C.uint8_t)(unsafe.Pointer(&msg[0])), C.size_t(msgLen), (*C.uint8_t)(unsafe.Pointer(&aad[0])), C.size_t(aadLen))
+
+ C.EVP_AEAD_CTX_cleanup((*C.EVP_AEAD_CTX)(unsafe.Pointer(&ctx)))
+
+ if ret != 1 && wt.Result == "invalid" {
+ fmt.Printf("INFO: Test case %d (%q) - EVP_AEAD_CTX_seal() = %d, want %v\n", wt.TCID, wt.Comment, int(ret), wt.Result)
+ return true
+ }
+
+ if (outLen != C.size_t(maxOutLen)) {
+ fmt.Printf("FAIL: Test case %d (%q) - ChaCha output length mismatch: got %d, want %d", wt.TCID, wt.Comment, outLen, maxOutLen)
+ return false
+ }
+
+ outCt := out[0:msgLen]
+ outTag := out[msgLen: maxOutLen]
+
+ ct, err := hex.DecodeString(wt.CT)
+ if err != nil {
+ log.Fatalf("Failed to decode ct %q: %v", wt.CT, err)
+ }
+ if len(ct) != msgLen {
+ fmt.Printf("FAIL: Test case %d (%q) - msg length %d doesn't match ct length %d", wt.TCID, wt.Comment, msgLen, len(ct))
+ return false
+ }
+ tag, err := hex.DecodeString(wt.Tag)
+ if err != nil {
+ log.Fatalf("Failed to decode tag %q: %v", wt.Tag, err)
+ }
+ if len(tag) != tag_len {
+ fmt.Printf("FAIL: Test case %d (%q) tag length: got %d, want %d", wt.TCID, wt.Comment, len(tag), tag_len)
+ return false
+ }
+
+ success := false
+ if (bytes.Equal(outCt, ct) && bytes.Equal(outTag, tag)) || wt.Result == "invalid" {
+ success = true
+ } else {
+ fmt.Printf("FAIL: Test case %d (%q) - EVP_AEAD_CTX_seal() = %d, ct match: %t, tag match: %t; want %v\n", wt.TCID, wt.Comment, int(ret), bytes.Equal(outCt, ct), bytes.Equal(outTag, tag), wt.Result)
+ }
+
+ return success
+}
+
+func runChaCha20Poly1305TestGroup(wtg *wycheproofTestGroupChaCha20Poly1305) bool {
+ // We currently only support nonces of length 12 (96 bits)
+ if wtg.IVSize != 96 {
+ return true
+ }
+
+ fmt.Printf("Running ChaCha20-Poly1305 test group %v with IV size %d, key size %d, tag size %d...\n", wtg.Type, wtg.IVSize, wtg.KeySize, wtg.TagSize)
+
+ success := true
+ for _, wt := range wtg.Tests {
+ if !runChaCha20Poly1305Test(wtg.IVSize / 8, wtg.KeySize / 8, wtg.TagSize / 8, wt) {
+ success = false
+ }
+ }
+ return success
+}
+
func runECDSATest(ecKey *C.EC_KEY, nid int, h hash.Hash, wt *wycheproofTestECDSA) bool {
msg, err := hex.DecodeString(wt.Msg)
if err != nil {
var wtg interface{}
switch wtv.Algorithm {
+ case "CHACHA20-POLY1305":
+ wtg = &wycheproofTestGroupChaCha20Poly1305{}
case "ECDSA":
wtg = &wycheproofTestGroupECDSA{}
case "RSASig":
log.Fatalf("Failed to unmarshal test groups JSON: %v", err)
}
switch wtv.Algorithm {
+ case "CHACHA20-POLY1305":
+ if !runChaCha20Poly1305TestGroup(wtg.(*wycheproofTestGroupChaCha20Poly1305)) {
+ success = false
+ }
case "ECDSA":
if !runECDSATestGroup(wtg.(*wycheproofTestGroupECDSA)) {
success = false
name string
pattern string
}{
+ {"ChaCha20-Poly1305", "chacha20_poly1305_test.json"},
{"ECDSA", "ecdsa_[^w]*test.json"}, // Skip ecdsa_webcrypto_test.json for now.
{"RSA signature", "rsa_signature_*test.json"},
{"X25519", "x25519_*test.json"},