| // Copyright 2022 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 hpke |
| |
| import ( |
| "bytes" |
| "math/big" |
| "testing" |
| |
| "github.com/google/tink/go/subtle" |
| pb "github.com/google/tink/go/proto/hpke_go_proto" |
| ) |
| |
| // TODO(b/201070904): Write tests using baseModeX25519HKDFSHA256Vectors. |
| func TestContextSender(t *testing.T) { |
| id, vec := internetDraftVector(t) |
| kem, err := newKEM(id.kemID) |
| if err != nil { |
| t.Fatalf("newKEM(%d): err %q", id.kemID, err) |
| } |
| x25519KEMGeneratePrivateKey = func() ([]byte, error) { |
| return vec.senderPrivKey, nil |
| } |
| kdf, err := newKDF(id.kdfID) |
| if err != nil { |
| t.Fatalf("newKDF(%d): err %q", id.kdfID, err) |
| } |
| aead, err := newAEAD(id.aeadID) |
| if err != nil { |
| t.Fatalf("newAEAD(%d): err %q", id.aeadID, err) |
| } |
| |
| recipientPubKey := &pb.HpkePublicKey{PublicKey: vec.recipientPubKey} |
| senderCtx, err := newSenderContext(recipientPubKey, kem, kdf, aead, vec.info) |
| if err != nil { |
| t.Fatalf("newSenderContext: err %q", err) |
| } |
| |
| for _, enc := range vec.consecutiveEncryptions { |
| if got, want := senderCtx.sequenceNumber, enc.sequenceNumber; got.Cmp(want) != 0 { |
| t.Fatalf("sequence number: got %s, want %s", got.String(), want.String()) |
| } |
| ct, err := senderCtx.seal(enc.plaintext, enc.associatedData) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(ct, enc.ciphertext) { |
| t.Errorf("ciphertext: got %x, want %x", ct, enc.ciphertext) |
| } |
| } |
| |
| for _, enc := range vec.otherEncryptions { |
| senderCtx.sequenceNumber.Set(enc.sequenceNumber) |
| ct, err := senderCtx.seal(enc.plaintext, enc.associatedData) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(ct, enc.ciphertext) { |
| t.Errorf("ciphertext: got %x, want %x", ct, enc.ciphertext) |
| } |
| } |
| |
| x25519KEMGeneratePrivateKey = subtle.GeneratePrivateKeyX25519 |
| } |
| |
| func TestContextRecipient(t *testing.T) { |
| id, vec := internetDraftVector(t) |
| kem, err := newKEM(id.kemID) |
| if err != nil { |
| t.Fatalf("newKEM(%d): err %q", id.kemID, err) |
| } |
| kdf, err := newKDF(id.kdfID) |
| if err != nil { |
| t.Fatalf("newKDF(%d): err %q", id.kdfID, err) |
| } |
| aead, err := newAEAD(id.aeadID) |
| if err != nil { |
| t.Fatalf("newAEAD(%d): err %q", id.aeadID, err) |
| } |
| |
| recipientPrivKey := &pb.HpkePrivateKey{PrivateKey: vec.recipientPrivKey} |
| recipientCtx, err := newRecipientContext(vec.encapsulatedKey, recipientPrivKey, kem, kdf, aead, vec.info) |
| if err != nil { |
| t.Fatalf("newRecipientContext: err %q", err) |
| } |
| |
| for _, enc := range vec.consecutiveEncryptions { |
| if got, want := recipientCtx.sequenceNumber, enc.sequenceNumber; got.Cmp(want) != 0 { |
| t.Fatalf("sequence number: got %s, want %s", got.String(), want.String()) |
| } |
| pt, err := recipientCtx.open(enc.ciphertext, enc.associatedData) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(pt, enc.plaintext) { |
| t.Errorf("plaintext: got %x, want %x", pt, enc.plaintext) |
| } |
| } |
| |
| for _, enc := range vec.otherEncryptions { |
| recipientCtx.sequenceNumber.Set(enc.sequenceNumber) |
| pt, err := recipientCtx.open(enc.ciphertext, enc.associatedData) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(pt, enc.plaintext) { |
| t.Errorf("plaintext: got %x, want %x", pt, enc.plaintext) |
| } |
| } |
| } |
| |
| func TestContextMaxSequenceNumber(t *testing.T) { |
| got := maxSequenceNumber(12 /*=AESGCMIVSize*/) |
| want, ok := new(big.Int).SetString("79228162514264337593543950335", 10) // (1 << (8*12)) - 1 |
| if !ok { |
| t.Fatalf("SetString(\"79228162514264337593543950335\", 10): got err, want success") |
| } |
| if got.Cmp(want) != 0 { |
| t.Errorf("maxSequenceNumber(12): got %s, want %s", got.String(), want.String()) |
| } |
| } |
| |
| func TestComputeNonce(t *testing.T) { |
| id, vec := internetDraftVector(t) |
| kem, err := newKEM(id.kemID) |
| if err != nil { |
| t.Fatalf("newKEM(%d): err %q", id.kemID, err) |
| } |
| kdf, err := newKDF(id.kdfID) |
| if err != nil { |
| t.Fatalf("newKDF(%d): err %q", id.kdfID, err) |
| } |
| aead, err := newAEAD(id.aeadID) |
| if err != nil { |
| t.Fatalf("newAEAD(%d): err %q", id.aeadID, err) |
| } |
| |
| recipientPrivKey := &pb.HpkePrivateKey{PrivateKey: vec.recipientPrivKey} |
| ctx, err := newRecipientContext(vec.encapsulatedKey, recipientPrivKey, kem, kdf, aead, vec.info) |
| if err != nil { |
| t.Fatalf("newRecipientContext: err %q", err) |
| } |
| |
| if !bytes.Equal(ctx.baseNonce, vec.baseNonce) { |
| t.Fatalf("base nonce: got %x, want %x", ctx.baseNonce, vec.baseNonce) |
| } |
| |
| for _, enc := range vec.consecutiveEncryptions { |
| nonce, err := ctx.computeNonce() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(nonce, enc.nonce) { |
| t.Errorf("computeNonce: got %x, want %x", nonce, enc.nonce) |
| } |
| if err := ctx.incrementSequenceNumber(); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| for _, enc := range vec.otherEncryptions { |
| ctx.sequenceNumber.Set(enc.sequenceNumber) |
| nonce, err := ctx.computeNonce() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(nonce, enc.nonce) { |
| t.Errorf("computeNonce: got %x, want %x", nonce, enc.nonce) |
| } |
| if err := ctx.incrementSequenceNumber(); err != nil { |
| t.Fatal(err) |
| } |
| } |
| } |