// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

// crypto package provides methods to encrypt and decrypt data
package crypto

import (
	"fmt"

	"github.com/aws/amazon-ssm-agent/agent/appconfig"
	"github.com/aws/amazon-ssm-agent/agent/context"
	"github.com/aws/amazon-ssm-agent/agent/sdkutil"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/kms/kmsiface"
)

// KMSKeySizeInBytes is the key size that is fetched from KMS. 64 bytes key is split into two halves.
// First half 32 bytes key is used by agent for encryption and second half 32 bytes by clients like cli/console
const KMSKeySizeInBytes int64 = 64

type IKMSService interface {
	Decrypt(cipherTextBlob []byte, encryptionContext map[string]*string, keyId string) (plainText []byte, err error)
}

type KMSService struct {
	client kmsiface.KMSAPI
}

// NewKMSService creates a new KMSService instance
func NewKMSService(context context.T) (kmsService *KMSService, err error) {
	var (
		awsConfig        *aws.Config
		appConfig        appconfig.SsmagentConfig
		kmsClientSession *session.Session
		agentName        string
		agentVersion     string
	)

	awsConfig = sdkutil.AwsConfig(context, "kms")

	appConfig = context.AppConfig()
	if appConfig.Kms.Endpoint != "" {
		awsConfig.Endpoint = &appConfig.Kms.Endpoint
	}
	agentName = appConfig.Agent.Name
	agentVersion = appConfig.Agent.Version
	if kmsClientSession, err = session.NewSession(awsConfig); err != nil {
		return nil, fmt.Errorf("Error creating new aws sdk session: %s", err)
	}
	kmsClientSession.Handlers.Build.PushBack(request.MakeAddToUserAgentHandler(agentName, agentVersion))
	kmsService = &KMSService{
		client: kms.New(kmsClientSession),
	}

	return kmsService, nil
}

// Decrypt will get the plaintext key from KMS service
func (kmsService *KMSService) Decrypt(cipherTextBlob []byte, encryptionContext map[string]*string, keyId string) (plainText []byte, err error) {
	output, err := kmsService.client.Decrypt(&kms.DecryptInput{
		CiphertextBlob:    cipherTextBlob,
		EncryptionContext: encryptionContext,
		KeyId:             &keyId,
	})
	if err != nil {
		return nil, fmt.Errorf("Error when decrypting data key %s", err)
	}
	return output.Plaintext, nil
}
