@@ -18,155 +18,112 @@ function pubkeyFromPrivkey(secHex: string): string {
1818
1919const SEC1 = '0000000000000000000000000000000000000000000000000000000000000001'
2020const SEC2 = '0000000000000000000000000000000000000000000000000000000000000002'
21+ const SEC3 = '0000000000000000000000000000000000000000000000000000000000000003'
2122const KNOWN_CONVERSATION_KEY = 'c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d'
2223const KNOWN_NONCE = '0000000000000000000000000000000000000000000000000000000000000001'
2324const KNOWN_PLAINTEXT = 'a'
2425const KNOWN_PAYLOAD =
2526 'AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb'
2627
28+ const PUB1 = pubkeyFromPrivkey ( SEC1 )
29+ const PUB2 = pubkeyFromPrivkey ( SEC2 )
30+ const PUB3 = pubkeyFromPrivkey ( SEC3 )
31+ const CONVERSATION_KEY = getConversationKey ( SEC1 , PUB2 )
32+ const RECIPIENT_CONVERSATION_KEY = getConversationKey ( SEC2 , PUB1 )
33+ const DIFFERENT_CONVERSATION_KEY = getConversationKey ( SEC1 , PUB3 )
34+
2735// ---------------------------------------------------------------------------
2836
2937describe ( 'NIP-44' , ( ) => {
3038 describe ( 'getConversationKey' , ( ) => {
3139 it ( 'derives the correct conversation key from sec1 and pub2' , ( ) => {
32- const pub2 = pubkeyFromPrivkey ( SEC2 )
33- const key = getConversationKey ( SEC1 , pub2 )
34- expect ( key . toString ( 'hex' ) ) . to . equal ( KNOWN_CONVERSATION_KEY )
40+ expect ( CONVERSATION_KEY . toString ( 'hex' ) ) . to . equal ( KNOWN_CONVERSATION_KEY )
3541 } )
3642
3743 it ( 'is symmetric: conv(a, B) == conv(b, A)' , ( ) => {
38- const pub1 = pubkeyFromPrivkey ( SEC1 )
39- const pub2 = pubkeyFromPrivkey ( SEC2 )
40- const keyAB = getConversationKey ( SEC1 , pub2 )
41- const keyBA = getConversationKey ( SEC2 , pub1 )
42- expect ( keyAB . toString ( 'hex' ) ) . to . equal ( keyBA . toString ( 'hex' ) )
44+ expect ( CONVERSATION_KEY . toString ( 'hex' ) ) . to . equal ( RECIPIENT_CONVERSATION_KEY . toString ( 'hex' ) )
4345 } )
4446
4547 it ( 'produces different keys for different key pairs' , ( ) => {
46- const sec3 = '0000000000000000000000000000000000000000000000000000000000000003'
47- const pub2 = pubkeyFromPrivkey ( SEC2 )
48- const pub3 = pubkeyFromPrivkey ( sec3 )
49- const key12 = getConversationKey ( SEC1 , pub2 )
50- const key13 = getConversationKey ( SEC1 , pub3 )
51- expect ( key12 . toString ( 'hex' ) ) . to . not . equal ( key13 . toString ( 'hex' ) )
48+ expect ( CONVERSATION_KEY . toString ( 'hex' ) ) . to . not . equal ( DIFFERENT_CONVERSATION_KEY . toString ( 'hex' ) )
5249 } )
5350 } )
5451
5552 describe ( 'nip44Encrypt' , ( ) => {
5653 it ( 'produces the canonical payload from the NIP-44 spec test vector' , ( ) => {
57- const pub2 = pubkeyFromPrivkey ( SEC2 )
58- const conversationKey = getConversationKey ( SEC1 , pub2 )
5954 const nonce = Buffer . from ( KNOWN_NONCE , 'hex' )
6055
61- const payload = nip44Encrypt ( KNOWN_PLAINTEXT , conversationKey , nonce )
56+ const payload = nip44Encrypt ( KNOWN_PLAINTEXT , CONVERSATION_KEY , nonce )
6257 expect ( payload ) . to . equal ( KNOWN_PAYLOAD )
6358 } )
6459
6560 it ( 'produces a valid base64 string starting with version byte 0x02' , ( ) => {
66- const pub2 = pubkeyFromPrivkey ( SEC2 )
67- const conversationKey = getConversationKey ( SEC1 , pub2 )
68-
69- const payload = nip44Encrypt ( 'hello' , conversationKey )
61+ const payload = nip44Encrypt ( 'hello' , CONVERSATION_KEY )
7062 const decoded = Buffer . from ( payload , 'base64' )
7163
7264 expect ( decoded [ 0 ] ) . to . equal ( 2 ) // version byte
7365 expect ( payload . length ) . to . be . within ( 132 , 87472 )
7466 } )
7567
7668 it ( 'produces different ciphertexts for the same plaintext (random nonce)' , ( ) => {
77- const pub2 = pubkeyFromPrivkey ( SEC2 )
78- const conversationKey = getConversationKey ( SEC1 , pub2 )
79-
80- const payload1 = nip44Encrypt ( 'same message' , conversationKey )
81- const payload2 = nip44Encrypt ( 'same message' , conversationKey )
69+ const payload1 = nip44Encrypt ( 'same message' , CONVERSATION_KEY )
70+ const payload2 = nip44Encrypt ( 'same message' , CONVERSATION_KEY )
8271
8372 expect ( payload1 ) . to . not . equal ( payload2 )
8473 } )
8574
8675 it ( 'throws for empty plaintext' , ( ) => {
87- const pub2 = pubkeyFromPrivkey ( SEC2 )
88- const conversationKey = getConversationKey ( SEC1 , pub2 )
89-
90- expect ( ( ) => nip44Encrypt ( '' , conversationKey ) ) . to . throw ( 'invalid plaintext length' )
76+ expect ( ( ) => nip44Encrypt ( '' , CONVERSATION_KEY ) ) . to . throw ( 'invalid plaintext length' )
9177 } )
9278
9379 it ( 'throws for plaintext exceeding 65535 bytes' , ( ) => {
94- const pub2 = pubkeyFromPrivkey ( SEC2 )
95- const conversationKey = getConversationKey ( SEC1 , pub2 )
96-
97- expect ( ( ) => nip44Encrypt ( 'x' . repeat ( 65536 ) , conversationKey ) ) . to . throw ( 'invalid plaintext length' )
80+ expect ( ( ) => nip44Encrypt ( 'x' . repeat ( 65536 ) , CONVERSATION_KEY ) ) . to . throw ( 'invalid plaintext length' )
9881 } )
9982 } )
10083
10184 describe ( 'nip44Decrypt' , ( ) => {
10285 it ( 'decrypts the canonical NIP-44 spec test vector' , ( ) => {
103- const pub2 = pubkeyFromPrivkey ( SEC2 )
104- const conversationKey = getConversationKey ( SEC1 , pub2 )
105-
106- const plaintext = nip44Decrypt ( KNOWN_PAYLOAD , conversationKey )
86+ const plaintext = nip44Decrypt ( KNOWN_PAYLOAD , CONVERSATION_KEY )
10787 expect ( plaintext ) . to . equal ( KNOWN_PLAINTEXT )
10888 } )
10989
11090 it ( 'round-trips any plaintext through encrypt then decrypt' , ( ) => {
111- const pub2 = pubkeyFromPrivkey ( SEC2 )
112- const conversationKey = getConversationKey ( SEC1 , pub2 )
11391 const original = 'Hola, que tal? 🌍'
11492
115- const payload = nip44Encrypt ( original , conversationKey )
116- const recovered = nip44Decrypt ( payload , conversationKey )
93+ const payload = nip44Encrypt ( original , CONVERSATION_KEY )
94+ const recovered = nip44Decrypt ( payload , CONVERSATION_KEY )
11795
11896 expect ( recovered ) . to . equal ( original )
11997 } )
12098
12199 it ( 'works with the symmetric key (recipient decrypts sender message)' , ( ) => {
122- const pub1 = pubkeyFromPrivkey ( SEC1 )
123- const pub2 = pubkeyFromPrivkey ( SEC2 )
124-
125- const senderKey = getConversationKey ( SEC1 , pub2 )
126- const recipientKey = getConversationKey ( SEC2 , pub1 )
127-
128- const payload = nip44Encrypt ( 'secret message' , senderKey )
129- const plaintext = nip44Decrypt ( payload , recipientKey )
100+ const payload = nip44Encrypt ( 'secret message' , CONVERSATION_KEY )
101+ const plaintext = nip44Decrypt ( payload , RECIPIENT_CONVERSATION_KEY )
130102
131103 expect ( plaintext ) . to . equal ( 'secret message' )
132104 } )
133105
134106 it ( 'throws when MAC is tampered' , ( ) => {
135- const pub2 = pubkeyFromPrivkey ( SEC2 )
136- const conversationKey = getConversationKey ( SEC1 , pub2 )
137- const payload = nip44Encrypt ( 'tamper me' , conversationKey )
107+ const payload = nip44Encrypt ( 'tamper me' , CONVERSATION_KEY )
138108
139109 // Flip the last character of the base64 payload to corrupt the MAC
140110 const tampered = payload . slice ( 0 , - 4 ) + 'AAAA'
141111
142- expect ( ( ) => nip44Decrypt ( tampered , conversationKey ) ) . to . throw ( )
112+ expect ( ( ) => nip44Decrypt ( tampered , CONVERSATION_KEY ) ) . to . throw ( )
143113 } )
144114
145115 it ( 'throws for payload starting with # (unsupported future version)' , ( ) => {
146- const pub2 = pubkeyFromPrivkey ( SEC2 )
147- const conversationKey = getConversationKey ( SEC1 , pub2 )
148-
149- expect ( ( ) => nip44Decrypt ( '#not-base64' , conversationKey ) ) . to . throw ( 'unknown version' )
116+ expect ( ( ) => nip44Decrypt ( '#not-base64' , CONVERSATION_KEY ) ) . to . throw ( 'unknown version' )
150117 } )
151118
152119 it ( 'throws for payload that is too short' , ( ) => {
153- const pub2 = pubkeyFromPrivkey ( SEC2 )
154- const conversationKey = getConversationKey ( SEC1 , pub2 )
155-
156- expect ( ( ) => nip44Decrypt ( 'dG9vc2hvcnQ=' , conversationKey ) ) . to . throw ( 'invalid payload size' )
120+ expect ( ( ) => nip44Decrypt ( 'dG9vc2hvcnQ=' , CONVERSATION_KEY ) ) . to . throw ( 'invalid payload size' )
157121 } )
158122
159123 it ( 'throws for wrong conversation key' , ( ) => {
160- const sec3 = '0000000000000000000000000000000000000000000000000000000000000003'
161- const pub2 = pubkeyFromPrivkey ( SEC2 )
162- const pub3 = pubkeyFromPrivkey ( sec3 )
163-
164- const senderKey = getConversationKey ( SEC1 , pub2 )
165- const wrongKey = getConversationKey ( SEC1 , pub3 )
166-
167- const payload = nip44Encrypt ( 'private' , senderKey )
124+ const payload = nip44Encrypt ( 'private' , CONVERSATION_KEY )
168125
169- expect ( ( ) => nip44Decrypt ( payload , wrongKey ) ) . to . throw ( )
126+ expect ( ( ) => nip44Decrypt ( payload , DIFFERENT_CONVERSATION_KEY ) ) . to . throw ( )
170127 } )
171128 } )
172129
@@ -176,9 +133,7 @@ describe('NIP-44', () => {
176133 } )
177134
178135 it ( 'returns undefined for a freshly encrypted payload' , ( ) => {
179- const pub2 = pubkeyFromPrivkey ( SEC2 )
180- const conversationKey = getConversationKey ( SEC1 , pub2 )
181- const payload = nip44Encrypt ( 'hello' , conversationKey )
136+ const payload = nip44Encrypt ( 'hello' , CONVERSATION_KEY )
182137
183138 expect ( validateNip44Payload ( payload ) ) . to . be . undefined
184139 } )
@@ -228,11 +183,9 @@ describe('NIP-44', () => {
228183
229184 for ( const [ unpaddedLen , expectedPaddedLen ] of cases ) {
230185 it ( `pads ${ unpaddedLen } bytes to ${ expectedPaddedLen } bytes` , ( ) => {
231- const pub2 = pubkeyFromPrivkey ( SEC2 )
232- const conversationKey = getConversationKey ( SEC1 , pub2 )
233186 const plaintext = 'a' . repeat ( unpaddedLen )
234187
235- const payload = nip44Encrypt ( plaintext , conversationKey )
188+ const payload = nip44Encrypt ( plaintext , CONVERSATION_KEY )
236189 const decoded = Buffer . from ( payload , 'base64' )
237190
238191 // Layout: 1 (version) + 32 (nonce) + paddedLen + 2 (length prefix) + 32 (mac)
0 commit comments