// 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/tink-crypto/tink-go/v2/subtle"
	pb "github.com/tink-crypto/tink-go/v2/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)
		}
	}
}
