Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BIP-360: QuBit - Pay to Quantum Resistant Hash #1670

Open
wants to merge 36 commits into
base: master
Choose a base branch
from

Conversation

cryptoquick
Copy link

This spent several months gathering feedback from the mailing list and from other advisors. This is hopefully polished enough to submit upstream.

Let me know if you have any questions or feedback, and of course feel free to submit suggestions.

Thank you for your time.

@cryptoquick cryptoquick marked this pull request as draft September 27, 2024 18:18
bip-p2qrh.mediawiki Outdated Show resolved Hide resolved
bip-p2qrh.mediawiki Outdated Show resolved Hide resolved
bip-p2qrh.mediawiki Outdated Show resolved Hide resolved
Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting (the question of resistance to quantum computing may have resurged lately with the publication of https://scottaaronson.blog/?p=8329, see also https://x.com/n1ckler/status/1839215426091249778).

bip-p2qrh.mediawiki Outdated Show resolved Hide resolved
@cryptoquick cryptoquick force-pushed the p2qrh branch 2 times, most recently from b6ed2c3 to d6d15ad Compare September 28, 2024 18:01
bip-p2qrh.mediawiki Outdated Show resolved Hide resolved
@jonatack
Copy link
Member

jonatack commented Oct 1, 2024

@cryptoquick Can you begin to write up the sections currently marked as TBD, along with a backwards compatibility section (to describe incompatibilities, severity, and suggest mitigations, where applicable/relevant)? We've begun to reserve a range of BIP numbers for this topic, pending continued progress here.

@jonatack jonatack added the PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author label Oct 9, 2024
@jonatack
Copy link
Member

@cryptoquick ping for an update here. Have you seen https://groups.google.com/g/bitcoindev/c/p8xz08YTvkw / https://github.com/chucrut/bips/blob/master/bip-xxxx.md? It may be interesting to review each other and possibly collaborate.

Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all that I have at this time from an editorial standpoint. It would be good if this proposal got more feedback and/or endorsements from domain experts in the next steps.

Comment on lines +308 to +311
h1 = HASH256(pubkey1)
h2 = HASH256(pubkey2)
h3 = HASH256(pubkey3)
h4 = HASH256(pubkey4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, they’re the same cost, you just prefix the hashing process with the tag. The advantage is that it prevents any collisions or hashing the same things at different levels of the tree in different contexts. They could for example be used to disambiguate the different types of cryptographic schemes on the public key commitment level for free, or for distinguishing public key hashes from inner node hashes. I’m not sure they’re necessary here, but they clean-up a whole category of issues, so they might be useful.

@mraksoll4
Copy link

the main problem that I see is decryptor wallets, as well as a master private key and a master public key, curves allow us to use something like this, but post-quantum algorithms do not provide such an opportunity (in current implementations)

if use clasic logic with key pool , we simple get key pairs and use them

but how are we going to associate a key obtained from master keys with post quantum keys?

@cryptoquick
Copy link
Author

@mraksoll4 All PQC algos will of course need to be compatible with BIP-32 HD wallet-style key derivation. There are definitely PQC libraries out there that just assume you'll never want to bring your own entropy, and so they don't provide a field or argument to provide that, but the intention behind the implementation of this BIP is that there will be a custom PQC library for bitcoin specifically that will implement things like this. So, your concern, while valid, is an implementation detail, and doesn't really have much bearing on the BIP itself.

@mraksoll4
Copy link

mraksoll4 commented Jan 1, 2025

@mraksoll4 All PQC algos will of course need to be compatible with BIP-32 HD wallet-style key derivation. There are definitely PQC libraries out there that just assume you'll never want to bring your own entropy, and so they don't provide a field or argument to provide that, but the intention behind the implementation of this BIP is that there will be a custom PQC library for bitcoin specifically that will implement things like this. So, your concern, while valid, is an implementation detail, and doesn't really have much bearing on the BIP itself.

Well, we have no problems with private keys, and also with generating from a seed, I have already implemented for experiments on the liboqs library the use of my own seed for falcon and dilithium to obtain a pair of keys, as well as obtaining a public key from a private one.

There are also no problems with multi-signature, the signature itself can be merged.

but we have a problem with obtaining public keys from the master public key ; due to the design of post quantum algorithms, we do not have the ability to obtain public keys from other public keys through predictable mathematical operations as in ecdsa.

although perhaps I don’t fully understand how we get the master public key.

how start you can see at base examle .

open-quantum-safe/liboqs#2031

first we need to solve the problem of key hierarchy, or we will have to forget about generating public keys without cration the private key, although for example in Falcon you don't need the entire private key but only part of it to reconstruct the public key


/*
 * This function reconstructs the public key from a given private key.
 * It decodes the private key components (f and g) from the secret key
 * and uses them to regenerate the corresponding public key (h).
 * The generated public key is then encoded into the provided pk array.
 * 
 * public (pk):  The output buffer where the public key will be stored (must be at least PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES in size).
 * private (sk):  The input secret key (private key) in byte array format (must be PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES in size).
 * Return value: 0 on success, -1 on error.
 */
int 
PQCLEAN_FALCON1024_CLEAN_crypto_sign_pubkey_from_privkey(
    uint8_t *pk, const uint8_t *sk) {
    union {
        uint8_t b[FALCON_KEYGEN_TEMP_10];
        uint64_t dummy_u64;
        fpr dummy_fpr;
    } tmp;
    int8_t f[1024], g[1024], F[1024];
    uint16_t h[1024];
    size_t u, v;

    /*
     * Decode the private key.
     */
    if (sk[0] != 0x50 + 10) {
        return -1;
    }
    u = 1;
    v = PQCLEAN_FALCON1024_CLEAN_trim_i8_decode(
            f, 10, PQCLEAN_FALCON1024_CLEAN_max_fg_bits[10],
            sk + u, PQCLEAN_FALCON1024_CLEAN_CRYPTO_SECRETKEYBYTES - u);
    if (v == 0) {
        return -1;
    }
    u += v;
    v = PQCLEAN_FALCON1024_CLEAN_trim_i8_decode(
            g, 10, PQCLEAN_FALCON1024_CLEAN_max_fg_bits[10],
            sk + u, PQCLEAN_FALCON1024_CLEAN_CRYPTO_SECRETKEYBYTES - u);
    if (v == 0) {
        return -1;
    }

    /*
     * Reconstruct the public key using f and g by calling the compute_public function.
     */
    if (!PQCLEAN_FALCON1024_CLEAN_compute_public(h, f, g, 10, tmp.b)) {
        return -1;
    }

    /*
     * Encode public key.
     */
    pk[0] = 0x00 + 10;
    v = PQCLEAN_FALCON1024_CLEAN_modq_encode(
            pk + 1, PQCLEAN_FALCON1024_CLEAN_CRYPTO_PUBLICKEYBYTES - 1,
            h, 10);
    if (v != PQCLEAN_FALCON1024_CLEAN_CRYPTO_PUBLICKEYBYTES - 1) {
        return -1;
    }

    return 0;
}

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did another review pass. Updated the PR title, as it looks like the BIP is now named "Pay to Quantum Resistant Hash."

Have you read the mail list discussion at https://groups.google.com/g/bitcoindev/c/8O857bRSVV8? It might be good to weigh in there if you're inclined.

Most important of the comments below: #1670 (comment) and #1670 (comment).


The vulnerability of existing Bitcoin addresses is investigated in
[https://web.archive.org/web/20240715101040/https://www2.deloitte.com/nl/nl/pages/innovatie/artikelen/quantum-computers-
and-the-bitcoin-blockchain.html this Deloitte report]. The report estimates that in 2020 approximately 25% of the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, let's avoid a line break in the url here, and s/estimates/estimated/

-[https://web.archive.org/web/20240715101040/https://www2.deloitte.com/nl/nl/pages/innovatie/artikelen/quantum-computers-
-and-the-bitcoin-blockchain.html this Deloitte report]. The report estimates that in 2020 approximately 25% of the
+[https://web.archive.org/web/20240715101040/https://www2.deloitte.com/nl/nl/pages/innovatie/artikelen/quantum-computers-and-the-bitcoin-blockchain.html this Deloitte report]. The report estimated that in 2020 approximately 25% of the

bip-0360.mediawiki Outdated Show resolved Hide resolved
bip-0360.mediawiki Outdated Show resolved Hide resolved
|-
| P2WPKH || No¹ || bc1q || bc1qsnh5ktku9ztqeqfr89yrqjd05eh58nah884mku
|-
| P2WSH || No¹ || bc1q || bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ISTM all of the No¹ entries ought to be Yes¹ or If revealed¹...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be clearer if I set the table caption to be this?

Output types vulnerable to long-range attacks on unspent addresses

bip-0360.mediawiki Outdated Show resolved Hide resolved
Comment on lines 303 to 311
When spending, if a public key hash is provided in the attestation with an empty signature, that hash will be used
directly in the merkle tree computation rather than hashing the full public key. This allows excluding unused public
keys from the transaction while still proving they were part of the original commitment.

This merkle tree construction creates an efficient cryptographic commitment to multiple public keys while enabling
selective disclosure.

This allows for inclusion of a Taproot MAST merkle root in the attestation, which makes P2QRH a quantum-resistant
version of Taproot.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BIP114 is also named "MAST" (Merkelized Abstract Syntax Trees), so perhaps write out the MAST you are referring to here.

bip-0360.mediawiki Outdated Show resolved Hide resolved
bip-0360.mediawiki Outdated Show resolved Hide resolved
|-
| [https://eprint.iacr.org/2011/191.pdf Winternitz signature] || 1982 || 2,368 bytes<ref name="winternitz">Winternitz
signatures are much smaller than Lamport signatures due to efficient chunking, but computation is much higher,
especially with high values for w. Winternitz values are for w of 4.</ref> || 2,368 bytes || Hash-based cryptography
Copy link
Member

@jonatack jonatack Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the caveat and consequences be mentioned here that Winternitz signatures are one-time?


== Test Vectors and Reference Code ==

TBD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any timeline for adding these?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, it is necessary to solve a rather simple and complex problem: key structure tree of priv and pub keys. Since in post-quantum algorithms there are not even approximate solutions, possible it will be universal since almost all of them are built on lattices

Well, what’s important is that we only have a problem with generating public keys from a master public key or xpub, there are no problems with private keys, in almost any algorithm you can feed a seed to get a pair of keys and, as in the example above, reconstruct the public key from part of the private one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonatack I haven't had much time to focus on that part. Ideally I could add them as a separate pull request.

@jonatack jonatack changed the title BIP-360: QuBit - P2QRH spending rules BIP-360: QuBit - Pay to Quantum Resistant Hash Jan 3, 2025
@cryptoquick
Copy link
Author

Have you read the mail list discussion at https://groups.google.com/g/bitcoindev/c/8O857bRSVV8? It might be good to weigh in there if you're inclined.

I did write a response in the mailing list many months ago, but it never showed up. Not sure what happened. I really don't want to repeat my analysis... It was a good amount of work that was lost.

@murchandamus
Copy link
Contributor

And is it not in your send folder?

@cryptoquick
Copy link
Author

And is it not in your send folder?

I composed it in the Google GUI ... Last I checked there wasn't a way to check back on it.

@murchandamus
Copy link
Contributor

murchandamus commented Jan 16, 2025

If you sent it from there, it should be in your "Sent" or "Drafts" folder, unless you deleted it.

@cryptoquick
Copy link
Author

I don't see anything like that here...
Screenshot from 2025-01-16 10-17-30

@murchandamus
Copy link
Contributor

murchandamus commented Jan 16, 2025

Oh, I thought you meant from the GUI of a Gmail account.

Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m a bit confused that another review was requested from me. As I said in my prior review:

This is all that I have at this time from an editorial standpoint. It would be good if this proposal got more feedback and/or endorsements from domain experts in the next steps.

I don’t think this needs another review from me, but rather it needs more engagement and support from other domain experts. Personally, I’m pretty skeptical about the approach of introducing a multitude of different signature schemes at once.

Comment on lines +133 to +136
and there are roughly 34,000 distinct P2PK scripts that are vulnerable. These coins can be considered
"Satoshi's Shield." Any addresses with a balance of less than the original block subsidy of 50 coins can be considered
cryptoeconomically incentive incompatible to capture until all of these are mined, and these addresses serve to provide
time to transition Bitcoin to implement post-quantum security.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once someone just starts stealing ~5% of the supply, it seems that it would be too late?

Comment on lines +316 to +318
This allows for inclusion of a [https://github.com/bitcoin/bips/blob/master/bip-0114.mediawiki BIP-114] Taproot
Merkelized Abstract Syntax Tree (MAST) merkle root in the attestation, which makes P2QRH a quantum-resistant
version of Taproot transactions.
Copy link
Contributor

@murchandamus murchandamus Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taproot does not use an Abstract Syntax Tree. It uses an Alternative Script Tree. Also the term "MAST" is not used in the Taproot BIPs in this context, they refer to the concept as "script tree".

The report estimates that in 2020 approximately 25% of the Bitcoin supply is held within addresses vulnerable to
quantum attack. As of the time of writing, that number is now closer to 20%. Independently, Bitcoin developer Pieter
Wuille [https://x.com/pwuille/status/1108085284862713856 reasons] even more addresses might be vulnerable, representing
5M to 10M bitcoin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As described above, please don’t reformat the entire paragraph when you change a single line. Moving all the line breaks makes it needlessly difficult to see what actually changed about the text.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I just noticed that it was over 120 characters in length, which was something you requested earlier.

@mraksoll4
Copy link

To start, I think we need to create a "skeleton" for integrating signature algorithms, such as Falcon-512 - 1024, as it seems to be the most optimal candidate at the moment. Since Falcon-512 - 1024 but it does not support key derivation for private and public keys, we can introduce an additional field in the wallet.dat to store the keys specifically for post-quantum signature algorithms.

Naturally, the descriptor would also need to be designed to handle single keys, ensuring compatibility with Falcon . This "skeleton" would serve as a foundation for integrating Falcon and similar post-quantum algorithms into the system. It will allow us to expand functionality while maintaining separation from existing ECDSA or other elliptic-curve-based algorithms.

I'm already trying to implement something similar using the available API, with slight extensions such as key generation from a seed and deriving a public key from a private key to match the expected logic

@mraksoll4
Copy link

Снимок экрана 2025-01-12 052928

@cryptoquick
Copy link
Author

@murchandamus

I’m a bit confused that another review was requested from me. As I said in my prior review:

This is all that I have at this time from an editorial standpoint. It would be good if this proposal got more feedback and/or endorsements from domain experts in the next steps.

I don’t think this needs another review from me, but rather it needs more engagement and support from other domain experts. Personally, I’m pretty skeptical about the approach of introducing a multitude of different signature schemes at once.

That makes sense. I'll try to solicit more feedback from SMEs. The only reason I re-requested review was because I was hoping to get an approval and merge, but I'm not sure how far off we are from that.

The reason four were proposed to be introduced was, not only does it make sense from a hybrid cryptography perspective (some may sign high-value transactions with multiple types for redundancy), it's also possible that this early on, there might be vulnerabilities in the PQC algorithms chosen. There is skepticism about algorithms that make more modern security assumptions. Some prefer hash-based algorithms like SPHINCS, which was a strong preference from people like Antoine Riard and Matthew Corallo. There are problems with SPHINCS, however, in that it's quite large, and also it doesn't support signature aggregation so far as I'm aware.

Do you think it might make sense to edit this to omit the specific signature algorithms? The only problem with that is, that makes it so I can't implement valid test vectors, and when I eventually go to implement BIP-360, it's unclear from the spec which algorithms to support.

My stance has evolved on this over time... In my opinion, we should wait as long as possible until PQC algos are better understood, verified, and implemented. That said, P2QRH, as it was introduced on the mailing list, is meant to just be a concrete starting point for these discussions, and it's essentially an imperfect compromise between multiple different competing interests.

Additionally, I'd like to have another conversation around how multisig should work. Currently specified is a merkle tree approach, but I'm considering instead going with an approach similar to P2SH as I outlined in one of my comments. I'm not sure if that should go into this PR or into a separate PR.

I also would like to work on an alternative BIP, one that's more conservative and informed by lessons learned from this BIP, for Pay to Taproot Hash (P2TRH, with SegWit v2 addresses), but I'm reticent to open multiple BIP PRs at once. I think it makes sense as a sort of stopgap for those who rely on signature aggregation such as FROST multisig, and it's a much less heavy a lift. It would be less controversial due to lack of PQC algo bikeshedding, and while it's imperfect because it doesn't make the mempool trustless to use, we can then point to P2QRH as the proposal to support if that's where community consensus is.

So, yes, there are a lot of considerations to be had here. I'm also likely going to need to deprecate SQIsign due to its terrible performance (100,000 times more computationally expensive to verify than ECDSA according to @EthanHeilman), and I might also want to include RACCOON because there's definitely demand for signature aggregation. Additionally, I've heard concerns about the constants chosen for FALCON in favor or NTRU Prime, but there are also people working on signature aggregation for FALCON which could improve the utility of the proposal.

One other possibility is that we make signature aggregation a requirement for any PQC algo included from this point forward so as to not have a significant reduction in functionality available with Taproot / Schnorr signatures. This requirement, which has been expressed to me by a number of users, would dramatically narrow down the number of valid signature algorithm candidates.

@mraksoll4

Since Falcon-512 - 1024 but it does not support key derivation for private and public keys

I believe this is actually an implementation detail. pqclean is unsuitable for bitcoin pqc because of its lack of BIP-32 support, but it might be useful as a basis. It just lacks an API to provide your own entropy, but that's not the fault of the signature algorithm, it's instead the fault of the implementation, which can be corrected in a more unified bitcoin pqc library designed in support of this BIP.

@mraksoll4
Copy link

mraksoll4 commented Jan 16, 2025

most post-quantum algorithms that are not yet cracked are lattice structures (unless we want the signature size to be several megabytes) their mathematical basis does not allow for tweaks, as in eleptic curves. These are different mathematical structures, for example RSA, the problem with dilithium and falcon is that there is no analogous G, even potential components f and g are secrets

This is not native functions of of clean , i created self based at exist function ( you can see it via added memory cleanse .. native does not clear temp buffers )

#include <stddef.h>
#include <string.h>
#include <stdio.h>

#include "api.h"
#include "inner.h"
#include "memory_cleanse.h"

#define NONCELEN   40

#include "randombytes.h"
/* keypair from fixed seed*/
int
PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_fseed(
    uint8_t *pk, uint8_t *sk, const uint8_t *seed) {
    union {
        uint8_t b[FALCON_KEYGEN_TEMP_9];
        uint64_t dummy_u64;
        fpr dummy_fpr;
    } tmp;
    int8_t f[512], g[512], F[512];
    uint16_t h[512];
    inner_shake256_context rng;
    size_t u, v;

    /*
     * Checking the input seed parameter.
     * If the seed is NULL, return an error.
     */
    if (seed == NULL) {
        return -1;  // Error: seed is not provided.
    }

    /*
     * Initialize the SHAKE256 random number generator using the seed.
     * We now pass the seed directly to the generator.
     */

    /*
     * print seed for debug 
	 */
    print_hex_debug("Generated Seed Keypair from fseed 1", seed, 48);

    inner_shake256_init(&rng);
    inner_shake256_inject(&rng, seed, 48);

    /*
     * print seed for debug 
	 */
    print_hex_debug("Generated Seed Keypair from fseed 2", seed, 48);

    memory_cleanse((void*)seed, 48);
    inner_shake256_flip(&rng);
    PQCLEAN_FALCON512_CLEAN_keygen(&rng, f, g, F, NULL, h, 9, tmp.b);
    inner_shake256_ctx_release(&rng);

	memory_cleanse(&rng, sizeof(inner_shake256_context));

    /*
     * Encode private key.
     */
    sk[0] = 0x50 + 9;
    u = 1;
    v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode(
            sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u,
            f, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9]);
    if (v == 0) {
		memory_cleanse(f, sizeof(f));
        memory_cleanse(g, sizeof(g));
        memory_cleanse(F, sizeof(F));
        memory_cleanse(h, sizeof(h));
        memory_cleanse(tmp.b, sizeof(tmp.b));
        return -1;
    }
    u += v;
    v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode(
            sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u,
            g, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9]);
    if (v == 0) {
		memory_cleanse(f, sizeof(f));
        memory_cleanse(g, sizeof(g));
        memory_cleanse(F, sizeof(F));
        memory_cleanse(h, sizeof(h));
        memory_cleanse(tmp.b, sizeof(tmp.b));
        return -1;
    }
    u += v;
    v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode(
            sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u,
            F, 9, PQCLEAN_FALCON512_CLEAN_max_FG_bits[9]);
    if (v == 0) {
		memory_cleanse(f, sizeof(f));
        memory_cleanse(g, sizeof(g));
        memory_cleanse(F, sizeof(F));
        memory_cleanse(h, sizeof(h));
        memory_cleanse(tmp.b, sizeof(tmp.b));
        return -1;
    }
    u += v;
    if (u != PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES) {
		memory_cleanse(f, sizeof(f));
        memory_cleanse(g, sizeof(g));
        memory_cleanse(F, sizeof(F));
        memory_cleanse(h, sizeof(h));
        memory_cleanse(tmp.b, sizeof(tmp.b));
        return -1;
    }

    memory_cleanse(f, sizeof(f));
    memory_cleanse(g, sizeof(g));
    memory_cleanse(F, sizeof(F));

    /*
     * Encode public key.
     */
    pk[0] = 0x00 + 9;
    v = PQCLEAN_FALCON512_CLEAN_modq_encode(
            pk + 1, PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1,
            h, 9);
    if (v != PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1) {
        memory_cleanse(h, sizeof(h));
        memory_cleanse(tmp.b, sizeof(tmp.b));
        return -1;
    }

    memory_cleanse(h, sizeof(h));
    memory_cleanse(tmp.b, sizeof(tmp.b));

    return 0;
}

/*
 * This function reconstructs the public key from a given private key.
 * It decodes the private key components (f and g) from the secret key
 * and uses them to regenerate the corresponding public key (h).
 * The generated public key is then encoded into the provided pk array.
 * 
 * public (pk):  The output buffer where the public key will be stored (must be at least PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES in size).
 * private (sk):  The input secret key (private key) in byte array format (must be PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES in size).
 * Return value: 0 on success, -1 on error.
 */
int
PQCLEAN_FALCON512_CLEAN_crypto_sign_pubkey_from_privkey(
    uint8_t *pk, const uint8_t *sk) {
    union {
        uint8_t b[FALCON_KEYGEN_TEMP_9];
        uint64_t dummy_u64;
        fpr dummy_fpr;
    } tmp;
    int8_t f[512], g[512];
    uint16_t h[512];
    size_t u, v;

    /*
     * Decode the private key.
     */
    if (sk[0] != 0x50 + 9) {
        return -1;
    }
    u = 1;
    v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode(
            f, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9],
            sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u);
    if (v == 0) {
        return -1;
    }
    u += v;
    v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode(
            g, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9],
            sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u);
    if (v == 0) {
        return -1;
    }

    /*
     * Reconstruct the public key using f and g by calling the compute_public function.
     */
    if (!PQCLEAN_FALCON512_CLEAN_compute_public(h, f, g, 9, tmp.b)) {
		memory_cleanse(f, sizeof(f));
        memory_cleanse(g, sizeof(g));
        memory_cleanse(tmp.b, sizeof(tmp.b));
	    memory_cleanse(h, sizeof(h));
        return -1;
    }

    /*
     * Encode public key.
     */
    pk[0] = 0x00 + 9;
    v = PQCLEAN_FALCON512_CLEAN_modq_encode(
            pk + 1, PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1,
            h, 9);
    if (v != PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1) {
		memory_cleanse(f, sizeof(f));
        memory_cleanse(g, sizeof(g));
        memory_cleanse(tmp.b, sizeof(tmp.b));
	    memory_cleanse(h, sizeof(h));
        return -1;
    }

    // Securely clear sensitive buffers
    memory_cleanse(f, sizeof(f));
    memory_cleanse(g, sizeof(g));
    memory_cleanse(tmp.b, sizeof(tmp.b));
	memory_cleanse(h, sizeof(h));
    return 0;
}

@cryptoquick
Copy link
Author

@mraksoll4 I'm not sure I understand the point you're trying to make.

@mraksoll4
Copy link

@mraksoll4 I'm not sure I understand the point you're trying to make.

I meant that the capabilities of elliptic curves cannot and should not be directly projected onto post-quantum algorithms. A different approach is needed. BIP32 and the ability to derive public keys from public keys is a unique feature of elliptic curves. Even isogeny-based cryptography, which no longer holds against certain attacks, doesn’t support such functionality. The key difference lies in the underlying mathematical structures.

But we can still build a key tree for private keys using abstractions on top of the seed.

The problem lies specifically with public keys.

@cryptoquick
Copy link
Author

No, it's not. BIP-32 doesn't rely on key tweaking. It just produces entropy (private keys) in a deterministic way.

I think you're confused because you're only working from one implementation. For example, this implementation of FALCON would support BIP-32:

https://docs.rs/falcon-rust/latest/falcon_rust/falcon1024/fn.keygen.html#:~:text=pub%20fn%20keygen(-,seed%3A%20%5Bu8%3B%2032%5D,-)%20-%3E%20(SecretKey%2C

@mraksoll4
Copy link

No, it's not. BIP-32 doesn't rely on key tweaking. It just produces entropy (private keys) in a deterministic way.

I think you're confused because you're only working from one implementation. For example, this implementation of FALCON would support BIP-32:

https://docs.rs/falcon-rust/latest/falcon_rust/falcon1024/fn.keygen.html#:~:text=pub%20fn%20keygen(-,seed%3A%20%5Bu8%3B%2032%5D,-)%20-%3E%20(SecretKey%2C

As far as I understand, this also works directly with key pairs. Earlier, I mentioned that we can manipulate the seed to generate child key pairs. The issue lies in the fact that we cannot derive public keys from other public key

shake256(seed + some data + some data  + index)=seed (48 bytes) --> int
PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_fseed(
    uint8_t *pk, uint8_t *sk, const uint8_t *seed)

@cryptoquick
Copy link
Author

I see your point now. For example, using an xpub alone to generate more keys for, say, a watch-only wallet, might not be possible with FALCON.

I'll need to think about that.

@mraksoll4
Copy link

I see your point now. For example, using an xpub alone to generate more keys for, say, a watch-only wallet, might not be possible with FALCON.

I'll need to think about that.

Yes, but nothing prevents us from using single keys for watch-only wallets. For example, if a user tries to export an xpub for a post-quantum algorithm, we could display a message like:

"Post-quantum algorithm [name] does not support xpub keys. You need to explicitly export the key using a command to retrieve it from the descriptor or key cache."

Alternatively, we could provide a list of existing keys in a serialized format. For instance:

Generate 2000 keys on-demand when the user requests a key list.
Offer an option to save the corresponding private keys to wallet.dat or not, securely wiping the buffer after generation.
This approach would give flexibility while acknowledging the limitations of post-quantum algorithms and maintaining compatibility with watch-only and partially signed setups.

@mraksoll4
Copy link

There is another option where multiple addresses can be linked to a single key pair through key packing and unpacking using additional parameters, with the index influencing the final address. However, this approach has limited practical value. While it makes it impossible to determine any connection between the addresses before a transaction is conducted, it resembles the concept of non-hardened derivation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.