// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 v1

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/assert"
	"golang.org/x/crypto/nacl/box"

	containerv1 "github.com/elastic/harp/api/gen/go/harp/container/v1"
)

func Test_deriveSharedKeyFromRecipient(t *testing.T) {
	pk1, sk1, err := box.GenerateKey(bytes.NewReader([]byte("00001-deterministic-buffer-for-tests-26FBE7DED9E992BC36C06C988C1AC8A1E672B4B5959EF60672A983EFA7C8EE0F")))
	assert.NoError(t, err)
	assert.NotNil(t, pk1)
	assert.NotNil(t, sk1)

	pk2, sk2, err := box.GenerateKey(bytes.NewReader([]byte("00002-deterministic-buffer-for-tests-37ACB0DD3A3CE5A0960CCE0F6A0D7E663DFFD221FBE8EEB03B20D3AD91BCDD55")))
	assert.NoError(t, err)
	assert.NotNil(t, pk2)
	assert.NotNil(t, sk2)

	// ECDH(pk2, sk1)
	dk1 := deriveSharedKeyFromRecipient(pk2, sk1)
	assert.Equal(t, &[32]byte{
		0xd1, 0xc0, 0x22, 0x38, 0x31, 0x6d, 0x7d, 0x6b,
		0x57, 0x88, 0x72, 0x3d, 0xc2, 0x82, 0xd7, 0xe2,
		0xfa, 0x6, 0x43, 0xb0, 0x98, 0x6f, 0x8, 0xe6,
		0x28, 0xb0, 0x86, 0x73, 0x15, 0x2e, 0x6e, 0x5,
	}, dk1)

	// ECDH(pk1, sk2)
	dk2 := deriveSharedKeyFromRecipient(pk1, sk2)
	assert.NoError(t, err)

	// ECDH(pk1, sk2) == ECDH(pk2, sk1)
	assert.Equal(t, dk1, dk2)
}

func Test_keyIdentifierFromDerivedKey(t *testing.T) {
	dk := &[32]byte{
		0xd1, 0xc0, 0x22, 0x38, 0x31, 0x6d, 0x7d, 0x6b,
		0x57, 0x88, 0x72, 0x3d, 0xc2, 0x82, 0xd7, 0xe2,
		0xfa, 0x6, 0x43, 0xb0, 0x98, 0x6f, 0x8, 0xe6,
		0x28, 0xb0, 0x86, 0x73, 0x15, 0x2e, 0x6e, 0x5,
	}

	id, err := keyIdentifierFromDerivedKey(dk)
	assert.NoError(t, err)
	assert.Equal(t, []byte{
		0xdd, 0x3e, 0x93, 0x8a, 0x57, 0x74, 0xbd, 0xf5,
		0xed, 0xe, 0x9a, 0xae, 0x86, 0xd5, 0xd6, 0xf7,
		0xfc, 0xbb, 0x3d, 0xe0, 0x54, 0xa8, 0x18, 0x38,
		0x5e, 0xea, 0xa6, 0x46, 0xa9, 0xb0, 0xc8, 0x57,
	}, id)
}

func Test_packRecipient(t *testing.T) {
	payloadKey := &[32]byte{}

	pk1, sk1, err := box.GenerateKey(bytes.NewReader([]byte("00001-deterministic-buffer-for-tests-26FBE7DED9E992BC36C06C988C1AC8A1E672B4B5959EF60672A983EFA7C8EE0F")))
	assert.NoError(t, err)
	assert.NotNil(t, pk1)
	assert.NotNil(t, sk1)

	pk2, sk2, err := box.GenerateKey(bytes.NewReader([]byte("00002-deterministic-buffer-for-tests-37ACB0DD3A3CE5A0960CCE0F6A0D7E663DFFD221FBE8EEB03B20D3AD91BCDD55")))
	assert.NoError(t, err)
	assert.NotNil(t, pk2)
	assert.NotNil(t, sk2)

	recipient, err := packRecipient(bytes.NewReader([]byte("00003-deterministic-buffer-for-tests")), payloadKey, sk1, pk2)
	assert.NoError(t, err)
	assert.NotNil(t, recipient)
	assert.Equal(t, []byte{
		0xdd, 0x3e, 0x93, 0x8a, 0x57, 0x74, 0xbd, 0xf5,
		0xed, 0xe, 0x9a, 0xae, 0x86, 0xd5, 0xd6, 0xf7,
		0xfc, 0xbb, 0x3d, 0xe0, 0x54, 0xa8, 0x18, 0x38,
		0x5e, 0xea, 0xa6, 0x46, 0xa9, 0xb0, 0xc8, 0x57,
	}, recipient.Identifier)
	assert.Equal(t, []byte{
		0x30, 0x30, 0x30, 0x30, 0x33, 0x2d, 0x64, 0x65,
		0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x73,
		0x74, 0x69, 0x63, 0x2d, 0x62, 0x75, 0x66, 0x66,
		0xc7, 0xc3, 0x15, 0x7a, 0x5b, 0x1b, 0x47, 0x31,
		0x5b, 0xb7, 0xb4, 0x4d, 0xe5, 0x92, 0x32, 0xd6,
		0xbf, 0xe7, 0x52, 0x9a, 0xff, 0x84, 0xb7, 0xaf,
		0x5f, 0x7, 0x47, 0xca, 0x57, 0x8f, 0x4e, 0x3c,
		0x28, 0x3a, 0x66, 0xa0, 0x4b, 0xec, 0x12, 0x23,
		0x7e, 0xa4, 0x37, 0x47, 0xf5, 0x53, 0x2a, 0xbb,
	}, recipient.Key)
}

func Test_tryRecipientKeys(t *testing.T) {
	payloadKey := &[32]byte{}

	pk1, sk1, err := box.GenerateKey(bytes.NewReader([]byte("00001-deterministic-buffer-for-tests-26FBE7DED9E992BC36C06C988C1AC8A1E672B4B5959EF60672A983EFA7C8EE0F")))
	assert.NoError(t, err)
	assert.NotNil(t, pk1)
	assert.NotNil(t, sk1)

	pk2, sk2, err := box.GenerateKey(bytes.NewReader([]byte("00002-deterministic-buffer-for-tests-37ACB0DD3A3CE5A0960CCE0F6A0D7E663DFFD221FBE8EEB03B20D3AD91BCDD55")))
	assert.NoError(t, err)
	assert.NotNil(t, pk2)
	assert.NotNil(t, sk2)

	recipient, err := packRecipient(bytes.NewReader([]byte("00003-deterministic-buffer-for-tests")), payloadKey, sk1, pk2)
	assert.NoError(t, err)
	assert.NotNil(t, recipient)
	assert.Equal(t, []byte{
		0xdd, 0x3e, 0x93, 0x8a, 0x57, 0x74, 0xbd, 0xf5,
		0xed, 0xe, 0x9a, 0xae, 0x86, 0xd5, 0xd6, 0xf7,
		0xfc, 0xbb, 0x3d, 0xe0, 0x54, 0xa8, 0x18, 0x38,
		0x5e, 0xea, 0xa6, 0x46, 0xa9, 0xb0, 0xc8, 0x57,
	}, recipient.Identifier)
	assert.Equal(t, []byte{
		0x30, 0x30, 0x30, 0x30, 0x33, 0x2d, 0x64, 0x65,
		0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x73,
		0x74, 0x69, 0x63, 0x2d, 0x62, 0x75, 0x66, 0x66,
		0xc7, 0xc3, 0x15, 0x7a, 0x5b, 0x1b, 0x47, 0x31,
		0x5b, 0xb7, 0xb4, 0x4d, 0xe5, 0x92, 0x32, 0xd6,
		0xbf, 0xe7, 0x52, 0x9a, 0xff, 0x84, 0xb7, 0xaf,
		0x5f, 0x7, 0x47, 0xca, 0x57, 0x8f, 0x4e, 0x3c,
		0x28, 0x3a, 0x66, 0xa0, 0x4b, 0xec, 0x12, 0x23,
		0x7e, 0xa4, 0x37, 0x47, 0xf5, 0x53, 0x2a, 0xbb,
	}, recipient.Key)

	// -------------------------------------------------------------------------
	// ECDH(pk2, sk1)
	dk := deriveSharedKeyFromRecipient(pk2, sk1)
	assert.Equal(t, &[32]byte{
		0xd1, 0xc0, 0x22, 0x38, 0x31, 0x6d, 0x7d, 0x6b,
		0x57, 0x88, 0x72, 0x3d, 0xc2, 0x82, 0xd7, 0xe2,
		0xfa, 0x6, 0x43, 0xb0, 0x98, 0x6f, 0x8, 0xe6,
		0x28, 0xb0, 0x86, 0x73, 0x15, 0x2e, 0x6e, 0x5,
	}, dk)

	expectedID := []byte{
		0xdd, 0x3e, 0x93, 0x8a, 0x57, 0x74, 0xbd, 0xf5,
		0xed, 0xe, 0x9a, 0xae, 0x86, 0xd5, 0xd6, 0xf7,
		0xfc, 0xbb, 0x3d, 0xe0, 0x54, 0xa8, 0x18, 0x38,
		0x5e, 0xea, 0xa6, 0x46, 0xa9, 0xb0, 0xc8, 0x57,
	}
	id, err := keyIdentifierFromDerivedKey(dk)
	assert.NoError(t, err)
	assert.Equal(t, expectedID, id)
	assert.Equal(t, expectedID, recipient.Identifier)

	decodedPayloadKey, err := tryRecipientKeys(dk, []*containerv1.Recipient{
		recipient,
	})
	assert.NoError(t, err)
	assert.Equal(t, payloadKey[:], decodedPayloadKey)
}
