Skip to content

Commit 49899e5

Browse files
theStackjosibake
authored andcommitted
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 50a60ca commit 49899e5

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

include/secp256k1_silentpayments.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,58 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_c
119119
size_t n_plain_seckeys
120120
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
121121

122+
/** Create Silent Payment label tweak and label.
123+
*
124+
* Given a recipient's 32 byte scan key b_scan and a label integer m, calculate the
125+
* corresponding label tweak and label:
126+
*
127+
* label_tweak = hash(b_scan || m)
128+
* label = label_tweak * G
129+
*
130+
* Returns: 1 if label tweak and label creation was successful.
131+
* 0 if label_tweak32 is an invalid scalar (statistically improbable).
132+
* Args: ctx: pointer to a context object
133+
* Out: label: pointer to the resulting label public key
134+
* label_tweak32: pointer to the 32 byte label tweak
135+
* In: recipient_scan_key32: pointer to the recipient's 32 byte scan key
136+
* m: label integer (0 is used for change outputs)
137+
*/
138+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_label(
139+
const secp256k1_context *ctx,
140+
secp256k1_pubkey *label,
141+
unsigned char *label_tweak32,
142+
const unsigned char *recipient_scan_key32,
143+
const uint32_t m
144+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
145+
146+
/** Create Silent Payment labeled spend public key.
147+
*
148+
* Given a recipient's spend public key B_spend and a label, calculate the
149+
* corresponding labeled spend public key:
150+
*
151+
* B_m = B_spend + label
152+
*
153+
* The result is used by the recipient to create a Silent Payment address,
154+
* consisting of the serialized and concatenated scan public key and
155+
* (labeled) spend public key each.
156+
*
157+
* Returns: 1 if labeled spend public key creation was successful.
158+
* 0 if the input public keys are invalid,
159+
* or the spend pubkey + label sum to zero (statistically improbable).
160+
* Args: ctx: pointer to a context object
161+
* Out: labeled_spend_pubkey: pointer to the resulting labeled spend
162+
* public key
163+
* In: unlabeled_spend_pubkey: pointer to the recipient's unlabeled spend pubkey
164+
* label: pointer to the recipient's label public
165+
* key
166+
*/
167+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(
168+
const secp256k1_context *ctx,
169+
secp256k1_pubkey *labeled_spend_pubkey,
170+
const secp256k1_pubkey *unlabeled_spend_pubkey,
171+
const secp256k1_pubkey *label
172+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
173+
122174
#ifdef __cplusplus
123175
}
124176
#endif

src/modules/silentpayments/main_impl.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,73 @@ int secp256k1_silentpayments_sender_create_outputs(
303303
return 1;
304304
}
305305

306+
/** Set hash state to the BIP340 tagged hash midstate for "BIP0352/Label". */
307+
static void secp256k1_silentpayments_sha256_init_label(secp256k1_sha256* hash) {
308+
secp256k1_sha256_initialize(hash);
309+
hash->s[0] = 0x26b95d63ul;
310+
hash->s[1] = 0x8bf1b740ul;
311+
hash->s[2] = 0x10a5986ful;
312+
hash->s[3] = 0x06a387a5ul;
313+
hash->s[4] = 0x2d1c1c30ul;
314+
hash->s[5] = 0xd035951aul;
315+
hash->s[6] = 0x2d7f0f96ul;
316+
hash->s[7] = 0x29e3e0dbul;
317+
318+
hash->bytes = 64;
319+
}
320+
321+
int secp256k1_silentpayments_recipient_create_label(const secp256k1_context *ctx, secp256k1_pubkey *label, unsigned char *label_tweak32, const unsigned char *recipient_scan_key32, const uint32_t m) {
322+
secp256k1_sha256 hash;
323+
unsigned char m_serialized[4];
324+
325+
/* Sanity check inputs. */
326+
VERIFY_CHECK(ctx != NULL);
327+
ARG_CHECK(label != NULL);
328+
ARG_CHECK(label_tweak32 != NULL);
329+
ARG_CHECK(recipient_scan_key32 != NULL);
330+
331+
/* Compute label_tweak = hash(ser_256(b_scan) || ser_32(m)) [sha256 with tag "BIP0352/Label"] */
332+
secp256k1_silentpayments_sha256_init_label(&hash);
333+
secp256k1_sha256_write(&hash, recipient_scan_key32, 32);
334+
secp256k1_write_be32(m_serialized, m);
335+
secp256k1_sha256_write(&hash, m_serialized, sizeof(m_serialized));
336+
secp256k1_sha256_finalize(&hash, label_tweak32);
337+
338+
/* Compute label = label_tweak * G */
339+
return secp256k1_ec_pubkey_create(ctx, label, label_tweak32);
340+
}
341+
342+
int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(const secp256k1_context *ctx, secp256k1_pubkey *labeled_spend_pubkey, const secp256k1_pubkey *recipient_spend_pubkey, const secp256k1_pubkey *label) {
343+
secp256k1_ge B_m, label_addend;
344+
secp256k1_gej result_gej;
345+
secp256k1_ge result_ge;
346+
int ret;
347+
348+
/* Sanity check inputs. */
349+
VERIFY_CHECK(ctx != NULL);
350+
ARG_CHECK(labeled_spend_pubkey != NULL);
351+
ARG_CHECK(recipient_spend_pubkey != NULL);
352+
ARG_CHECK(label != NULL);
353+
354+
/* Calculate B_m = B_spend + label
355+
* If either the label or spend public key is an invalid public key,
356+
* return early
357+
*/
358+
ret = secp256k1_pubkey_load(ctx, &B_m, recipient_spend_pubkey);
359+
ret &= secp256k1_pubkey_load(ctx, &label_addend, label);
360+
if (!ret) {
361+
return ret;
362+
}
363+
secp256k1_gej_set_ge(&result_gej, &B_m);
364+
secp256k1_gej_add_ge_var(&result_gej, &result_gej, &label_addend, NULL);
365+
if (secp256k1_gej_is_infinity(&result_gej)) {
366+
return 0;
367+
}
368+
369+
secp256k1_ge_set_gej(&result_ge, &result_gej);
370+
secp256k1_pubkey_save(labeled_spend_pubkey, &result_ge);
371+
372+
return 1;
373+
}
374+
306375
#endif

src/modules/silentpayments/tests_impl.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,47 @@ static void test_send_api(void) {
253253
}
254254
}
255255

256+
static void test_label_api(void) {
257+
secp256k1_pubkey l, s, ls, e; /* label pk, spend pk, labeled spend pk, expected labeled spend pk */
258+
unsigned char lt[32]; /* label tweak */
259+
const unsigned char expected[33] = {
260+
0x03, 0xdc, 0x7f, 0x09, 0x9a, 0xbe, 0x95, 0x7a,
261+
0x58, 0x43, 0xd2, 0xb6, 0xbb, 0x35, 0x79, 0x61,
262+
0x5c, 0x60, 0x36, 0xa4, 0x9b, 0x86, 0xf4, 0xbe,
263+
0x46, 0x38, 0x60, 0x28, 0xa8, 0x1a, 0x77, 0xd4,
264+
0x91
265+
};
266+
267+
/* Create a label and labeled spend public key, verify we get the expected result */
268+
CHECK(secp256k1_ec_pubkey_parse(CTX, &s, BOB_ADDRESS[1], 33));
269+
CHECK(secp256k1_silentpayments_recipient_create_label(CTX, &l, lt, ALICE_SECKEY, 1));
270+
CHECK(secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &l));
271+
CHECK(secp256k1_ec_pubkey_parse(CTX, &e, expected, 33));
272+
CHECK(secp256k1_ec_pubkey_cmp(CTX, &ls, &e) == 0);
273+
274+
/* Check null values are handled */
275+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_label(CTX, NULL, lt, ALICE_SECKEY, 1));
276+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_label(CTX, &l, NULL, ALICE_SECKEY, 1));
277+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_label(CTX, &l, lt, NULL, 1));
278+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, NULL, &s, &l));
279+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, NULL, &l));
280+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, NULL));
281+
/* Check for malformed spend and label public keys, i.e., any single pubkey is malformed or the public
282+
* keys are valid but sum up to zero.
283+
*/
284+
{
285+
secp256k1_pubkey neg_spend_pubkey = s;
286+
CHECK(secp256k1_ec_pubkey_negate(CTX, &neg_spend_pubkey));
287+
CHECK(secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &neg_spend_pubkey) == 0);
288+
memset(&l, 0, sizeof(l));
289+
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(CTX, &ls, &s, &l));
290+
}
291+
}
292+
256293
void run_silentpayments_tests(void) {
257294
test_recipient_sort();
258295
test_send_api();
296+
test_label_api();
259297
}
260298

261299
#endif

0 commit comments

Comments
 (0)