func ParseRecipient(recipientHexString string) (*ecdsa.PublicKey, error) {
	publicKeyBytes, err := hex.DecodeString(recipientHexString)
	if err != nil {
		return nil, err
	}
	pubkey, err := btcec.ParsePubKey(publicKeyBytes, btcec.S256())
	if err != nil {
		return nil, err
	}
	return (*ecdsa.PublicKey)(pubkey), err
}
ParseRecipient extract ephemeral public key from the hexadecimal string to use with el-Gamal.
package pss
import (
	"bytes"
	"context"
	"crypto/ecdsa"
	random "crypto/rand"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"github.com/btcsuite/btcd/btcec"
	"github.com/ethsana/sana/pkg/bmtpool"
	"github.com/ethsana/sana/pkg/crypto"
	"github.com/ethsana/sana/pkg/encryption"
	"github.com/ethsana/sana/pkg/encryption/elgamal"
	"github.com/ethsana/sana/pkg/swarm"
	"golang.org/x/sync/errgroup"
)
var (
	ErrPayloadTooBig = fmt.Errorf("message payload size cannot be greater than %d bytes", MaxPayloadSize)
	ErrEmptyTargets = errors.New("target list cannot be empty")
	ErrVarLenTargets = errors.New("target list cannot have targets of different length")
)
type Topic [32]byte
func NewTopic(text string) Topic {
	bytes, _ := crypto.LegacyKeccak256([]byte(text))
	var topic Topic
	copy(topic[:], bytes[:32])
	return topic
}
type Target []byte
type Targets []Target
const (
	MaxPayloadSize = swarm.ChunkSize - 3*swarm.HashSize
)
func Wrap(ctx context.Context, topic Topic, msg []byte, recipient *ecdsa.PublicKey, targets Targets) (swarm.Chunk, error) {
	if len(msg) > MaxPayloadSize {
		return nil, ErrPayloadTooBig
	}
	integrity, err := crypto.LegacyKeccak256(msg)
	if err != nil {
		return nil, err
	}
	binary.BigEndian.PutUint16(integrity[:2], uint16(len(msg)))
	plaintext := append(integrity, msg...)
	enc, ephpub, err := elgamal.NewEncryptor(recipient, topic[:], 4032, swarm.NewHasher)
	if err != nil {
		return nil, err
	}
	ciphertext, err := enc.Encrypt(plaintext)
	if err != nil {
		return nil, err
	}
	ephpubBytes := (*btcec.PublicKey)(ephpub).SerializeCompressed()
	payload := append(ephpubBytes[1:], ciphertext...)
	odd := ephpubBytes[0]&0x1 != 0
	if err := checkTargets(targets); err != nil {
		return nil, err
	}
	targetsLen := len(targets[0])
	hash, err := crypto.LegacyKeccak256(append(enc.Key(), topic[:]...))
	if err != nil {
		return nil, err
	}
	hint := hash[:8]
	h := hasher(hint, payload)
	f := func(nonce []byte) (swarm.Chunk, error) {
		hash, err := h(nonce)
		if err != nil {
			return nil, err
		}
		if !contains(targets, hash[:targetsLen]) {
			return nil, nil
		}
		chunk := swarm.NewChunk(swarm.NewAddress(hash), append(hint, append(nonce, payload...)...))
		return chunk, nil
	}
	return mine(ctx, odd, f)
}
func Unwrap(ctx context.Context, key *ecdsa.PrivateKey, chunk swarm.Chunk, topics []Topic) (topic Topic, msg []byte, err error) {
	chunkData := chunk.Data()
	pubkey, err := extractPublicKey(chunkData)
	if err != nil {
		return Topic{}, nil, err
	}
	hint := chunkData[:8]
	for _, topic = range topics {
		select {
		case <-ctx.Done():
			return Topic{}, nil, ctx.Err()
		default:
		}
		dec, err := matchTopic(key, pubkey, hint, topic[:])
		if err != nil {
			privk := crypto.Secp256k1PrivateKeyFromBytes(topic[:])
			dec, err = matchTopic(privk, pubkey, hint, topic[:])
			if err != nil {
				continue
			}
		}
		ciphertext := chunkData[72:]
		msg, err = decryptAndCheck(dec, ciphertext)
		if err != nil {
			continue
		}
		break
	}
	return topic, msg, nil
}
func checkTargets(targets Targets) error {
	if len(targets) == 0 {
		return ErrEmptyTargets
	}
	validLen := len(targets[0])  
	for i := 1; i < len(targets); i++ {
		if len(targets[i]) != validLen {
			return ErrVarLenTargets
		}
	}
	return nil
}
func hasher(span, b []byte) func([]byte) ([]byte, error) {
	return func(nonce []byte) ([]byte, error) {
		s := append(nonce, b...)
		hasher := bmtpool.Get()
		defer bmtpool.Put(hasher)
		hasher.SetHeader(span)
		if _, err := hasher.Write(s); err != nil {
			return nil, err
		}
		return hasher.Hash(nil)
	}
}
func contains(col Targets, elem []byte) bool {
	for i := range col {
		if bytes.Equal(elem, col[i]) {
			return true
		}
	}
	return false
}
func mine(ctx context.Context, odd bool, f func(nonce []byte) (swarm.Chunk, error)) (r swarm.Chunk, e error) {
	seeds := make([]uint32, 8)
	initnonce := make([]byte, 32)
	if _, err := io.ReadFull(random.Reader, initnonce); err != nil {
		return nil, err
	}
	if odd {
		initnonce[28] |= 0x01
	} else {
		initnonce[28] &= 0xfe
	}
	for i := range seeds {
		seeds[i] = binary.BigEndian.Uint32(initnonce[i*4 : i*4+4])
	}
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	eg, ctx := errgroup.WithContext(ctx)
	result := make(chan swarm.Chunk, 8)
	for i := 0; i < 8; i++ {
		j := i
		eg.Go(func() error {
			nonce := make([]byte, 32)
			copy(nonce, initnonce)
			for seed := seeds[j]; ; seed += 2 {
				select {
				case <-ctx.Done():
					return ctx.Err()
				default:
				}
				binary.BigEndian.PutUint32(nonce[j*4:j*4+4], seed)
				res, err := f(nonce)
				if err != nil {
					return err
				}
				if res != nil {
					result <- res
					return nil
				}
			}
		})
	}
	errc := make(chan error)
	go func() {
		errc <- eg.Wait()
	}()
	select {
	case r = <-result:
	case e = <-errc:
	}
	return r, e
}
func extractPublicKey(chunkData []byte) (*ecdsa.PublicKey, error) {
	pubkeyBytes := make([]byte, 33)
	pubkeyBytes[0] |= 0x2
	copy(pubkeyBytes[1:], chunkData[40:72])
	if chunkData[36]|0x1 != 0 {
		pubkeyBytes[0] |= 0x1
	}
	pubkey, err := btcec.ParsePubKey(pubkeyBytes, btcec.S256())
	return (*ecdsa.PublicKey)(pubkey), err
}
func matchTopic(key *ecdsa.PrivateKey, pubkey *ecdsa.PublicKey, hint, topic []byte) (encryption.Decrypter, error) {
	dec, err := elgamal.NewDecrypter(key, pubkey, topic, swarm.NewHasher)
	if err != nil {
		return nil, err
	}
	match, err := crypto.LegacyKeccak256(append(dec.Key(), topic...))
	if err != nil {
		return nil, err
	}
	if !bytes.Equal(hint, match[:8]) {
		return nil, errors.New("topic does not match hint")
	}
	return dec, nil
}
func decryptAndCheck(dec encryption.Decrypter, ciphertext []byte) ([]byte, error) {
	plaintext, err := dec.Decrypt(ciphertext)
	if err != nil {
		return nil, err
	}
	length := int(binary.BigEndian.Uint16(plaintext[:2]))
	if length > MaxPayloadSize {
		return nil, errors.New("invalid length")
	}
	msg := plaintext[32 : 32+length]
	integrity := plaintext[2:32]
	hash, err := crypto.LegacyKeccak256(msg)
	if err != nil {
		return nil, err
	}
	if !bytes.Equal(integrity, hash[2:]) {
		return nil, errors.New("invalid message")
	}
	return msg, nil
}