Skip to content

Commit df0a1fe

Browse files
committed
silentpayments: recipient label support
Add function for creating a label tweak. This requires a tagged hash function for labels. This function is used by the receiver for creating labels to be used for a) creating labeled addresses and b) to populate a labels cache when scanning. Add function for creating a labeled spend pubkey. This involves taking a label tweak, turning it into a public key and adding it to the spend public key. This function is used by the receiver to create a labeled silent payment address. Add tests for the label API.
1 parent 5aaf3be commit df0a1fe

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

include/secp256k1_silentpayments.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,60 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_c
126126
size_t n_plain_seckeys
127127
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
128128

129+
/** Create Silent Payment label tweak and label.
130+
*
131+
* Given a recipient's 32 byte scan key and a label integer m, calculate the
132+
* corresponding label tweak and label:
133+
*
134+
* label_tweak = hash(scan_key || m)
135+
* label = label_tweak * G
136+
*
137+
* Returns: 1 if label tweak and label creation was successful.
138+
* 0 if hash output label_tweak32 is not valid scalar (negligible
139+
* probability per hash evaluation).
140+
*
141+
* Args: ctx: pointer to a context object
142+
* (not secp256k1_context_static)
143+
* Out: label: pointer to the resulting label public key
144+
* label_tweak32: pointer to the 32 byte label tweak
145+
* In: scan_key32: pointer to the recipient's 32 byte scan key
146+
* m: integer for the m-th label (0 is used for change outputs)
147+
*/
148+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_label(
149+
const secp256k1_context *ctx,
150+
secp256k1_pubkey *label,
151+
unsigned char *label_tweak32,
152+
const unsigned char *scan_key32,
153+
uint32_t m
154+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
155+
156+
/** Create Silent Payment labeled spend public key.
157+
*
158+
* Given a recipient's spend public key and a label, calculate the
159+
* corresponding labeled spend public key:
160+
*
161+
* labeled_spend_pubkey = unlabeled_spend_pubkey + label
162+
*
163+
* The result is used by the recipient to create a Silent Payment address,
164+
* consisting of the serialized and concatenated scan public key and
165+
* (labeled) spend public key.
166+
*
167+
* Returns: 1 if labeled spend public key creation was successful.
168+
* 0 if spend pubkey and label sum to zero (negligible probability for
169+
* labels created according to BIP352).
170+
*
171+
* Args: ctx: pointer to a context object
172+
* Out: labeled_spend_pubkey: pointer to the resulting labeled spend public key
173+
* In: unlabeled_spend_pubkey: pointer to the recipient's unlabeled spend public key
174+
* label: pointer to the recipient's label public key
175+
*/
176+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(
177+
const secp256k1_context *ctx,
178+
secp256k1_pubkey *labeled_spend_pubkey,
179+
const secp256k1_pubkey *unlabeled_spend_pubkey,
180+
const secp256k1_pubkey *label
181+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
182+
129183
#ifdef __cplusplus
130184
}
131185
#endif

src/modules/silentpayments/main_impl.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,74 @@ int secp256k1_silentpayments_sender_create_outputs(
326326
return 1;
327327
}
328328

329+
/** Set hash state to the BIP340 tagged hash midstate for "BIP0352/Label". */
330+
static void secp256k1_silentpayments_sha256_init_label(secp256k1_sha256* hash) {
331+
secp256k1_sha256_initialize(hash);
332+
hash->s[0] = 0x26b95d63ul;
333+
hash->s[1] = 0x8bf1b740ul;
334+
hash->s[2] = 0x10a5986ful;
335+
hash->s[3] = 0x06a387a5ul;
336+
hash->s[4] = 0x2d1c1c30ul;
337+
hash->s[5] = 0xd035951aul;
338+
hash->s[6] = 0x2d7f0f96ul;
339+
hash->s[7] = 0x29e3e0dbul;
340+
341+
hash->bytes = 64;
342+
}
343+
344+
int secp256k1_silentpayments_recipient_create_label(const secp256k1_context *ctx, secp256k1_pubkey *label, unsigned char *label_tweak32, const unsigned char *scan_key32, uint32_t m) {
345+
secp256k1_sha256 hash;
346+
unsigned char m_serialized[4];
347+
348+
/* Sanity check inputs. */
349+
VERIFY_CHECK(ctx != NULL);
350+
ARG_CHECK(label != NULL);
351+
ARG_CHECK(label_tweak32 != NULL);
352+
ARG_CHECK(scan_key32 != NULL);
353+
354+
/* Compute hash(ser_256(b_scan) || ser_32(m)) [sha256 with tag "BIP0352/Label"] */
355+
secp256k1_silentpayments_sha256_init_label(&hash);
356+
secp256k1_sha256_write(&hash, scan_key32, 32);
357+
secp256k1_write_be32(m_serialized, m);
358+
secp256k1_sha256_write(&hash, m_serialized, sizeof(m_serialized));
359+
secp256k1_sha256_finalize(&hash, label_tweak32);
360+
361+
secp256k1_memclear_explicit(m_serialized, sizeof(m_serialized));
362+
secp256k1_sha256_clear(&hash);
363+
return secp256k1_ec_pubkey_create(ctx, label, label_tweak32);
364+
}
365+
366+
int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(const secp256k1_context *ctx, secp256k1_pubkey *labeled_spend_pubkey, const secp256k1_pubkey *unlabeled_spend_pubkey, const secp256k1_pubkey *label) {
367+
secp256k1_ge labeled_spend_pubkey_ge, label_addend;
368+
secp256k1_gej result_gej;
369+
secp256k1_ge result_ge;
370+
int ret;
371+
372+
/* Sanity check inputs. */
373+
VERIFY_CHECK(ctx != NULL);
374+
ARG_CHECK(labeled_spend_pubkey != NULL);
375+
ARG_CHECK(unlabeled_spend_pubkey != NULL);
376+
ARG_CHECK(label != NULL);
377+
378+
/* Calculate labeled_spend_pubkey = spend_pubkey + label.
379+
* If either the label or spend public key is an invalid public key,
380+
* return early
381+
*/
382+
ret = secp256k1_pubkey_load(ctx, &labeled_spend_pubkey_ge, unlabeled_spend_pubkey);
383+
ret &= secp256k1_pubkey_load(ctx, &label_addend, label);
384+
if (!ret) {
385+
return 0;
386+
}
387+
secp256k1_gej_set_ge(&result_gej, &labeled_spend_pubkey_ge);
388+
secp256k1_gej_add_ge_var(&result_gej, &result_gej, &label_addend, NULL);
389+
if (secp256k1_gej_is_infinity(&result_gej)) {
390+
return 0;
391+
}
392+
393+
secp256k1_ge_set_gej_var(&result_ge, &result_gej);
394+
secp256k1_pubkey_save(labeled_spend_pubkey, &result_ge);
395+
396+
return 1;
397+
}
398+
329399
#endif

src/modules/silentpayments/tests_impl.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,10 +264,57 @@ static void test_send_api(void) {
264264
}
265265
}
266266

267+
static void test_label_api(void) {
268+
secp256k1_pubkey l, s, ls, e; /* label pk, spend pk, labeled spend pk, expected labeled spend pk */
269+
unsigned char lt[32]; /* label tweak */
270+
const unsigned char expected[33] = {
271+
0x03, 0xdc, 0x7f, 0x09, 0x9a, 0xbe, 0x95, 0x7a,
272+
0x58, 0x43, 0xd2, 0xb6, 0xbb, 0x35, 0x79, 0x61,
273+
0x5c, 0x60, 0x36, 0xa4, 0x9b, 0x86, 0xf4, 0xbe,
274+
0x46, 0x38, 0x60, 0x28, 0xa8, 0x1a, 0x77, 0xd4,
275+
0x91
276+
};
277+
278+
/* Create a label and labeled spend public key, verify we get the expected result */
279+
CHECK(secp256k1_ec_pubkey_parse(CTX, &s, BOB_ADDRESS[1], 33));
280+
CHECK(secp256k1_silentpayments_recipient_create_label(CTX, &l, lt, ALICE_SECKEY, 1));
281+
CHECK(secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &l));
282+
CHECK(secp256k1_ec_pubkey_parse(CTX, &e, expected, 33));
283+
CHECK(secp256k1_ec_pubkey_cmp(CTX, &ls, &e) == 0);
284+
285+
/* Check null values are handled */
286+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_label(CTX, NULL, lt, ALICE_SECKEY, 1));
287+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_label(CTX, &l, NULL, ALICE_SECKEY, 1));
288+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_label(CTX, &l, lt, NULL, 1));
289+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, NULL, &s, &l));
290+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, NULL, &l));
291+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, NULL));
292+
/* Check for malformed spend and label public keys, i.e., any single pubkey is malformed or the public
293+
* keys are valid but sum up to zero.
294+
*/
295+
{
296+
secp256k1_pubkey neg_spend_pubkey = s;
297+
CHECK(secp256k1_ec_pubkey_negate(CTX, &neg_spend_pubkey));
298+
CHECK(secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &neg_spend_pubkey) == 0);
299+
/* Also test with a malformed spend public key. */
300+
memset(&s, 0, sizeof(s));
301+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &neg_spend_pubkey));
302+
/* Reset s back to a valid public key for the next test. */
303+
CHECK(secp256k1_ec_pubkey_parse(CTX, &s, BOB_ADDRESS[1], 33));
304+
memset(&l, 0, sizeof(l));
305+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &l));
306+
/* Reset l back to a valid public key for the next test */
307+
CHECK(secp256k1_silentpayments_recipient_create_label(CTX, &l, lt, ALICE_SECKEY, 1));
308+
memset(&s, 0, sizeof(s));
309+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &l));
310+
}
311+
}
312+
267313
/* --- Test registry --- */
268314
static const struct tf_test_entry tests_silentpayments[] = {
269315
CASE1(test_recipient_sort),
270316
CASE1(test_send_api),
317+
CASE1(test_label_api),
271318
};
272319

273320
#endif

0 commit comments

Comments
 (0)