// Copyright 2022-2024 The NATS Authors
// 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 nkeys

import (
	
	
	
	
	

	
	
)

// This package will support safe use of X25519 keys for asymmetric encryption.
// We will be compatible with nacl.Box, but generate random nonces automatically.
// We may add more advanced options in the future for group recipients and better
// end to end algorithms.

const (
	curveKeyLen    = 32
	curveDecodeLen = 35
	curveNonceLen  = 24
)

type ckp struct {
	seed [curveKeyLen]byte // Private raw key.
}

// CreateCurveKeys will create a Curve typed KeyPair.
func () (KeyPair, error) {
	return CreateCurveKeysWithRand(nil)
}

// CreateCurveKeysWithRand will create a Curve typed KeyPair
// with specified rand source.
func ( io.Reader) (KeyPair, error) {
	var  ckp
	, ,  := ed25519.GenerateKey()
	if  != nil {
		return nil, 
	}
	.seed = [curveKeyLen]byte(.Seed())
	return &, nil
}

// Will create a curve key pair from seed.
func ( []byte) (KeyPair, error) {
	, ,  := DecodeSeed()
	if  != nil {
		return nil, 
	}
	if  != PrefixByteCurve || len() != curveKeyLen {
		return nil, ErrInvalidCurveSeed
	}
	var  ckp
	copy(.seed[:], )
	return &, nil
}

// Seed will return the encoded seed.
func ( *ckp) () ([]byte, error) {
	return EncodeSeed(PrefixByteCurve, .seed[:])
}

// PublicKey will return the encoded public key.
func ( *ckp) () (string, error) {
	var  [curveKeyLen]byte
	curve25519.ScalarBaseMult(&, &.seed)
	,  := Encode(PrefixByteCurve, [:])
	return string(), 
}

// PrivateKey will return the encoded private key.
func ( *ckp) () ([]byte, error) {
	return Encode(PrefixBytePrivate, .seed[:])
}

func decodePubCurveKey( string,  []byte) error {
	var  [curveDecodeLen]byte // should always be 35
	,  := b32Enc.Decode([:], []byte())
	if  != nil {
		return 
	}
	if  != curveDecodeLen {
		return ErrInvalidCurveKey
	}
	// Make sure it is what we expected.
	if  := PrefixByte([0]);  != PrefixByteCurve {
		return ErrInvalidPublicKey
	}
	var  uint16
	 :=  - 2
	 := [:]
	 := bytes.NewReader()
	if  := binary.Read(, binary.LittleEndian, &);  != nil {
		return 
	}

	// ensure checksum is valid
	if  := validate([:], );  != nil {
		return 
	}

	// Copy over, ignore prefix byte.
	copy(, [1:])
	return nil
}

// Only version for now, but could add in X3DH in the future, etc.
const XKeyVersionV1 = "xkv1"
const vlen = len(XKeyVersionV1)

// Seal is compatible with nacl.Box.Seal() and can be used in similar situations for small messages.
// We generate the nonce from crypto rand by default.
func ( *ckp) ( []byte,  string) ([]byte, error) {
	return .SealWithRand(, , rand.Reader)
}

func ( *ckp) ( []byte,  string,  io.Reader) ([]byte, error) {
	var (
		  [curveKeyLen]byte
		 [curveNonceLen]byte
		   [vlen + curveNonceLen]byte
		   error
	)

	if  = decodePubCurveKey(, [:]);  != nil {
		return nil, ErrInvalidRecipient
	}
	if ,  := io.ReadFull(, [:]);  != nil {
		return nil, 
	}
	copy([:vlen], []byte(XKeyVersionV1))
	copy([vlen:], [:])
	return box.Seal([:], , &, &, &.seed), nil
}

func ( *ckp) ( []byte,  string) ([]byte, error) {
	if len() <= vlen+curveNonceLen {
		return nil, ErrInvalidEncrypted
	}
	var (
		  [curveKeyLen]byte
		 [curveNonceLen]byte
		   error
	)
	if !bytes.Equal([:vlen], []byte(XKeyVersionV1)) {
		return nil, ErrInvalidEncVersion
	}
	copy([:], [vlen:vlen+curveNonceLen])

	if  = decodePubCurveKey(, [:]);  != nil {
		return nil, ErrInvalidSender
	}

	,  := box.Open(nil, [vlen+curveNonceLen:], &, &, &.seed)
	if ! {
		return nil, ErrCouldNotDecrypt
	}
	return , nil
}

// Wipe will randomize the contents of the secret key
func ( *ckp) () {
	io.ReadFull(rand.Reader, .seed[:])
}

func ( *ckp) ( []byte) ([]byte, error) {
	return nil, ErrInvalidCurveKeyOperation
}

func ( *ckp) ( []byte,  []byte) error {
	return ErrInvalidCurveKeyOperation
}