1*e7b1675dSTing-Kang Chang// Copyright 2020 Google LLC 2*e7b1675dSTing-Kang Chang// 3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang// 7*e7b1675dSTing-Kang Chang// http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang// 9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang// limitations under the License. 14*e7b1675dSTing-Kang Chang// 15*e7b1675dSTing-Kang Chang//////////////////////////////////////////////////////////////////////////////// 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Changpackage subtle_test 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "bytes" 21*e7b1675dSTing-Kang Chang "encoding/hex" 22*e7b1675dSTing-Kang Chang "fmt" 23*e7b1675dSTing-Kang Chang "math/rand" 24*e7b1675dSTing-Kang Chang "testing" 25*e7b1675dSTing-Kang Chang 26*e7b1675dSTing-Kang Chang "golang.org/x/crypto/chacha20poly1305" 27*e7b1675dSTing-Kang Chang "github.com/google/tink/go/aead/subtle" 28*e7b1675dSTing-Kang Chang "github.com/google/tink/go/subtle/random" 29*e7b1675dSTing-Kang Chang "github.com/google/tink/go/testutil" 30*e7b1675dSTing-Kang Chang) 31*e7b1675dSTing-Kang Chang 32*e7b1675dSTing-Kang Changfunc TestChaCha20Poly1305EncryptDecrypt(t *testing.T) { 33*e7b1675dSTing-Kang Chang for i, test := range chaCha20Poly1305Tests { 34*e7b1675dSTing-Kang Chang key, _ := hex.DecodeString(test.key) 35*e7b1675dSTing-Kang Chang pt, _ := hex.DecodeString(test.plaintext) 36*e7b1675dSTing-Kang Chang ad, _ := hex.DecodeString(test.associatedData) 37*e7b1675dSTing-Kang Chang nonce, _ := hex.DecodeString(test.nonce) 38*e7b1675dSTing-Kang Chang out, _ := hex.DecodeString(test.out) 39*e7b1675dSTing-Kang Chang 40*e7b1675dSTing-Kang Chang ca, err := subtle.NewChaCha20Poly1305(key) 41*e7b1675dSTing-Kang Chang if err != nil { 42*e7b1675dSTing-Kang Chang t.Errorf("#%d, cannot create new instance of ChaCha20Poly1305: %s", i, err) 43*e7b1675dSTing-Kang Chang continue 44*e7b1675dSTing-Kang Chang } 45*e7b1675dSTing-Kang Chang 46*e7b1675dSTing-Kang Chang _, err = ca.Encrypt(pt, ad) 47*e7b1675dSTing-Kang Chang if err != nil { 48*e7b1675dSTing-Kang Chang t.Errorf("#%d, unexpected encryption error: %s", i, err) 49*e7b1675dSTing-Kang Chang continue 50*e7b1675dSTing-Kang Chang } 51*e7b1675dSTing-Kang Chang 52*e7b1675dSTing-Kang Chang var combinedCT []byte 53*e7b1675dSTing-Kang Chang combinedCT = append(combinedCT, nonce...) 54*e7b1675dSTing-Kang Chang combinedCT = append(combinedCT, out...) 55*e7b1675dSTing-Kang Chang if got, err := ca.Decrypt(combinedCT, ad); err != nil { 56*e7b1675dSTing-Kang Chang t.Errorf("#%d, unexpected decryption error: %s", i, err) 57*e7b1675dSTing-Kang Chang continue 58*e7b1675dSTing-Kang Chang } else if !bytes.Equal(pt, got) { 59*e7b1675dSTing-Kang Chang t.Errorf("#%d, plaintext's don't match: got %x vs %x", i, got, pt) 60*e7b1675dSTing-Kang Chang continue 61*e7b1675dSTing-Kang Chang } 62*e7b1675dSTing-Kang Chang } 63*e7b1675dSTing-Kang Chang} 64*e7b1675dSTing-Kang Chang 65*e7b1675dSTing-Kang Changfunc TestChaCha20Poly1305EmptyAssociatedData(t *testing.T) { 66*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 67*e7b1675dSTing-Kang Chang emptyAD := []byte{} 68*e7b1675dSTing-Kang Chang badAD := []byte{1, 2, 3} 69*e7b1675dSTing-Kang Chang 70*e7b1675dSTing-Kang Chang ca, err := subtle.NewChaCha20Poly1305(key) 71*e7b1675dSTing-Kang Chang if err != nil { 72*e7b1675dSTing-Kang Chang t.Fatal(err) 73*e7b1675dSTing-Kang Chang } 74*e7b1675dSTing-Kang Chang 75*e7b1675dSTing-Kang Chang for i := 0; i < 75; i++ { 76*e7b1675dSTing-Kang Chang pt := random.GetRandomBytes(uint32(i)) 77*e7b1675dSTing-Kang Chang // Encrypting with associatedData as a 0-length array 78*e7b1675dSTing-Kang Chang { 79*e7b1675dSTing-Kang Chang ct, err := ca.Encrypt(pt, emptyAD) 80*e7b1675dSTing-Kang Chang if err != nil { 81*e7b1675dSTing-Kang Chang t.Errorf("Encrypt(%x, %x) failed", pt, emptyAD) 82*e7b1675dSTing-Kang Chang continue 83*e7b1675dSTing-Kang Chang } 84*e7b1675dSTing-Kang Chang 85*e7b1675dSTing-Kang Chang if got, err := ca.Decrypt(ct, emptyAD); err != nil || !bytes.Equal(pt, got) { 86*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x", emptyAD, got, pt) 87*e7b1675dSTing-Kang Chang } 88*e7b1675dSTing-Kang Chang if got, err := ca.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) { 89*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x", got, pt) 90*e7b1675dSTing-Kang Chang } 91*e7b1675dSTing-Kang Chang if _, err := ca.Decrypt(ct, badAD); err == nil { 92*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAD) 93*e7b1675dSTing-Kang Chang } 94*e7b1675dSTing-Kang Chang } 95*e7b1675dSTing-Kang Chang // Encrypting with associatedData equal to nil 96*e7b1675dSTing-Kang Chang { 97*e7b1675dSTing-Kang Chang ct, err := ca.Encrypt(pt, nil) 98*e7b1675dSTing-Kang Chang if err != nil { 99*e7b1675dSTing-Kang Chang t.Errorf("Encrypt(%x, nil) failed", pt) 100*e7b1675dSTing-Kang Chang } 101*e7b1675dSTing-Kang Chang 102*e7b1675dSTing-Kang Chang if got, err := ca.Decrypt(ct, emptyAD); err != nil || !bytes.Equal(pt, got) { 103*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", emptyAD, got, pt, err) 104*e7b1675dSTing-Kang Chang } 105*e7b1675dSTing-Kang Chang if got, err := ca.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) { 106*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x; error: %v", got, pt, err) 107*e7b1675dSTing-Kang Chang } 108*e7b1675dSTing-Kang Chang if _, err := ca.Decrypt(ct, badAD); err == nil { 109*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAD) 110*e7b1675dSTing-Kang Chang } 111*e7b1675dSTing-Kang Chang } 112*e7b1675dSTing-Kang Chang } 113*e7b1675dSTing-Kang Chang} 114*e7b1675dSTing-Kang Chang 115*e7b1675dSTing-Kang Changfunc TestChaCha20Poly1305LongMessages(t *testing.T) { 116*e7b1675dSTing-Kang Chang dataSize := uint32(16) 117*e7b1675dSTing-Kang Chang // Encrypts and decrypts messages of size <= 8192. 118*e7b1675dSTing-Kang Chang for dataSize <= 1<<24 { 119*e7b1675dSTing-Kang Chang pt := random.GetRandomBytes(dataSize) 120*e7b1675dSTing-Kang Chang ad := random.GetRandomBytes(dataSize / 3) 121*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 122*e7b1675dSTing-Kang Chang 123*e7b1675dSTing-Kang Chang ca, err := subtle.NewChaCha20Poly1305(key) 124*e7b1675dSTing-Kang Chang if err != nil { 125*e7b1675dSTing-Kang Chang t.Fatal(err) 126*e7b1675dSTing-Kang Chang } 127*e7b1675dSTing-Kang Chang 128*e7b1675dSTing-Kang Chang ct, err := ca.Encrypt(pt, ad) 129*e7b1675dSTing-Kang Chang if err != nil { 130*e7b1675dSTing-Kang Chang t.Errorf("Encrypt(%x, %x) failed", pt, ad) 131*e7b1675dSTing-Kang Chang continue 132*e7b1675dSTing-Kang Chang } 133*e7b1675dSTing-Kang Chang 134*e7b1675dSTing-Kang Chang if got, err := ca.Decrypt(ct, ad); err != nil || !bytes.Equal(pt, got) { 135*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", ad, got, pt, err) 136*e7b1675dSTing-Kang Chang } 137*e7b1675dSTing-Kang Chang 138*e7b1675dSTing-Kang Chang dataSize += 5 * dataSize / 11 139*e7b1675dSTing-Kang Chang } 140*e7b1675dSTing-Kang Chang} 141*e7b1675dSTing-Kang Chang 142*e7b1675dSTing-Kang Changfunc TestChaCha20Poly1305ModifyCiphertext(t *testing.T) { 143*e7b1675dSTing-Kang Chang for i, test := range chaCha20Poly1305Tests { 144*e7b1675dSTing-Kang Chang key, _ := hex.DecodeString(test.key) 145*e7b1675dSTing-Kang Chang pt, _ := hex.DecodeString(test.plaintext) 146*e7b1675dSTing-Kang Chang ad, _ := hex.DecodeString(test.associatedData) 147*e7b1675dSTing-Kang Chang 148*e7b1675dSTing-Kang Chang ca, err := subtle.NewChaCha20Poly1305(key) 149*e7b1675dSTing-Kang Chang if err != nil { 150*e7b1675dSTing-Kang Chang t.Fatal(err) 151*e7b1675dSTing-Kang Chang } 152*e7b1675dSTing-Kang Chang 153*e7b1675dSTing-Kang Chang ct, err := ca.Encrypt(pt, ad) 154*e7b1675dSTing-Kang Chang if err != nil { 155*e7b1675dSTing-Kang Chang t.Errorf("#%d: Encrypt failed", i) 156*e7b1675dSTing-Kang Chang continue 157*e7b1675dSTing-Kang Chang } 158*e7b1675dSTing-Kang Chang 159*e7b1675dSTing-Kang Chang if len(ad) > 0 { 160*e7b1675dSTing-Kang Chang alteredIndex := rand.Intn(len(ad)) 161*e7b1675dSTing-Kang Chang ad[alteredIndex] ^= 0x80 162*e7b1675dSTing-Kang Chang if _, err := ca.Decrypt(ct, ad); err == nil { 163*e7b1675dSTing-Kang Chang t.Errorf("#%d: Decrypt was successful after altering associated data", i) 164*e7b1675dSTing-Kang Chang continue 165*e7b1675dSTing-Kang Chang } 166*e7b1675dSTing-Kang Chang ad[alteredIndex] ^= 0x80 167*e7b1675dSTing-Kang Chang } 168*e7b1675dSTing-Kang Chang 169*e7b1675dSTing-Kang Chang alterCtIdx := rand.Intn(len(ct)) 170*e7b1675dSTing-Kang Chang ct[alterCtIdx] ^= 0x80 171*e7b1675dSTing-Kang Chang if _, err := ca.Decrypt(ct, ad); err == nil { 172*e7b1675dSTing-Kang Chang t.Errorf("#%d: Decrypt was successful after altering ciphertext", i) 173*e7b1675dSTing-Kang Chang continue 174*e7b1675dSTing-Kang Chang } 175*e7b1675dSTing-Kang Chang ct[alterCtIdx] ^= 0x80 176*e7b1675dSTing-Kang Chang } 177*e7b1675dSTing-Kang Chang} 178*e7b1675dSTing-Kang Chang 179*e7b1675dSTing-Kang Chang// This is a very simple test for the randomness of the nonce. 180*e7b1675dSTing-Kang Chang// The test simply checks that the multiple ciphertexts of the same message are distinct. 181*e7b1675dSTing-Kang Changfunc TestChaCha20Poly1305RandomNonce(t *testing.T) { 182*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 183*e7b1675dSTing-Kang Chang ca, err := subtle.NewChaCha20Poly1305(key) 184*e7b1675dSTing-Kang Chang if err != nil { 185*e7b1675dSTing-Kang Chang t.Fatal(err) 186*e7b1675dSTing-Kang Chang } 187*e7b1675dSTing-Kang Chang 188*e7b1675dSTing-Kang Chang cts := make(map[string]bool) 189*e7b1675dSTing-Kang Chang pt, ad := []byte{}, []byte{} 190*e7b1675dSTing-Kang Chang for i := 0; i < 1<<10; i++ { 191*e7b1675dSTing-Kang Chang ct, err := ca.Encrypt(pt, ad) 192*e7b1675dSTing-Kang Chang ctHex := hex.EncodeToString(ct) 193*e7b1675dSTing-Kang Chang if err != nil || cts[ctHex] { 194*e7b1675dSTing-Kang Chang t.Errorf("TestRandomNonce failed: %v", err) 195*e7b1675dSTing-Kang Chang } else { 196*e7b1675dSTing-Kang Chang cts[ctHex] = true 197*e7b1675dSTing-Kang Chang } 198*e7b1675dSTing-Kang Chang } 199*e7b1675dSTing-Kang Chang} 200*e7b1675dSTing-Kang Chang 201*e7b1675dSTing-Kang Changfunc TestChaCha20Poly1305WycheproofCases(t *testing.T) { 202*e7b1675dSTing-Kang Chang testutil.SkipTestIfTestSrcDirIsNotSet(t) 203*e7b1675dSTing-Kang Chang suite := new(AEADSuite) 204*e7b1675dSTing-Kang Chang if err := testutil.PopulateSuite(suite, "chacha20_poly1305_test.json"); err != nil { 205*e7b1675dSTing-Kang Chang t.Fatalf("failed populating suite: %s", err) 206*e7b1675dSTing-Kang Chang } 207*e7b1675dSTing-Kang Chang for _, group := range suite.TestGroups { 208*e7b1675dSTing-Kang Chang if group.KeySize/8 != chacha20poly1305.KeySize { 209*e7b1675dSTing-Kang Chang continue 210*e7b1675dSTing-Kang Chang } 211*e7b1675dSTing-Kang Chang if group.IvSize/8 != chacha20poly1305.NonceSize { 212*e7b1675dSTing-Kang Chang continue 213*e7b1675dSTing-Kang Chang } 214*e7b1675dSTing-Kang Chang 215*e7b1675dSTing-Kang Chang for _, test := range group.Tests { 216*e7b1675dSTing-Kang Chang caseName := fmt.Sprintf("%s-%s:Case-%d", suite.Algorithm, group.Type, test.CaseID) 217*e7b1675dSTing-Kang Chang t.Run(caseName, func(t *testing.T) { runChaCha20Poly1305WycheproofCase(t, test) }) 218*e7b1675dSTing-Kang Chang } 219*e7b1675dSTing-Kang Chang } 220*e7b1675dSTing-Kang Chang} 221*e7b1675dSTing-Kang Chang 222*e7b1675dSTing-Kang Changfunc runChaCha20Poly1305WycheproofCase(t *testing.T, tc *AEADCase) { 223*e7b1675dSTing-Kang Chang var combinedCT []byte 224*e7b1675dSTing-Kang Chang combinedCT = append(combinedCT, tc.Iv...) 225*e7b1675dSTing-Kang Chang combinedCT = append(combinedCT, tc.Ct...) 226*e7b1675dSTing-Kang Chang combinedCT = append(combinedCT, tc.Tag...) 227*e7b1675dSTing-Kang Chang 228*e7b1675dSTing-Kang Chang ca, err := subtle.NewChaCha20Poly1305(tc.Key) 229*e7b1675dSTing-Kang Chang if err != nil { 230*e7b1675dSTing-Kang Chang t.Fatalf("cannot create new instance of ChaCha20Poly1305: %s", err) 231*e7b1675dSTing-Kang Chang } 232*e7b1675dSTing-Kang Chang 233*e7b1675dSTing-Kang Chang _, err = ca.Encrypt(tc.Msg, tc.Aad) 234*e7b1675dSTing-Kang Chang if err != nil { 235*e7b1675dSTing-Kang Chang t.Fatalf("unexpected encryption error: %s", err) 236*e7b1675dSTing-Kang Chang } 237*e7b1675dSTing-Kang Chang 238*e7b1675dSTing-Kang Chang decrypted, err := ca.Decrypt(combinedCT, tc.Aad) 239*e7b1675dSTing-Kang Chang if err != nil { 240*e7b1675dSTing-Kang Chang if tc.Result == "valid" { 241*e7b1675dSTing-Kang Chang t.Errorf("unexpected error: %s", err) 242*e7b1675dSTing-Kang Chang } 243*e7b1675dSTing-Kang Chang } else { 244*e7b1675dSTing-Kang Chang if tc.Result == "invalid" { 245*e7b1675dSTing-Kang Chang t.Error("decrypted invalid") 246*e7b1675dSTing-Kang Chang } 247*e7b1675dSTing-Kang Chang if !bytes.Equal(decrypted, tc.Msg) { 248*e7b1675dSTing-Kang Chang t.Error("incorrect decryption") 249*e7b1675dSTing-Kang Chang } 250*e7b1675dSTing-Kang Chang } 251*e7b1675dSTing-Kang Chang} 252