From 6bc21dcfb6594592b4d211c086f8b8e3336b10c0 Mon Sep 17 00:00:00 2001 From: Tink Team <tink-dev@google.com> Date: Thu, 21 Mar 2019 06:43:52 -0700 Subject: [PATCH] Add Go structure for running Wycheproof test cases PiperOrigin-RevId: 239585715 GitOrigin-RevId: 30c96506a61203162d24c1107f7f0db3a7b360d6 --- go/subtle/kwp/BUILD.bazel | 4 + go/subtle/kwp/kwp_test.go | 147 +++++++++++++++++++---------- go/testutil/BUILD.bazel | 9 +- go/testutil/wycheproofutil.go | 56 +++++++++++ go/testutil/wycheproofutil_test.go | 64 +++++++++++++ 5 files changed, 230 insertions(+), 50 deletions(-) create mode 100644 go/testutil/wycheproofutil.go create mode 100644 go/testutil/wycheproofutil_test.go diff --git a/go/subtle/kwp/BUILD.bazel b/go/subtle/kwp/BUILD.bazel index fa9ac4a50..481fed7c2 100644 --- a/go/subtle/kwp/BUILD.bazel +++ b/go/subtle/kwp/BUILD.bazel @@ -11,8 +11,12 @@ go_library( go_test( name = "go_default_test", srcs = ["kwp_test.go"], + data = [ + "@wycheproof//testvectors:all", + ], deps = [ ":go_default_library", "//go/subtle/random:go_default_library", + "//go/testutil:go_default_library", ], ) diff --git a/go/subtle/kwp/kwp_test.go b/go/subtle/kwp/kwp_test.go index 9c0604fb2..2ffa4354d 100644 --- a/go/subtle/kwp/kwp_test.go +++ b/go/subtle/kwp/kwp_test.go @@ -17,11 +17,13 @@ package kwp_test import ( "bytes" "encoding/hex" + "encoding/json" "fmt" "testing" "github.com/google/tink/go/subtle/kwp" "github.com/google/tink/go/subtle/random" + "github.com/google/tink/go/testutil" ) func TestWrapUnwrap(t *testing.T) { @@ -86,61 +88,108 @@ func TestInvalidWrappingSizes(t *testing.T) { } } -func TestKnownVectors(t *testing.T) { - // vectors from Wycheproof - vectors := []struct { - id int - key, msg, ct string - }{ - { - 1, - "6f67486d1e914419cb43c28509c7c1ea", - "8dc0632d92ee0be4f740028410b08270", - "8cd63fa6788aa5edfa753fc87d645a672b14107c3b4519e7", - }, - { - 76, - "fce0429c610658ef8e7cfb0154c51de2239a8a317f5af5b6714f985fb5c4d75c", - "287326b5ed0078e7ca0164d748f667e7", - "e3eab96d9a2fda12f9e252053aff15e753e5ea6f5172c92b", - }, +type KwpCase struct { + testutil.WycheproofCase + Key string `json:"key"` + Message string `json:"msg"` + Ciphertext string `json:"ct"` +} + +type KwpGroup struct { + testutil.WycheproofGroup + KeySize int `json:"keySize"` + Tests []*KwpCase `json:"tests"` +} + +type KwpSuite struct { + testutil.WycheproofSuite + Groups []*KwpGroup `json:"testGroups"` +} + +func TestWycheproofCases(t *testing.T) { + suiteBytes, err := testutil.ReadWycheproofTests("kwp_test.json") + if err != nil { + t.Fatal(err) } - for _, v := range vectors { - t.Run(fmt.Sprintf("Vector%d", v.id), func(t *testing.T) { - kek, err := hex.DecodeString(v.key) - if err != nil { - t.Fatal("hex.DecodeString(v.key) => bad kek") - } + suite := new(KwpSuite) + err = json.Unmarshal(suiteBytes, suite) + if err != nil { + t.Fatal(err) + } - msg, err := hex.DecodeString(v.msg) - if err != nil { - t.Fatal("hex.DecodeString(v.msg) => bad msg") - } + for _, group := range suite.Groups { + if group.KeySize == 192 { + continue + } - ct, err := hex.DecodeString(v.ct) - if err != nil { - t.Fatal("hex.DecodeString(v.ct) => bad ciphertext") - } + for _, test := range group.Tests { + caseName := fmt.Sprintf("%s-%s(%d):Case-%d", + suite.Algorithm, group.Type, group.KeySize, test.CaseID) + t.Run(caseName, func(t *testing.T) { runWycheproofCase(t, test) }) + } + } +} - cipher, err := kwp.NewKWP(kek) - if err != nil { - t.Fatalf("cannot create kwp, error: %v", err) - } +func runWycheproofCase(t *testing.T, testCase *KwpCase) { + kek, err := hex.DecodeString(testCase.Key) + if err != nil { + t.Fatalf("hex.DecodeString(testCase.Key) => %v", err) + } - wrapped, err := cipher.Wrap(msg) - if err != nil { - t.Errorf("cannot wrap, error: %v", err) - } else if !bytes.Equal(ct, wrapped) { - t.Error("wrapped key mismatches test vector") - } + msg, err := hex.DecodeString(testCase.Message) + if err != nil { + t.Fatalf("hex.DecodeString(testCase.Message) => %v", err) + } - unwrapped, err := cipher.Unwrap(ct) - if err != nil { - t.Errorf("cannot unwrap, error: %v", err) - } else if !bytes.Equal(msg, unwrapped) { - t.Error("unwrapped key mismatches test vector") - } - }) + ct, err := hex.DecodeString(testCase.Ciphertext) + if err != nil { + t.Fatalf("hex.DecodeString(testCase.Ciphertext) => %v", err) + } + + cipher, err := kwp.NewKWP(kek) + if err != nil { + switch testCase.Result { + case "valid": + t.Fatalf("cannot create kwp, error: %v", err) + case "invalid", "acceptable": + return + } + } + + wrapped, err := cipher.Wrap(msg) + switch testCase.Result { + case "valid": + if err != nil { + t.Errorf("cannot wrap, error: %v", err) + } else if !bytes.Equal(ct, wrapped) { + t.Error("wrapped key mismatches test vector") + } + case "invalid": + if err == nil && bytes.Equal(ct, wrapped) { + t.Error("no error and wrapped key matches test vector for invalid case") + } + case "acceptable": + if err == nil && !bytes.Equal(ct, wrapped) { + t.Error("no error and wrapped key mismatches test vector for acceptable case") + } + } + + unwrapped, err := cipher.Unwrap(ct) + switch testCase.Result { + case "valid": + if err != nil { + t.Errorf("cannot unwrap, error: %v", err) + } else if !bytes.Equal(msg, unwrapped) { + t.Error("unwrapped key mismatches test vector") + } + case "invalid": + if err == nil { + t.Error("no error unwrapping invalid case") + } + case "acceptable": + if err == nil && !bytes.Equal(msg, unwrapped) { + t.Error("no error and unwrapped key mismatches plaintext") + } } } diff --git a/go/testutil/BUILD.bazel b/go/testutil/BUILD.bazel index f365bb056..84e448d7b 100644 --- a/go/testutil/BUILD.bazel +++ b/go/testutil/BUILD.bazel @@ -7,6 +7,7 @@ go_library( srcs = [ "constant.go", "testutil.go", + "wycheproofutil.go", ], importpath = "github.com/google/tink/go/testutil", deps = [ @@ -36,7 +37,13 @@ go_library( go_test( name = "go_default_test", - srcs = ["testutil_test.go"], + srcs = [ + "testutil_test.go", + "wycheproofutil_test.go", + ], + data = [ + "@wycheproof//testvectors:all", + ], deps = [ "//go/testutil:go_default_library", "//go/tink:go_default_library", diff --git a/go/testutil/wycheproofutil.go b/go/testutil/wycheproofutil.go new file mode 100644 index 000000000..2615cfa1c --- /dev/null +++ b/go/testutil/wycheproofutil.go @@ -0,0 +1,56 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +package testutil + +import ( + "fmt" + "io/ioutil" +) + +// WycheproofSuite represents the common elements of the top level +// object in a Wycheproof json file. Implementations should embed +// WycheproofSuite in a struct that strongly types the testGroups +// field. See wycheproofutil_test.go for an example. +type WycheproofSuite struct { + Algorithm string `json:"algorithm"` + GeneratorVersion string `json:"generatorVersion"` + NumberOfTests int `json:"numberOfTests"` + Notes map[string]string `json:"notes"` +} + +// WycheproofGroup represents the common elements of a testGroups +// object in a Wycheproof suite. Implementations should embed +// WycheproofGroup in a struct that strongly types its list of cases. +// See wycheproofutil_test.go for an example. +type WycheproofGroup struct { + Type string `json:"type"` +} + +// WycheproofCase represents the common elements of a tests object +// in a Wycheproof group. Implementation should embed WycheproofCase +// in a struct that contains fields specific to the test type. +// See wycheproofutil_test.go for an example. +type WycheproofCase struct { + CaseID int `json:"tcId"` + Comment string `json:"comment"` + Result string `json:"result"` + Flags []string `json:"flags"` +} + +// ReadWycheproofTests loads the named document (for example, "aes_gcm_test.json") +// from the Wycheproof test vectors directory. +func ReadWycheproofTests(filename string) ([]byte, error) { + return ioutil.ReadFile(fmt.Sprintf("../../../wycheproof/testvectors/%s", filename)) +} diff --git a/go/testutil/wycheproofutil_test.go b/go/testutil/wycheproofutil_test.go new file mode 100644 index 000000000..2b74cc887 --- /dev/null +++ b/go/testutil/wycheproofutil_test.go @@ -0,0 +1,64 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +package testutil_test + +import ( + "encoding/json" + "testing" + + "github.com/google/tink/go/testutil" +) + +func TestWycheproofParsing(t *testing.T) { + + type AeadTest struct { + testutil.WycheproofCase + Key string `json:"key"` + IV string `json:"iv"` + AAD string `json:"aad"` + Message string `json:"msg"` + Ciphertext string `json:"ct"` + Tag string `json:"tag"` + } + + type AeadGroup struct { + testutil.WycheproofGroup + Tests []*AeadTest `json:"tests"` + } + + type AeadSuite struct { + testutil.WycheproofSuite + TestGroups []*AeadGroup `json:"testGroups"` + } + + bytes, err := testutil.ReadWycheproofTests("aes_gcm_test.json") + if err != nil { + t.Fatal(err) + } + + suite := new(AeadSuite) + err = json.Unmarshal(bytes, suite) + if err != nil { + t.Fatal(err) + } + + if suite.Algorithm != "AES-GCM" { + t.Errorf("suite.Algorithm=%s, want AES-GCM", suite.Algorithm) + } + + if suite.TestGroups[0].Tests[0].Key == "" { + t.Error("suite.TestGroups[0].Tests[0].Key is empty") + } +} -- GitLab