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 TestXChaCha20Poly1305EncryptDecrypt(t *testing.T) { 33*e7b1675dSTing-Kang Chang for i, test := range xChaCha20Poly1305Tests { 34*e7b1675dSTing-Kang Chang key, _ := hex.DecodeString(test.key) 35*e7b1675dSTing-Kang Chang pt, _ := hex.DecodeString(test.plaintext) 36*e7b1675dSTing-Kang Chang aad, _ := hex.DecodeString(test.aad) 37*e7b1675dSTing-Kang Chang nonce, _ := hex.DecodeString(test.nonce) 38*e7b1675dSTing-Kang Chang out, _ := hex.DecodeString(test.out) 39*e7b1675dSTing-Kang Chang tag, _ := hex.DecodeString(test.tag) 40*e7b1675dSTing-Kang Chang 41*e7b1675dSTing-Kang Chang x, err := subtle.NewXChaCha20Poly1305(key) 42*e7b1675dSTing-Kang Chang if err != nil { 43*e7b1675dSTing-Kang Chang t.Errorf("#%d, cannot create new instance of XChaCha20Poly1305: %s", i, err) 44*e7b1675dSTing-Kang Chang continue 45*e7b1675dSTing-Kang Chang } 46*e7b1675dSTing-Kang Chang 47*e7b1675dSTing-Kang Chang _, err = x.Encrypt(pt, aad) 48*e7b1675dSTing-Kang Chang if err != nil { 49*e7b1675dSTing-Kang Chang t.Errorf("#%d, unexpected encryption error: %s", i, err) 50*e7b1675dSTing-Kang Chang continue 51*e7b1675dSTing-Kang Chang } 52*e7b1675dSTing-Kang Chang 53*e7b1675dSTing-Kang Chang var combinedCt []byte 54*e7b1675dSTing-Kang Chang combinedCt = append(combinedCt, nonce...) 55*e7b1675dSTing-Kang Chang combinedCt = append(combinedCt, out...) 56*e7b1675dSTing-Kang Chang combinedCt = append(combinedCt, tag...) 57*e7b1675dSTing-Kang Chang if got, err := x.Decrypt(combinedCt, aad); err != nil { 58*e7b1675dSTing-Kang Chang t.Errorf("#%d, unexpected decryption error: %s", i, err) 59*e7b1675dSTing-Kang Chang continue 60*e7b1675dSTing-Kang Chang } else if !bytes.Equal(pt, got) { 61*e7b1675dSTing-Kang Chang t.Errorf("#%d, plaintext's don't match: got %x vs %x", i, got, pt) 62*e7b1675dSTing-Kang Chang continue 63*e7b1675dSTing-Kang Chang } 64*e7b1675dSTing-Kang Chang } 65*e7b1675dSTing-Kang Chang} 66*e7b1675dSTing-Kang Chang 67*e7b1675dSTing-Kang Changfunc TestXChaCha20Poly1305EmptyAssociatedData(t *testing.T) { 68*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 69*e7b1675dSTing-Kang Chang aad := []byte{} 70*e7b1675dSTing-Kang Chang badAad := []byte{1, 2, 3} 71*e7b1675dSTing-Kang Chang 72*e7b1675dSTing-Kang Chang x, err := subtle.NewXChaCha20Poly1305(key) 73*e7b1675dSTing-Kang Chang if err != nil { 74*e7b1675dSTing-Kang Chang t.Fatal(err) 75*e7b1675dSTing-Kang Chang } 76*e7b1675dSTing-Kang Chang 77*e7b1675dSTing-Kang Chang for i := 0; i < 75; i++ { 78*e7b1675dSTing-Kang Chang pt := random.GetRandomBytes(uint32(i)) 79*e7b1675dSTing-Kang Chang // Encrpting with aad as a 0-length array 80*e7b1675dSTing-Kang Chang { 81*e7b1675dSTing-Kang Chang ct, err := x.Encrypt(pt, aad) 82*e7b1675dSTing-Kang Chang if err != nil { 83*e7b1675dSTing-Kang Chang t.Errorf("Encrypt(%x, %x) failed", pt, aad) 84*e7b1675dSTing-Kang Chang continue 85*e7b1675dSTing-Kang Chang } 86*e7b1675dSTing-Kang Chang 87*e7b1675dSTing-Kang Chang if got, err := x.Decrypt(ct, aad); err != nil || !bytes.Equal(pt, got) { 88*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x", aad, got, pt) 89*e7b1675dSTing-Kang Chang } 90*e7b1675dSTing-Kang Chang if got, err := x.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) { 91*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x", got, pt) 92*e7b1675dSTing-Kang Chang } 93*e7b1675dSTing-Kang Chang if _, err := x.Decrypt(ct, badAad); err == nil { 94*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAad) 95*e7b1675dSTing-Kang Chang } 96*e7b1675dSTing-Kang Chang } 97*e7b1675dSTing-Kang Chang // Encrpting with aad equal to null 98*e7b1675dSTing-Kang Chang { 99*e7b1675dSTing-Kang Chang ct, err := x.Encrypt(pt, nil) 100*e7b1675dSTing-Kang Chang if err != nil { 101*e7b1675dSTing-Kang Chang t.Errorf("Encrypt(%x, nil) failed", pt) 102*e7b1675dSTing-Kang Chang } 103*e7b1675dSTing-Kang Chang 104*e7b1675dSTing-Kang Chang if got, err := x.Decrypt(ct, aad); err != nil || !bytes.Equal(pt, got) { 105*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", aad, got, pt, err) 106*e7b1675dSTing-Kang Chang } 107*e7b1675dSTing-Kang Chang if got, err := x.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) { 108*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x; error: %v", got, pt, err) 109*e7b1675dSTing-Kang Chang } 110*e7b1675dSTing-Kang Chang if _, err := x.Decrypt(ct, badAad); err == nil { 111*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAad) 112*e7b1675dSTing-Kang Chang } 113*e7b1675dSTing-Kang Chang } 114*e7b1675dSTing-Kang Chang } 115*e7b1675dSTing-Kang Chang} 116*e7b1675dSTing-Kang Chang 117*e7b1675dSTing-Kang Changfunc TestXChaCha20Poly1305LongMessages(t *testing.T) { 118*e7b1675dSTing-Kang Chang dataSize := uint32(16) 119*e7b1675dSTing-Kang Chang // Encrypts and decrypts messages of size <= 8192. 120*e7b1675dSTing-Kang Chang for dataSize <= 1<<24 { 121*e7b1675dSTing-Kang Chang pt := random.GetRandomBytes(dataSize) 122*e7b1675dSTing-Kang Chang aad := random.GetRandomBytes(dataSize / 3) 123*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 124*e7b1675dSTing-Kang Chang 125*e7b1675dSTing-Kang Chang x, err := subtle.NewXChaCha20Poly1305(key) 126*e7b1675dSTing-Kang Chang if err != nil { 127*e7b1675dSTing-Kang Chang t.Fatal(err) 128*e7b1675dSTing-Kang Chang } 129*e7b1675dSTing-Kang Chang 130*e7b1675dSTing-Kang Chang ct, err := x.Encrypt(pt, aad) 131*e7b1675dSTing-Kang Chang if err != nil { 132*e7b1675dSTing-Kang Chang t.Errorf("Encrypt(%x, %x) failed", pt, aad) 133*e7b1675dSTing-Kang Chang continue 134*e7b1675dSTing-Kang Chang } 135*e7b1675dSTing-Kang Chang 136*e7b1675dSTing-Kang Chang if got, err := x.Decrypt(ct, aad); err != nil || !bytes.Equal(pt, got) { 137*e7b1675dSTing-Kang Chang t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", aad, got, pt, err) 138*e7b1675dSTing-Kang Chang } 139*e7b1675dSTing-Kang Chang 140*e7b1675dSTing-Kang Chang dataSize += 5 * dataSize / 11 141*e7b1675dSTing-Kang Chang } 142*e7b1675dSTing-Kang Chang} 143*e7b1675dSTing-Kang Chang 144*e7b1675dSTing-Kang Changfunc TestXChaCha20Poly1305ModifyCiphertext(t *testing.T) { 145*e7b1675dSTing-Kang Chang for i, test := range xChaCha20Poly1305Tests { 146*e7b1675dSTing-Kang Chang key, _ := hex.DecodeString(test.key) 147*e7b1675dSTing-Kang Chang pt, _ := hex.DecodeString(test.plaintext) 148*e7b1675dSTing-Kang Chang aad, _ := hex.DecodeString(test.aad) 149*e7b1675dSTing-Kang Chang 150*e7b1675dSTing-Kang Chang x, err := subtle.NewXChaCha20Poly1305(key) 151*e7b1675dSTing-Kang Chang if err != nil { 152*e7b1675dSTing-Kang Chang t.Fatal(err) 153*e7b1675dSTing-Kang Chang } 154*e7b1675dSTing-Kang Chang 155*e7b1675dSTing-Kang Chang ct, err := x.Encrypt(pt, aad) 156*e7b1675dSTing-Kang Chang if err != nil { 157*e7b1675dSTing-Kang Chang t.Errorf("#%d: Encrypt failed", i) 158*e7b1675dSTing-Kang Chang continue 159*e7b1675dSTing-Kang Chang } 160*e7b1675dSTing-Kang Chang 161*e7b1675dSTing-Kang Chang if len(aad) > 0 { 162*e7b1675dSTing-Kang Chang alterAadIdx := rand.Intn(len(aad)) 163*e7b1675dSTing-Kang Chang aad[alterAadIdx] ^= 0x80 164*e7b1675dSTing-Kang Chang if _, err := x.Decrypt(ct, aad); err == nil { 165*e7b1675dSTing-Kang Chang t.Errorf("#%d: Decrypt was successful after altering additional data", i) 166*e7b1675dSTing-Kang Chang continue 167*e7b1675dSTing-Kang Chang } 168*e7b1675dSTing-Kang Chang aad[alterAadIdx] ^= 0x80 169*e7b1675dSTing-Kang Chang } 170*e7b1675dSTing-Kang Chang 171*e7b1675dSTing-Kang Chang alterCtIdx := rand.Intn(len(ct)) 172*e7b1675dSTing-Kang Chang ct[alterCtIdx] ^= 0x80 173*e7b1675dSTing-Kang Chang if _, err := x.Decrypt(ct, aad); err == nil { 174*e7b1675dSTing-Kang Chang t.Errorf("#%d: Decrypt was successful after altering ciphertext", i) 175*e7b1675dSTing-Kang Chang continue 176*e7b1675dSTing-Kang Chang } 177*e7b1675dSTing-Kang Chang ct[alterCtIdx] ^= 0x80 178*e7b1675dSTing-Kang Chang } 179*e7b1675dSTing-Kang Chang} 180*e7b1675dSTing-Kang Chang 181*e7b1675dSTing-Kang Chang// This is a very simple test for the randomness of the nonce. 182*e7b1675dSTing-Kang Chang// The test simply checks that the multiple ciphertexts of the same message are distinct. 183*e7b1675dSTing-Kang Changfunc TestXChaCha20Poly1305RandomNonce(t *testing.T) { 184*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 185*e7b1675dSTing-Kang Chang x, err := subtle.NewXChaCha20Poly1305(key) 186*e7b1675dSTing-Kang Chang if err != nil { 187*e7b1675dSTing-Kang Chang t.Fatal(err) 188*e7b1675dSTing-Kang Chang } 189*e7b1675dSTing-Kang Chang 190*e7b1675dSTing-Kang Chang cts := make(map[string]bool) 191*e7b1675dSTing-Kang Chang pt, aad := []byte{}, []byte{} 192*e7b1675dSTing-Kang Chang for i := 0; i < 1<<10; i++ { 193*e7b1675dSTing-Kang Chang ct, err := x.Encrypt(pt, aad) 194*e7b1675dSTing-Kang Chang ctHex := hex.EncodeToString(ct) 195*e7b1675dSTing-Kang Chang if err != nil || cts[ctHex] { 196*e7b1675dSTing-Kang Chang t.Errorf("TestRandomNonce failed: %v", err) 197*e7b1675dSTing-Kang Chang } else { 198*e7b1675dSTing-Kang Chang cts[ctHex] = true 199*e7b1675dSTing-Kang Chang } 200*e7b1675dSTing-Kang Chang } 201*e7b1675dSTing-Kang Chang} 202*e7b1675dSTing-Kang Chang 203*e7b1675dSTing-Kang Changfunc TestXChaCha20Poly1305WycheproofCases(t *testing.T) { 204*e7b1675dSTing-Kang Chang testutil.SkipTestIfTestSrcDirIsNotSet(t) 205*e7b1675dSTing-Kang Chang suite := new(AEADSuite) 206*e7b1675dSTing-Kang Chang if err := testutil.PopulateSuite(suite, "xchacha20_poly1305_test.json"); err != nil { 207*e7b1675dSTing-Kang Chang t.Fatalf("failed populating suite: %s", err) 208*e7b1675dSTing-Kang Chang } 209*e7b1675dSTing-Kang Chang for _, group := range suite.TestGroups { 210*e7b1675dSTing-Kang Chang if group.KeySize/8 != chacha20poly1305.KeySize { 211*e7b1675dSTing-Kang Chang continue 212*e7b1675dSTing-Kang Chang } 213*e7b1675dSTing-Kang Chang if group.IvSize/8 != chacha20poly1305.NonceSizeX { 214*e7b1675dSTing-Kang Chang continue 215*e7b1675dSTing-Kang Chang } 216*e7b1675dSTing-Kang Chang for _, test := range group.Tests { 217*e7b1675dSTing-Kang Chang caseName := fmt.Sprintf("%s-%s:Case-%d", suite.Algorithm, group.Type, test.CaseID) 218*e7b1675dSTing-Kang Chang t.Run(caseName, func(t *testing.T) { runXChaCha20Poly1305WycheproofCase(t, test) }) 219*e7b1675dSTing-Kang Chang } 220*e7b1675dSTing-Kang Chang } 221*e7b1675dSTing-Kang Chang} 222*e7b1675dSTing-Kang Chang 223*e7b1675dSTing-Kang Changfunc runXChaCha20Poly1305WycheproofCase(t *testing.T, tc *AEADCase) { 224*e7b1675dSTing-Kang Chang var combinedCt []byte 225*e7b1675dSTing-Kang Chang combinedCt = append(combinedCt, tc.Iv...) 226*e7b1675dSTing-Kang Chang combinedCt = append(combinedCt, tc.Ct...) 227*e7b1675dSTing-Kang Chang combinedCt = append(combinedCt, tc.Tag...) 228*e7b1675dSTing-Kang Chang 229*e7b1675dSTing-Kang Chang ca, err := subtle.NewXChaCha20Poly1305(tc.Key) 230*e7b1675dSTing-Kang Chang if err != nil { 231*e7b1675dSTing-Kang Chang t.Fatalf("cannot create new instance of ChaCha20Poly1305: %s", err) 232*e7b1675dSTing-Kang Chang } 233*e7b1675dSTing-Kang Chang 234*e7b1675dSTing-Kang Chang _, err = ca.Encrypt(tc.Msg, tc.Aad) 235*e7b1675dSTing-Kang Chang if err != nil { 236*e7b1675dSTing-Kang Chang t.Fatalf("unexpected encryption error: %s", err) 237*e7b1675dSTing-Kang Chang } 238*e7b1675dSTing-Kang Chang 239*e7b1675dSTing-Kang Chang decrypted, err := ca.Decrypt(combinedCt, tc.Aad) 240*e7b1675dSTing-Kang Chang if err != nil { 241*e7b1675dSTing-Kang Chang if tc.Result == "valid" { 242*e7b1675dSTing-Kang Chang t.Errorf("unexpected error: %s", err) 243*e7b1675dSTing-Kang Chang } 244*e7b1675dSTing-Kang Chang } else { 245*e7b1675dSTing-Kang Chang if tc.Result == "invalid" { 246*e7b1675dSTing-Kang Chang t.Error("decrypted invalid") 247*e7b1675dSTing-Kang Chang } 248*e7b1675dSTing-Kang Chang if !bytes.Equal(decrypted, tc.Msg) { 249*e7b1675dSTing-Kang Chang t.Error("incorrect decryption") 250*e7b1675dSTing-Kang Chang } 251*e7b1675dSTing-Kang Chang } 252*e7b1675dSTing-Kang Chang} 253*e7b1675dSTing-Kang Chang 254*e7b1675dSTing-Kang Changfunc TestPreallocatedCiphertextMemoryInXChaCha20Poly1305IsExact(t *testing.T) { 255*e7b1675dSTing-Kang Chang key := random.GetRandomBytes(chacha20poly1305.KeySize) 256*e7b1675dSTing-Kang Chang a, err := subtle.NewXChaCha20Poly1305(key) 257*e7b1675dSTing-Kang Chang if err != nil { 258*e7b1675dSTing-Kang Chang t.Fatalf("aead.NewAESGCMInsecureIV() err = %v, want nil", err) 259*e7b1675dSTing-Kang Chang } 260*e7b1675dSTing-Kang Chang plaintext := random.GetRandomBytes(13) 261*e7b1675dSTing-Kang Chang associatedData := random.GetRandomBytes(17) 262*e7b1675dSTing-Kang Chang 263*e7b1675dSTing-Kang Chang ciphertext, err := a.Encrypt(plaintext, associatedData) 264*e7b1675dSTing-Kang Chang if err != nil { 265*e7b1675dSTing-Kang Chang t.Fatalf("a.Encrypt() err = %v, want nil", err) 266*e7b1675dSTing-Kang Chang } 267*e7b1675dSTing-Kang Chang // Encrypt() uses cipher.Overhead() to pre-allocate the memory needed store the ciphertext. 268*e7b1675dSTing-Kang Chang // For ChaCha20Poly1305, the size of the allocated memory should always be exact. If this check 269*e7b1675dSTing-Kang Chang // fails, the pre-allocated memory was too large or too small. If it was too small, the system had 270*e7b1675dSTing-Kang Chang // to re-allocate more memory, which is expensive and should be avoided. 271*e7b1675dSTing-Kang Chang if len(ciphertext) != cap(ciphertext) { 272*e7b1675dSTing-Kang Chang t.Errorf("want len(ciphertext) == cap(ciphertext), got %d != %d", len(ciphertext), cap(ciphertext)) 273*e7b1675dSTing-Kang Chang } 274*e7b1675dSTing-Kang Chang} 275