example_test.go raw

   1  // Copyright (c) 2014 The btcsuite developers
   2  // Copyright (c) 2015-2020 The Decred developers
   3  // Use of this source code is governed by an ISC
   4  // license that can be found in the LICENSE file.
   5  
   6  package secp256k1_test
   7  
   8  import (
   9  	"crypto/aes"
  10  	"crypto/cipher"
  11  	"encoding/binary"
  12  	"fmt"
  13  
  14  	"next.orly.dev/pkg/nostr/crypto/ec/secp256k1"
  15  	"next.orly.dev/pkg/nostr/encoders/hex"
  16  	"github.com/minio/sha256-simd"
  17  )
  18  
  19  // This example demonstrates use of GenerateSharedSecret to encrypt a message
  20  // for a recipient's public key, and subsequently decrypt the message using the
  21  // recipient's secret key.
  22  func Example_encryptDecryptMessage() {
  23  	newAEAD := func(key []byte) (cipher.AEAD, error) {
  24  		block, err := aes.NewCipher(key)
  25  		if err != nil {
  26  			return nil, err
  27  		}
  28  		return cipher.NewGCM(block)
  29  	}
  30  	// Decode the hex-encoded pubkey of the recipient.
  31  	pubKeyBytes, err := hex.Dec(
  32  		"04115c42e757b2efb7671c578530ec191a1359381e6a71127a9d37c486fd30da" +
  33  			"e57e76dc58f693bd7e7010358ce6b165e483a2921010db67ac11b1b51b651953d2",
  34  	) // uncompressed pubkey
  35  	if err != nil {
  36  		fmt.Println(err)
  37  		return
  38  	}
  39  	pubKey, err := secp256k1.ParsePubKey(pubKeyBytes)
  40  	if err != nil {
  41  		fmt.Println(err)
  42  		return
  43  	}
  44  	// Derive an ephemeral public/secret keypair for performing ECDHE with
  45  	// the recipient.
  46  	ephemeralSecKey, err := secp256k1.GenerateSecretKey()
  47  	if err != nil {
  48  		fmt.Println(err)
  49  		return
  50  	}
  51  	ephemeralPubKey := ephemeralSecKey.PubKey().SerializeCompressed()
  52  	// Using ECDHE, derive a shared symmetric key for encryption of the plaintext.
  53  	cipherKey := sha256.Sum256(
  54  		secp256k1.GenerateSharedSecret(
  55  			ephemeralSecKey,
  56  			pubKey,
  57  		),
  58  	)
  59  	// Seal the message using an AEAD.  Here we use AES-256-GCM.
  60  	// The ephemeral public key must be included in this message, and becomes
  61  	// the authenticated data for the AEAD.
  62  	//
  63  	// Note that unless a unique nonce can be guaranteed, the ephemeral
  64  	// and/or shared keys must not be reused to encrypt different messages.
  65  	// Doing so destroys the security of the scheme.  Random nonces may be
  66  	// used if XChaCha20-Poly1305 is used instead, but the message must then
  67  	// also encode the nonce (which we don't do here).
  68  	//
  69  	// Since a new ephemeral key is generated for every message ensuring there
  70  	// is no key reuse and AES-GCM permits the nonce to be used as a counter,
  71  	// the nonce is intentionally initialized to all zeros so it acts like the
  72  	// first (and only) use of a counter.
  73  	plaintext := []byte("test message")
  74  	aead, err := newAEAD(cipherKey[:])
  75  	if err != nil {
  76  		fmt.Println(err)
  77  		return
  78  	}
  79  	nonce := make([]byte, aead.NonceSize())
  80  	ciphertext := make([]byte, 4+len(ephemeralPubKey))
  81  	binary.LittleEndian.PutUint32(ciphertext, uint32(len(ephemeralPubKey)))
  82  	copy(ciphertext[4:], ephemeralPubKey)
  83  	ciphertext = aead.Seal(ciphertext, nonce, plaintext, ephemeralPubKey)
  84  	// The remainder of this example is performed by the recipient on the
  85  	// ciphertext shared by the sender.
  86  	//
  87  	// Decode the hex-encoded secret key.
  88  	pkBytes, err := hex.Dec(
  89  		"a11b0a4e1a132305652ee7a8eb7848f6ad5ea381e3ce20a2c086a2e388230811",
  90  	)
  91  	if err != nil {
  92  		fmt.Println(err)
  93  		return
  94  	}
  95  	secKey := secp256k1.SecKeyFromBytes(pkBytes)
  96  	// Read the sender's ephemeral public key from the start of the message.
  97  	// Error handling for inappropriate pubkey lengths is elided here for
  98  	// brevity.
  99  	pubKeyLen := binary.LittleEndian.Uint32(ciphertext[:4])
 100  	senderPubKeyBytes := ciphertext[4 : 4+pubKeyLen]
 101  	senderPubKey, err := secp256k1.ParsePubKey(senderPubKeyBytes)
 102  	if err != nil {
 103  		fmt.Println(err)
 104  		return
 105  	}
 106  	// Derive the key used to seal the message, this time from the
 107  	// recipient's secret key and the sender's public key.
 108  	recoveredCipherKey := sha256.Sum256(
 109  		secp256k1.GenerateSharedSecret(
 110  			secKey,
 111  			senderPubKey,
 112  		),
 113  	)
 114  	// Open the sealed message.
 115  	aead, err = newAEAD(recoveredCipherKey[:])
 116  	if err != nil {
 117  		fmt.Println(err)
 118  		return
 119  	}
 120  	nonce = make([]byte, aead.NonceSize())
 121  	recoveredPlaintext, err := aead.Open(
 122  		nil, nonce, ciphertext[4+pubKeyLen:],
 123  		senderPubKeyBytes,
 124  	)
 125  	if err != nil {
 126  		fmt.Println(err)
 127  		return
 128  	}
 129  	fmt.Println(string(recoveredPlaintext))
 130  	// Output:
 131  	// test message
 132  }
 133