//go:build ignore
// +build ignore

package paillier

// envelope provide hybrid encryption for a user with a remote cryptographic server.
// The server has a KEK and the user wants to encrypt a file.
// 1. The user generate a random user key ukey
// 2. The user use ukey to encrypt the file
// 3. The user do the followings to encrypt the ukey with KEK without leaking ukey to the server:
// 3-0. The user generate a (temporary or long term) paillier key pair (sk, pk).
// 3-1. The user encrypt ukey with paillier encryption: ukeyCipher = PaiEnc(ukey, pk)
// 3-2. The server HOMOMORPHICLY encrypt ukey with KEK: ukeyEnvelopeCipher = PaiEnc(SM4Enc(ukey, KEK), pk)
// 3-3. The user decrypt ukeyEnvelopeCipher and get ukeyEnvelope = SM4Enc(ukey, KEK)
//
// The user do the following to decrypt the ukey and then to decrypt the file.
// Also without leaking ukey.
// 1. The user generate a (temporary or long term) paillier key pair (sk, pk).
// 2. User encrypt ukeyEnvelope: ukeyEnvelopeCipher = PaiEnc(ukeyEnvelope, pk)
// 3. The server HOMORPHICLY decrypt ukeyEnvelopeCipher: ukeyCipher = PaiEnc(ukey, pk)
// 4. The user decrypt ukeyCipher, with ukey.

import (
	"fmt"
	"math/big"

	"xdx.jelly/xgcl/grand"
	"xdx.jelly/xgcl/sm/sm4"
)

// xyzw => 0x0y0z0w
var tbl = []byte{0, 1, 4, 5, 16, 17, 20, 21, 64, 65, 68, 69, 80, 81, 84, 85}

type EnvelopeUser struct{}
type EnvelopeServer struct{}

// EncryptUserKey
func (EnvelopeUser) EncryptUserKey(ukey []byte, pk *PublicKey) (ukeyCipher *Cipher, err error) {
	if len(ukey)*8*2 > pk.Bits() {
		return nil, fmt.Errorf("input key too long, should less than %d bytes", pk.Bits()/8/2)
	}
	buf := make([]byte, 0, len(ukey)*2)
	for _, k := range ukey {
		buf = append(buf, tbl[k&0xf])
		buf = append(buf, tbl[k>>4])
	}
	return pk.Encrypt(new(big.Int).SetBytes(buf), grand.Reader)
}

func DecryptKey(c *Cipher, keyLength int, priKey *PrivateKey) ([]byte, error) {
	k, err := priKey.Decrypt(c)
	_ = err
	key2 := make([]byte, keyLength*2)
	k.FillBytes(key2)
	key := make([]byte, keyLength)
	for i := 0; i < len(key); i++ {
		x := key2[2*i]
		y := key2[2*i+1]
		n := byte(0)
		n |= (x >> 0) & 1
		n |= (x >> 1) & 2
		n |= (x >> 2) & 4
		n |= (x >> 3) & 8
		key[i] = n

		n = byte(0)
		n |= (y >> 0) & 1
		n |= (y >> 1) & 2
		n |= (y >> 2) & 4
		n |= (y >> 3) & 8
		key[i] |= (n << 4)
	}
	return key, nil
}

func GenEnvelope(c0 *Cipher, kek []byte, pubkey *PublicKey) ([]byte, *Cipher, error) {
	iv := grand.GetRandom(16)
	c1, err := sm4.EncryptECB(nil, kek, iv) // c1 = sm4.Enc(iv)
	c2, err := EncryptUserKey(c1, pubkey)   // c2 = PaiEnc(sm4.Enc(iv))
	c2.HomomorphicAdd(c2, c0, pubkey)       // c2 = PaiEnc(sm4.Enc(iv) ^ key)

	_ = err
	return iv, c2, nil
}

func DecryptEnvelope(c *Cipher, prikey *PrivateKey) ([]byte, error) {
	k, err := prikey.Decrypt(c)
	_ = err
	key2 := make([]byte, 32)
	k.FillBytes(key2)
	key := make([]byte, 16)
	for i := 0; i < 16; i++ {
		x := key2[2*i]
		y := key2[2*i+1]
		n := byte(0)
		n |= (x >> 0) & 1
		n |= (x >> 1) & 2
		n |= (x >> 2) & 4
		n |= (x >> 3) & 8
		key[i] = n

		n = byte(0)
		n |= (y >> 0) & 1
		n |= (y >> 1) & 2
		n |= (y >> 2) & 4
		n |= (y >> 3) & 8
		key[i] |= (n << 4)
	}
	return key, nil
}
