// 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 (
	"encoding/hex"
	"encoding/json"
	"math/big"
	"os"
	"path/filepath"
	"testing"

	"github.com/tink-crypto/tink-go/v2/testutil"
)

// TODO(b/201070904): Separate tests into internal_test package.

// aeadIDs are specified at
// https://www.rfc-editor.org/rfc/rfc9180.html#section-7.3.
var aeadIDs = []struct {
	name      string
	aeadID    uint16
	keyLength int
}{
	{"AES128GCM", aes128GCM, 16},
	{"AES256GCM", aes256GCM, 32},
	{"ChaCha20Poly1305", chaCha20Poly1305, 32},
}

type hpkeID struct {
	id     int
	mode   uint8
	kemID  uint16
	kdfID  uint16
	aeadID uint16
}

type vector struct {
	info                   []byte
	senderPubKey           []byte
	senderPrivKey          []byte
	recipientPubKey        []byte
	recipientPrivKey       []byte
	encapsulatedKey        []byte
	sharedSecret           []byte
	keyScheduleCtx         []byte
	secret                 []byte
	key                    []byte
	baseNonce              []byte
	consecutiveEncryptions []encryptionVector
	otherEncryptions       []encryptionVector
}

type encryptionVector struct {
	key            []byte
	plaintext      []byte
	associatedData []byte
	nonce          []byte
	ciphertext     []byte
	sequenceNumber *big.Int
}

type encryptionString struct {
	sequenceNumber uint64
	plaintext      string
	associatedData string
	nonce          string
	ciphertext     string
}

// TODO(b/201070904): Include all Tink-supported RFC vectors.
func internetDraftVector(t *testing.T) (hpkeID, vector) {
	t.Helper()

	// Test vector from HPKE RFC
	// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1.
	v := struct {
		mode                                                                                    uint8
		kemID, kdfID, aeadID                                                                    uint16
		info, pkEm, skEm, pkRm, skRm, enc, sharedSecret, keyScheduleCtx, secret, key, baseNonce string
		consecutiveEncryptions, otherEncryptions                                                []encryptionString
	}{
		mode:           0,
		kemID:          32,
		kdfID:          1,
		aeadID:         1,
		info:           "4f6465206f6e2061204772656369616e2055726e",
		pkEm:           "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431",
		skEm:           "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736",
		pkRm:           "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d",
		skRm:           "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8",
		enc:            "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431",
		sharedSecret:   "fe0e18c9f024ce43799ae393c7e8fe8fce9d218875e8227b0187c04e7d2ea1fc",
		keyScheduleCtx: "00725611c9d98c07c03f60095cd32d400d8347d45ed67097bbad50fc56da742d07cb6cffde367bb0565ba28bb02c90744a20f5ef37f30523526106f637abb05449",
		secret:         "12fff91991e93b48de37e7daddb52981084bd8aa64289c3788471d9a9712f397",
		key:            "4531685d41d65f03dc48f6b8302c05b0",
		baseNonce:      "56d890e5accaaf011cff4b7d",
		consecutiveEncryptions: []encryptionString{
			{
				sequenceNumber: 0,
				plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
				associatedData: "436f756e742d30",
				nonce:          "56d890e5accaaf011cff4b7d",
				ciphertext:     "f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a",
			},
			{
				sequenceNumber: 1,
				plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
				associatedData: "436f756e742d31",
				nonce:          "56d890e5accaaf011cff4b7c",
				ciphertext:     "af2d7e9ac9ae7e270f46ba1f975be53c09f8d875bdc8535458c2494e8a6eab251c03d0c22a56b8ca42c2063b84",
			},
			{
				sequenceNumber: 2,
				plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
				associatedData: "436f756e742d32",
				nonce:          "56d890e5accaaf011cff4b7f",
				ciphertext:     "498dfcabd92e8acedc281e85af1cb4e3e31c7dc394a1ca20e173cb72516491588d96a19ad4a683518973dcc180",
			},
		},
		otherEncryptions: []encryptionString{
			{
				sequenceNumber: 4,
				plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
				associatedData: "436f756e742d34",
				nonce:          "56d890e5accaaf011cff4b79",
				ciphertext:     "583bd32bc67a5994bb8ceaca813d369bca7b2a42408cddef5e22f880b631215a09fc0012bc69fccaa251c0246d",
			},
			{
				sequenceNumber: 255,
				plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
				associatedData: "436f756e742d323535",
				nonce:          "56d890e5accaaf011cff4b82",
				ciphertext:     "7175db9717964058640a3a11fb9007941a5d1757fda1a6935c805c21af32505bf106deefec4a49ac38d71c9e0a",
			},
			{
				sequenceNumber: 256,
				plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
				associatedData: "436f756e742d323536",
				nonce:          "56d890e5accaaf011cff4a7d",
				ciphertext:     "957f9800542b0b8891badb026d79cc54597cb2d225b54c00c5238c25d05c30e3fbeda97d2e0e1aba483a2df9f2",
			},
		},
	}

	var info, senderPubKey, senderPrivKey, recipientPubKey, recipientPrivKey, encapsulatedKey, sharedSecret, keyScheduleCtx, secret, key, baseNonce []byte
	var err error
	if info, err = hex.DecodeString(v.info); err != nil {
		t.Fatalf("hex.DecodeString(info): err %q", err)
	}
	if senderPubKey, err = hex.DecodeString(v.pkEm); err != nil {
		t.Fatalf("hex.DecodeString(pkEm): err %q", err)
	}
	if senderPrivKey, err = hex.DecodeString(v.skEm); err != nil {
		t.Fatalf("hex.DecodeString(skEm): err %q", err)
	}
	if recipientPubKey, err = hex.DecodeString(v.pkRm); err != nil {
		t.Fatalf("hex.DecodeString(pkRm): err %q", err)
	}
	if recipientPrivKey, err = hex.DecodeString(v.skRm); err != nil {
		t.Fatalf("hex.DecodeString(skRm): err %q", err)
	}
	if encapsulatedKey, err = hex.DecodeString(v.enc); err != nil {
		t.Fatalf("hex.DecodeString(enc): err %q", err)
	}
	if sharedSecret, err = hex.DecodeString(v.sharedSecret); err != nil {
		t.Fatalf("hex.DecodeString(sharedSecret): err %q", err)
	}
	if keyScheduleCtx, err = hex.DecodeString(v.keyScheduleCtx); err != nil {
		t.Fatalf("hex.DecodeString(keyScheduleCtx): err %q", err)
	}
	if secret, err = hex.DecodeString(v.secret); err != nil {
		t.Fatalf("hex.DecodeString(secret): err %q", err)
	}
	if key, err = hex.DecodeString(v.key); err != nil {
		t.Fatalf("hex.DecodeString(key): err %q", err)
	}
	if baseNonce, err = hex.DecodeString(v.baseNonce); err != nil {
		t.Fatalf("hex.DecodeString(baseNonce): err %q", err)
	}

	return hpkeID{0 /*=id */, v.mode, v.kemID, v.kdfID, v.aeadID},
		vector{
			info:                   info,
			senderPubKey:           senderPubKey,
			senderPrivKey:          senderPrivKey,
			recipientPubKey:        recipientPubKey,
			recipientPrivKey:       recipientPrivKey,
			encapsulatedKey:        encapsulatedKey,
			sharedSecret:           sharedSecret,
			keyScheduleCtx:         keyScheduleCtx,
			secret:                 secret,
			key:                    key,
			baseNonce:              baseNonce,
			consecutiveEncryptions: parseEncryptions(t, v.consecutiveEncryptions),
			otherEncryptions:       parseEncryptions(t, v.otherEncryptions),
		}
}

func parseEncryptions(t *testing.T, encs []encryptionString) []encryptionVector {
	t.Helper()

	var res []encryptionVector
	for _, e := range encs {
		var plaintext, associatedData, nonce, ciphertext []byte
		var err error
		if plaintext, err = hex.DecodeString(e.plaintext); err != nil {
			t.Fatalf("hex.DecodeString(plaintext): err %q", err)
		}
		if associatedData, err = hex.DecodeString(e.associatedData); err != nil {
			t.Fatalf("hex.DecodeString(associatedData): err %q", err)
		}
		if nonce, err = hex.DecodeString(e.nonce); err != nil {
			t.Fatalf("hex.DecodeString(nonce): err %q", err)
		}
		if ciphertext, err = hex.DecodeString(e.ciphertext); err != nil {
			t.Fatalf("hex.DecodeString(ciphertext): err %q", err)
		}

		res = append(res, encryptionVector{
			plaintext:      plaintext,
			associatedData: associatedData,
			nonce:          nonce,
			ciphertext:     ciphertext,
			sequenceNumber: big.NewInt(int64(e.sequenceNumber)),
		})
	}

	return res
}

// aeadRFCVectors returns RFC test vectors for AEAD IDs aes128GCM, aes256GCM,
// and chaCha20Poly1305.
func aeadRFCVectors(t *testing.T) map[hpkeID]encryptionVector {
	t.Helper()

	vecs := []struct {
		mode                                              uint8
		kemID, kdfID, aeadID                              uint16
		key, plaintext, associatedData, nonce, ciphertext string
	}{
		// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1.1
		{
			mode:           0,
			kemID:          32,
			kdfID:          1,
			aeadID:         1,
			key:            "4531685d41d65f03dc48f6b8302c05b0",
			plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
			associatedData: "436f756e742d30",
			nonce:          "56d890e5accaaf011cff4b7d",
			ciphertext:     "f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a",
		},
		{
			mode:           0,
			kemID:          32,
			kdfID:          1,
			aeadID:         1,
			key:            "4531685d41d65f03dc48f6b8302c05b0",
			plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
			associatedData: "436f756e742d31",
			nonce:          "56d890e5accaaf011cff4b7c",
			ciphertext:     "af2d7e9ac9ae7e270f46ba1f975be53c09f8d875bdc8535458c2494e8a6eab251c03d0c22a56b8ca42c2063b84",
		},
		// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.6.1.1
		{
			mode:           0,
			kemID:          18,
			kdfID:          3,
			aeadID:         2,
			key:            "751e346ce8f0ddb2305c8a2a85c70d5cf559c53093656be636b9406d4d7d1b70",
			plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
			associatedData: "436f756e742d30",
			nonce:          "55ff7a7d739c69f44b25447b",
			ciphertext:     "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a",
		},
		{
			mode:           0,
			kemID:          18,
			kdfID:          3,
			aeadID:         2,
			key:            "751e346ce8f0ddb2305c8a2a85c70d5cf559c53093656be636b9406d4d7d1b70",
			plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
			associatedData: "436f756e742d31",
			nonce:          "55ff7a7d739c69f44b25447a",
			ciphertext:     "d9ee248e220ca24ac00bbbe7e221a832e4f7fa64c4fbab3945b6f3af0c5ecd5e16815b328be4954a05fd352256",
		},
		// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.2.1.1
		{
			mode:           0,
			kemID:          32,
			kdfID:          1,
			aeadID:         3,
			key:            "ad2744de8e17f4ebba575b3f5f5a8fa1f69c2a07f6e7500bc60ca6e3e3ec1c91",
			plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
			associatedData: "436f756e742d30",
			nonce:          "5c4d98150661b848853b547f",
			ciphertext:     "1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28",
		},
		{
			mode:           0,
			kemID:          32,
			kdfID:          1,
			aeadID:         3,
			key:            "ad2744de8e17f4ebba575b3f5f5a8fa1f69c2a07f6e7500bc60ca6e3e3ec1c91",
			plaintext:      "4265617574792069732074727574682c20747275746820626561757479",
			associatedData: "436f756e742d31",
			nonce:          "5c4d98150661b848853b547e",
			ciphertext:     "6b53c051e4199c518de79594e1c4ab18b96f081549d45ce015be002090bb119e85285337cc95ba5f59992dc98c",
		},
	}

	m := make(map[hpkeID]encryptionVector)
	for i, v := range vecs {
		var key, plaintext, associatedData, nonce, ciphertext []byte
		var err error
		if key, err = hex.DecodeString(v.key); err != nil {
			t.Fatalf("hex.DecodeString(key): err %q", err)
		}
		if plaintext, err = hex.DecodeString(v.plaintext); err != nil {
			t.Fatalf("hex.DecodeString(plaintext): err %q", err)
		}
		if associatedData, err = hex.DecodeString(v.associatedData); err != nil {
			t.Fatalf("hex.DecodeString(associatedData): err %q", err)
		}
		if nonce, err = hex.DecodeString(v.nonce); err != nil {
			t.Fatalf("hex.DecodeString(nonce): err %q", err)
		}
		if ciphertext, err = hex.DecodeString(v.ciphertext); err != nil {
			t.Fatalf("hex.DecodeString(ciphertext): err %q", err)
		}

		id := hpkeID{i, v.mode, v.kemID, v.kdfID, v.aeadID}
		m[id] = encryptionVector{
			key:            key,
			plaintext:      plaintext,
			associatedData: associatedData,
			nonce:          nonce,
			ciphertext:     ciphertext,
		}
	}

	return m
}

// baseModeX25519HKDFSHA256Vectors returns BoringSSL test vectors for HPKE base
// mode with Diffie-Hellman-based X25519, HKDF-SHA256 KEM as per
// https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.
func baseModeX25519HKDFSHA256Vectors(t *testing.T) map[hpkeID]vector {
	testutil.SkipTestIfTestSrcDirIsNotSet(t)
	t.Helper()

	srcDir, _ := os.LookupEnv("TEST_SRCDIR")
	path := filepath.Join(srcDir, os.Getenv("TEST_WORKSPACE"), "/testdata/testvectors/hpke_boringssl.json")
	f, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}

	var vecs []struct {
		Mode             uint8             `json:"mode"`
		KEMID            uint16            `json:"kem_id"`
		KDFID            uint16            `json:"kdf_id"`
		AEADID           uint16            `json:"aead_id"`
		Info             testutil.HexBytes `json:"info"`
		SenderPubKey     testutil.HexBytes `json:"pkEm"`
		SenderPrivKey    testutil.HexBytes `json:"skEm"`
		RecipientPubKey  testutil.HexBytes `json:"pkRm"`
		RecipientPrivKey testutil.HexBytes `json:"skRm"`
		EncapsulatedKey  testutil.HexBytes `json:"enc"`
		SharedSecret     testutil.HexBytes `json:"shared_secret"`
		KeyScheduleCtx   testutil.HexBytes `json:"key_schedule_context"`
		Secret           testutil.HexBytes `json:"secret"`
		Key              testutil.HexBytes `json:"key"`
		BaseNonce        testutil.HexBytes `json:"base_nonce"`
	}
	parser := json.NewDecoder(f)
	if err := parser.Decode(&vecs); err != nil {
		t.Fatal(err)
	}

	m := make(map[hpkeID]vector)
	for i, v := range vecs {
		if v.Mode != baseMode || v.KEMID != x25519HKDFSHA256 {
			continue
		}

		id := hpkeID{i, v.Mode, v.KEMID, v.KDFID, v.AEADID}
		m[id] = vector{
			info:             v.Info,
			senderPubKey:     v.SenderPubKey,
			senderPrivKey:    v.SenderPrivKey,
			recipientPubKey:  v.RecipientPubKey,
			recipientPrivKey: v.RecipientPrivKey,
			encapsulatedKey:  v.EncapsulatedKey,
			sharedSecret:     v.SharedSecret,
			keyScheduleCtx:   v.KeyScheduleCtx,
			secret:           v.Secret,
			key:              v.Key,
			baseNonce:        v.BaseNonce,
		}
	}

	return m
}

func TestHpkeSuiteIDMemoryAllocatedIsExact(t *testing.T) {
	suiteID := hpkeSuiteID(1, 2, 3)
	if len(suiteID) != cap(suiteID) {
		t.Errorf("want len(suiteID) == cap(suiteID), got %d != %d", len(suiteID), cap(suiteID))
	}
}

func TestKeyScheduleContextMemoryAllocatedIsExact(t *testing.T) {
	context := keyScheduleContext(1, []byte{1, 2, 3}, []byte{1, 2, 3, 4, 5})
	if len(context) != cap(context) {
		t.Errorf("want len(context) == cap(context), got %d != %d", len(context), cap(context))
	}
}

func TestLabelIKMMemoryAllocatedIsExact(t *testing.T) {
	ikm := labelIKM("abcde", []byte{1, 2, 3}, []byte{1, 2, 3, 4, 5})
	if len(ikm) != cap(ikm) {
		t.Errorf("want len(ikm) == cap(ikm), got %d != %d", len(ikm), cap(ikm))
	}
}

func TestLabelInfoMemoryAllocatedIsExact(t *testing.T) {
	info, err := labelInfo("abcde", []byte{1, 2, 3}, []byte{1, 2, 3, 4, 5}, 42)
	if err != nil {
		t.Errorf("labelInfo() err = %v, want nil", err)
	}
	if len(info) != cap(info) {
		t.Errorf("want len(info) == cap(info), got %d != %d", len(info), cap(info))
	}
}
