| /* |
| * |
| * Copyright 2018 gRPC authors. |
| * |
| * 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 conn |
| |
| import ( |
| "bytes" |
| "testing" |
| |
| core "google.golang.org/grpc/credentials/alts/internal" |
| ) |
| |
| // cryptoTestVector is struct for a GCM test vector |
| type cryptoTestVector struct { |
| key, counter, plaintext, ciphertext, tag []byte |
| allocateDst bool |
| } |
| |
| // getGCMCryptoPair outputs a client/server pair on aes128gcm. |
| func getGCMCryptoPair(key []byte, counter []byte, t *testing.T) (ALTSRecordCrypto, ALTSRecordCrypto) { |
| client, err := NewAES128GCM(core.ClientSide, key) |
| if err != nil { |
| t.Fatalf("NewAES128GCM(ClientSide, key) = %v", err) |
| } |
| server, err := NewAES128GCM(core.ServerSide, key) |
| if err != nil { |
| t.Fatalf("NewAES128GCM(ServerSide, key) = %v", err) |
| } |
| // set counter if provided. |
| if counter != nil { |
| if CounterSide(counter) == core.ClientSide { |
| client.(*aes128gcm).outCounter = CounterFromValue(counter, overflowLenAES128GCM) |
| server.(*aes128gcm).inCounter = CounterFromValue(counter, overflowLenAES128GCM) |
| } else { |
| server.(*aes128gcm).outCounter = CounterFromValue(counter, overflowLenAES128GCM) |
| client.(*aes128gcm).inCounter = CounterFromValue(counter, overflowLenAES128GCM) |
| } |
| } |
| return client, server |
| } |
| |
| func testGCMEncryptionDecryption(sender ALTSRecordCrypto, receiver ALTSRecordCrypto, test *cryptoTestVector, withCounter bool, t *testing.T) { |
| // Ciphertext is: counter + encrypted text + tag. |
| ciphertext := []byte(nil) |
| if withCounter { |
| ciphertext = append(ciphertext, test.counter...) |
| } |
| ciphertext = append(ciphertext, test.ciphertext...) |
| ciphertext = append(ciphertext, test.tag...) |
| |
| // Decrypt. |
| if got, err := receiver.Decrypt(nil, ciphertext); err != nil || !bytes.Equal(got, test.plaintext) { |
| t.Errorf("key=%v\ncounter=%v\ntag=%v\nciphertext=%v\nDecrypt = %v, %v\nwant: %v", |
| test.key, test.counter, test.tag, test.ciphertext, got, err, test.plaintext) |
| } |
| |
| // Encrypt. |
| var dst []byte |
| if test.allocateDst { |
| dst = make([]byte, len(test.plaintext)+sender.EncryptionOverhead()) |
| } |
| if got, err := sender.Encrypt(dst[:0], test.plaintext); err != nil || !bytes.Equal(got, ciphertext) { |
| t.Errorf("key=%v\ncounter=%v\nplaintext=%v\nEncrypt = %v, %v\nwant: %v", |
| test.key, test.counter, test.plaintext, got, err, ciphertext) |
| } |
| } |
| |
| // Test encrypt and decrypt using test vectors for aes128gcm. |
| func (s) TestAES128GCMEncrypt(t *testing.T) { |
| for _, test := range []cryptoTestVector{ |
| { |
| key: dehex("11754cd72aec309bf52f7687212e8957"), |
| counter: dehex("3c819d9a9bed087615030b65"), |
| plaintext: nil, |
| ciphertext: nil, |
| tag: dehex("250327c674aaf477aef2675748cf6971"), |
| allocateDst: false, |
| }, |
| { |
| key: dehex("ca47248ac0b6f8372a97ac43508308ed"), |
| counter: dehex("ffd2b598feabc9019262d2be"), |
| plaintext: nil, |
| ciphertext: nil, |
| tag: dehex("60d20404af527d248d893ae495707d1a"), |
| allocateDst: false, |
| }, |
| { |
| key: dehex("7fddb57453c241d03efbed3ac44e371c"), |
| counter: dehex("ee283a3fc75575e33efd4887"), |
| plaintext: dehex("d5de42b461646c255c87bd2962d3b9a2"), |
| ciphertext: dehex("2ccda4a5415cb91e135c2a0f78c9b2fd"), |
| tag: dehex("b36d1df9b9d5e596f83e8b7f52971cb3"), |
| allocateDst: false, |
| }, |
| { |
| key: dehex("ab72c77b97cb5fe9a382d9fe81ffdbed"), |
| counter: dehex("54cc7dc2c37ec006bcc6d1da"), |
| plaintext: dehex("007c5e5b3e59df24a7c355584fc1518d"), |
| ciphertext: dehex("0e1bde206a07a9c2c1b65300f8c64997"), |
| tag: dehex("2b4401346697138c7a4891ee59867d0c"), |
| allocateDst: false, |
| }, |
| { |
| key: dehex("11754cd72aec309bf52f7687212e8957"), |
| counter: dehex("3c819d9a9bed087615030b65"), |
| plaintext: nil, |
| ciphertext: nil, |
| tag: dehex("250327c674aaf477aef2675748cf6971"), |
| allocateDst: true, |
| }, |
| { |
| key: dehex("ca47248ac0b6f8372a97ac43508308ed"), |
| counter: dehex("ffd2b598feabc9019262d2be"), |
| plaintext: nil, |
| ciphertext: nil, |
| tag: dehex("60d20404af527d248d893ae495707d1a"), |
| allocateDst: true, |
| }, |
| { |
| key: dehex("7fddb57453c241d03efbed3ac44e371c"), |
| counter: dehex("ee283a3fc75575e33efd4887"), |
| plaintext: dehex("d5de42b461646c255c87bd2962d3b9a2"), |
| ciphertext: dehex("2ccda4a5415cb91e135c2a0f78c9b2fd"), |
| tag: dehex("b36d1df9b9d5e596f83e8b7f52971cb3"), |
| allocateDst: true, |
| }, |
| { |
| key: dehex("ab72c77b97cb5fe9a382d9fe81ffdbed"), |
| counter: dehex("54cc7dc2c37ec006bcc6d1da"), |
| plaintext: dehex("007c5e5b3e59df24a7c355584fc1518d"), |
| ciphertext: dehex("0e1bde206a07a9c2c1b65300f8c64997"), |
| tag: dehex("2b4401346697138c7a4891ee59867d0c"), |
| allocateDst: true, |
| }, |
| } { |
| // Test encryption and decryption for aes128gcm. |
| client, server := getGCMCryptoPair(test.key, test.counter, t) |
| if CounterSide(test.counter) == core.ClientSide { |
| testGCMEncryptionDecryption(client, server, &test, false, t) |
| } else { |
| testGCMEncryptionDecryption(server, client, &test, false, t) |
| } |
| } |
| } |
| |
| func testGCMEncryptRoundtrip(client ALTSRecordCrypto, server ALTSRecordCrypto, t *testing.T) { |
| // Encrypt. |
| const plaintext = "This is plaintext." |
| var err error |
| buf := []byte(plaintext) |
| buf, err = client.Encrypt(buf[:0], buf) |
| if err != nil { |
| t.Fatal("Encrypting with client-side context: unexpected error", err, "\n", |
| "Plaintext:", []byte(plaintext)) |
| } |
| |
| // Encrypt a second message. |
| const plaintext2 = "This is a second plaintext." |
| buf2 := []byte(plaintext2) |
| buf2, err = client.Encrypt(buf2[:0], buf2) |
| if err != nil { |
| t.Fatal("Encrypting with client-side context: unexpected error", err, "\n", |
| "Plaintext:", []byte(plaintext2)) |
| } |
| |
| // Decryption fails: cannot decrypt second message before first. |
| if got, err := server.Decrypt(nil, buf2); err == nil { |
| t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want unexpected counter error:\n", |
| " Original plaintext:", []byte(plaintext2), "\n", |
| " Ciphertext:", buf2, "\n", |
| " Decrypted plaintext:", got) |
| } |
| |
| // Decryption fails: wrong counter space. |
| if got, err := client.Decrypt(nil, buf); err == nil { |
| t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want counter space error:\n", |
| " Original plaintext:", []byte(plaintext), "\n", |
| " Ciphertext:", buf, "\n", |
| " Decrypted plaintext:", got) |
| } |
| |
| // Decrypt first message. |
| ciphertext := append([]byte(nil), buf...) |
| buf, err = server.Decrypt(buf[:0], buf) |
| if err != nil || string(buf) != plaintext { |
| t.Fatal("Decrypting client-side ciphertext with a server-side context did not produce original content:\n", |
| " Original plaintext:", []byte(plaintext), "\n", |
| " Ciphertext:", ciphertext, "\n", |
| " Decryption error:", err, "\n", |
| " Decrypted plaintext:", buf) |
| } |
| |
| // Decryption fails: replay attack. |
| if got, err := server.Decrypt(nil, buf); err == nil { |
| t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want unexpected counter error:\n", |
| " Original plaintext:", []byte(plaintext), "\n", |
| " Ciphertext:", buf, "\n", |
| " Decrypted plaintext:", got) |
| } |
| } |
| |
| // Test encrypt and decrypt on roundtrip messages for aes128gcm. |
| func (s) TestAES128GCMEncryptRoundtrip(t *testing.T) { |
| // Test for aes128gcm. |
| key := make([]byte, 16) |
| client, server := getGCMCryptoPair(key, nil, t) |
| testGCMEncryptRoundtrip(client, server, t) |
| } |