| // Copyright 2020 Google LLC |
| // |
| // 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 subtle_test |
| |
| import ( |
| "encoding/hex" |
| "encoding/json" |
| "os" |
| "path/filepath" |
| "testing" |
| |
| "github.com/google/tink/go/mac/subtle" |
| "github.com/google/tink/go/subtle/random" |
| ) |
| |
| var ( |
| // Test vectors from RFC 4493. |
| keyRFC4493, _ = hex.DecodeString("2b7e151628aed2a6abf7158809cf4f3c") |
| dataRFC4493, _ = hex.DecodeString("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710") |
| expected = map[int]string{ |
| 0: "bb1d6929e95937287fa37d129b756746", |
| 16: "070a16b46b4d4144f79bdd9dd04a287c", |
| 40: "dfa66747de9ae63030ca32611497c827", |
| 64: "51f0bebf7e3b9d92fc49741779363cfe", |
| } |
| ) |
| |
| type testdata struct { |
| Algorithm string |
| GeneratorVersion string |
| NumberOfTests uint32 |
| TestGroups []*testgroup |
| } |
| |
| type testgroup struct { |
| KeySize uint32 |
| TagSize uint32 |
| Type string |
| Tests []*testcase |
| } |
| |
| type testcase struct { |
| Comment string |
| Key string |
| Msg string |
| Result string |
| Tag string |
| TcID uint32 |
| } |
| |
| func TestVectorsWycheproof(t *testing.T) { |
| srcDir, ok := os.LookupEnv("TEST_SRCDIR") |
| if !ok { |
| t.Skip("TEST_SRCDIR not set") |
| } |
| f, err := os.Open(filepath.Join(srcDir, "wycheproof/testvectors/aes_cmac_test.json")) |
| if err != nil { |
| t.Fatalf("cannot open file: %s", err) |
| } |
| parser := json.NewDecoder(f) |
| data := new(testdata) |
| if err := parser.Decode(data); err != nil { |
| t.Fatalf("cannot decode test data: %s", err) |
| } |
| |
| for _, g := range data.TestGroups { |
| for _, tc := range g.Tests { |
| key, err := hex.DecodeString(tc.Key) |
| if err != nil || uint32(len(key))*8 != g.KeySize { |
| t.Errorf("Could not decode key for test case %d (%s): %v", tc.TcID, tc.Comment, err) |
| continue |
| } |
| msg, err := hex.DecodeString(tc.Msg) |
| if err != nil { |
| t.Errorf("Could not decode message for test case %d (%s): %v", tc.TcID, tc.Comment, err) |
| continue |
| } |
| tag, err := hex.DecodeString(tc.Tag) |
| if err != nil { |
| t.Errorf("Could not decode expected tag for test case %d (%s): %v", tc.TcID, tc.Comment, err) |
| continue |
| } |
| if g.TagSize%8 != 0 { |
| t.Errorf("Requested tag size for test case %d (%s) is not a multiple of 8, but %d", tc.TcID, tc.Comment, g.TagSize) |
| continue |
| } |
| aes, err := subtle.NewAESCMAC(key, g.TagSize/8) |
| valid := tc.Result == "valid" |
| if valid && err != nil { |
| t.Errorf("Could not create subtle.CMAC for test case %d (%s): %v", tc.TcID, tc.Comment, err) |
| continue |
| } |
| if !valid && err != nil { |
| continue |
| } |
| res, err := aes.ComputeMAC(msg) |
| if valid && err != nil { |
| t.Errorf("Could not compute AES-CMAC for test case %d (%s): %v", tc.TcID, tc.Comment, err) |
| continue |
| } |
| if valid && hex.EncodeToString(res) != tc.Tag { |
| t.Errorf("Compute AES-CMAC and expected for test case %d (%s) do not match:\nComputed: %q\nExpected: %q", tc.TcID, tc.Comment, hex.EncodeToString(res), tc.Tag) |
| } |
| if !valid && hex.EncodeToString(res) == tc.Tag && err == nil { |
| t.Errorf("Compute AES-CMAC and invalid expected for test case %d (%s) match:\nComputed: %q\nExpected: %q", tc.TcID, tc.Comment, hex.EncodeToString(res), tc.Tag) |
| } |
| err = aes.VerifyMAC(tag, msg) |
| if valid && err != nil { |
| t.Errorf("Could not verify MAC for test case %d (%s): %v", tc.TcID, tc.Comment, err) |
| } |
| if !valid && err == nil { |
| t.Errorf("Verified invalid MAC for test case %d (%s)", tc.TcID, tc.Comment) |
| } |
| } |
| } |
| } |
| |
| func TestCMACBasic(t *testing.T) { |
| a, err := subtle.NewAESCMAC(keyRFC4493, 16) |
| if err != nil { |
| t.Errorf("Could not create subtle.CMAC object: %v", err) |
| } |
| for l, e := range expected { |
| output, err := a.ComputeMAC(dataRFC4493[:l]) |
| if err != nil { |
| t.Errorf("Error computing AES-CMAC: %v", err) |
| } |
| if hex.EncodeToString(output) != e { |
| t.Errorf("Computation and test vector differ. Computation: %q, Test Vector %q", hex.EncodeToString(output), e) |
| } |
| exp, err := hex.DecodeString(e) |
| if err != nil { |
| t.Errorf("Could not decode expected string %q: %v", e, err) |
| } |
| err = a.VerifyMAC(exp, dataRFC4493[:l]) |
| if err != nil { |
| t.Errorf("Verification of test vector failed. Test Vector %q, Verification %v", e, err) |
| } |
| } |
| } |
| |
| func TestNewCMACWithInvalidInput(t *testing.T) { |
| // key too short |
| _, err := subtle.NewAESCMAC(random.GetRandomBytes(1), 16) |
| if err == nil { |
| t.Errorf("expect an error when key is too short") |
| } |
| // tag too short |
| _, err = subtle.NewAESCMAC(random.GetRandomBytes(16), 9) |
| if err == nil { |
| t.Errorf("expect an error when tag size is too small") |
| } |
| // tag too big |
| _, err = subtle.NewAESCMAC(random.GetRandomBytes(16), 17) |
| if err == nil { |
| t.Errorf("expect an error when tag size is too big") |
| } |
| } |
| |
| func TestCMACComputeVerifyWithNilInput(t *testing.T) { |
| cipher, err := subtle.NewAESCMAC(random.GetRandomBytes(16), 16) |
| if err != nil { |
| t.Errorf("unexpected error when creating new CMAC") |
| } |
| tag, err := cipher.ComputeMAC(nil) |
| if err != nil { |
| t.Errorf("cipher.ComputeMAC(nil) failed: %v", err) |
| } |
| if err := cipher.VerifyMAC(tag, nil); err != nil { |
| t.Errorf("cipher.VerifyMAC(tag, nil) failed: %v", err) |
| } |
| } |
| |
| func TestCMACVerifyMACWithInvalidInput(t *testing.T) { |
| cipher, err := subtle.NewAESCMAC(random.GetRandomBytes(16), 16) |
| if err != nil { |
| t.Errorf("unexpected error when creating new CMAC") |
| } |
| if err := cipher.VerifyMAC(nil, []byte{1}); err == nil { |
| t.Errorf("expect an error when mac is nil") |
| } |
| if err := cipher.VerifyMAC([]byte{1}, nil); err == nil { |
| t.Errorf("expect an error when data is nil") |
| } |
| if err := cipher.VerifyMAC(nil, nil); err == nil { |
| t.Errorf("cipher.VerifyMAC(nil, nil) succeeded unexpectedly") |
| } |
| } |
| |
| func TestCMACModification(t *testing.T) { |
| a, err := subtle.NewAESCMAC(keyRFC4493, 16) |
| if err != nil { |
| t.Errorf("Could not create subtle.CMAC object: %v", err) |
| } |
| for l, e := range expected { |
| exp, err := hex.DecodeString(e) |
| if err != nil { |
| t.Errorf("Could not decode expected string %q: %v", e, err) |
| } |
| for i := 0; i < len(exp); i++ { |
| for j := 0; j < 8; j++ { |
| notExpected := make([]byte, 16) |
| copy(notExpected, exp) |
| notExpected[i] ^= 1 << uint8(j) |
| err = a.VerifyMAC(notExpected, dataRFC4493[:l]) |
| if err == nil { |
| t.Errorf("Verification of modified test vector did not fail. Test Vector %q, Modified: %q", e, hex.EncodeToString(notExpected)) |
| } |
| } |
| } |
| } |
| } |
| |
| func TestCMACTruncation(t *testing.T) { |
| a, err := subtle.NewAESCMAC(keyRFC4493, 16) |
| if err != nil { |
| t.Errorf("Could not create subtle.CMAC object: %v", err) |
| } |
| for l, e := range expected { |
| exp, err := hex.DecodeString(e) |
| if err != nil { |
| t.Errorf("Could not decode expected string %q: %v", e, err) |
| } |
| for i := 1; i < len(exp); i++ { |
| notExpected := exp[:i] |
| err = a.VerifyMAC(notExpected, dataRFC4493[:l]) |
| if err == nil { |
| t.Errorf("Verification of truncated test vector did not fail. Test Vector %q, Modified: %q", e, hex.EncodeToString(notExpected)) |
| } |
| } |
| } |
| } |
| |
| func TestCMACSmallerTagSize(t *testing.T) { |
| for i := 10; i <= 16; i++ { |
| a, err := subtle.NewAESCMAC(keyRFC4493, uint32(i)) |
| if err != nil { |
| t.Errorf("Could not create subtle.CMAC object: %v", err) |
| } |
| for l, e := range expected { |
| exp, err := hex.DecodeString(e) |
| if err != nil { |
| t.Errorf("Could not decode expected string %q: %v", e, err) |
| } |
| err = a.VerifyMAC(exp[:i], dataRFC4493[:l]) |
| if err != nil { |
| t.Errorf("Verification of smaller tag test vector did fail. Test Vector %q, Verification: %v", hex.EncodeToString(exp[:i]), err) |
| } |
| } |
| } |
| } |