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