internal/wycheproof: add test for hkdf key extraction

Change-Id: I501bcc2f4bd8107937756087d20e849e3a4bc182
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/214584
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/internal/wycheproof/hkdf_test.go b/internal/wycheproof/hkdf_test.go
new file mode 100644
index 0000000..6b72e2c
--- /dev/null
+++ b/internal/wycheproof/hkdf_test.go
@@ -0,0 +1,111 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package wycheproof
+
+import (
+	"bytes"
+	"io"
+	"testing"
+
+	"golang.org/x/crypto/hkdf"
+)
+
+func TestHkdf(t *testing.T) {
+
+	// HkdfTestVector
+	type HkdfTestVector struct {
+
+		// A brief description of the test case
+		Comment string `json:"comment,omitempty"`
+
+		// A list of flags
+		Flags []string `json:"flags,omitempty"`
+
+		// the key (input key material)
+		Ikm string `json:"ikm,omitempty"`
+
+		// additional information used in the key derivation
+		Info string `json:"info,omitempty"`
+
+		// the generated bytes (output key material)
+		Okm string `json:"okm,omitempty"`
+
+		// Test result
+		Result string `json:"result,omitempty"`
+
+		// the salt for the key derivation
+		Salt string `json:"salt,omitempty"`
+
+		// the size of the output in bytes
+		Size int `json:"size,omitempty"`
+
+		// Identifier of the test case
+		TcId int `json:"tcId,omitempty"`
+	}
+
+	// Notes a description of the labels used in the test vectors
+	type Notes struct {
+	}
+
+	// HkdfTestGroup
+	type HkdfTestGroup struct {
+
+		// the size of the ikm in bits
+		KeySize int               `json:"keySize,omitempty"`
+		Tests   []*HkdfTestVector `json:"tests,omitempty"`
+		Type    interface{}       `json:"type,omitempty"`
+	}
+
+	// Root
+	type Root struct {
+
+		// the primitive tested in the test file
+		Algorithm string `json:"algorithm,omitempty"`
+
+		// the version of the test vectors.
+		GeneratorVersion string `json:"generatorVersion,omitempty"`
+
+		// additional documentation
+		Header []string `json:"header,omitempty"`
+
+		// a description of the labels used in the test vectors
+		Notes *Notes `json:"notes,omitempty"`
+
+		// the number of test vectors in this test
+		NumberOfTests int              `json:"numberOfTests,omitempty"`
+		Schema        interface{}      `json:"schema,omitempty"`
+		TestGroups    []*HkdfTestGroup `json:"testGroups,omitempty"`
+	}
+
+	fileHashAlgorithms := map[string]string{
+		"hkdf_sha1_test.json":   "SHA-1",
+		"hkdf_sha256_test.json": "SHA-256",
+		"hkdf_sha384_test.json": "SHA-384",
+		"hkdf_sha512_test.json": "SHA-512",
+	}
+
+	for f := range fileHashAlgorithms {
+		var root Root
+		readTestVector(t, f, &root)
+		for _, tg := range root.TestGroups {
+			for _, tv := range tg.Tests {
+				h := parseHash(fileHashAlgorithms[f]).New
+				hkdf := hkdf.New(h, decodeHex(tv.Ikm), decodeHex(tv.Salt), decodeHex(tv.Info))
+				key := make([]byte, tv.Size)
+				wantPass := shouldPass(tv.Result, tv.Flags, nil)
+				_, err := io.ReadFull(hkdf, key)
+				if (err == nil) != wantPass {
+					t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t, got: %v", tv.TcId, tv.Result, tv.Comment, wantPass, err)
+				}
+				if err != nil {
+					continue // don't validate output text if reading failed
+				}
+				if got, want := key, decodeHex(tv.Okm); !bytes.Equal(got, want) {
+					t.Errorf("tcid: %d, type: %s, comment: %q, output bytes don't match", tv.TcId, tv.Result, tv.Comment)
+				}
+			}
+		}
+	}
+}