Skip to content

[FEATURE] Seed XOR functionality in Seeds Menu #738

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

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

Advaitgaur004
Copy link

Description

This PR adds a new feature that allows users to create a new seed by XORing two existing seeds. This is an advanced feature for users who want to combine seeds for additional security or recovery options.

Characteristics

  • New "Seed XOR" option in the Seeds Menu when multiple compatible seeds are loaded
  • Two-step process for selecting seeds to XOR together
  • Compatible seeds must have the same word count and no passphrase
  • Resulting XORed seed is saved as a new seed in memory

NOTE : Currently a new setting SETTING__SEED_XOR is enabled for the time being.

This is tested on Emulator, with 2 seeds with BIP39 Mnemonic

abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon length (9d4cfd)

abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon lobster (6b88c501)

image

Then we need to select 2 seeds for Xoring

image
image

which lead to final seed aftering XORing

image

and BIP39 Mnemonic for this seed are

abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon actual

For testing, I'll use this script and credit goes to 91DarioDev

image

This pull request is categorized as a:

  • New feature
  • Bug fix
  • Code refactor
  • Documentation
  • Other

Checklist

  • I’ve run pytest and made sure all unit tests pass before sumbitting the PR

If you modified or added functionality/workflow, did you add new unit tests?

  • No, I’m a fool
  • Yes
  • N/A

I have tested this PR on the following platforms/os:

@Advaitgaur004 Advaitgaur004 changed the title Seed XOR functionality in Seeds Menu [Feature] Seed XOR functionality in Seeds Menu Apr 17, 2025
@kdmukai
Copy link
Contributor

kdmukai commented Apr 17, 2025

I would prefer this be a part of the seed loading process, though that doesn't necessarily mean that this standalone process isn't useful (but I'm less sure of that).

Think of solving this more from a FLOW-based philosophy that can guide the user right from the beginning (i.e. the difference between: "Load all n keys first, then select the SeedXOR option" vs "Start by selecting SeedXOR, then follow the prompts to load keys").

Something like:

  • In "Load A Seed" select "Rebuild SeedXOR" (or "Assemble"? or "Combine"?)
  • Prompt to load first component key (via SeedQR or mnemonic entry)
  • Prompt to load next component key
  • Show resulting SeedXOR fingerprint.
    • Load another key
    • OR "Finalization options" (goes to Finalize screen, user can add a bip39 passphrase or be done)

And then the end result would JUST be the final SeedXORed key held in memory. The individual SeedXOR component keys would be discarded.


The standalone process UX you've implemented is kind of a "reverse polish notation" approach (load first, then apply the operator). It's not really a big deal for a 2-component key SeedXOR. But if someone has 4 component keys...?

And is there any value in having those component keys stay loaded in memory? Just gets too be too much fingerprint management to keep track of which key is the final result vs which are component keys.

@Advaitgaur004
Copy link
Author

Advaitgaur004 commented Apr 17, 2025

Load all n keys first, then select the SeedXOR option" vs "Start by selecting SeedXOR, then follow the prompts to load keys"
Got it.

I will make a Flow Diagram for that.

Edit : Is this a correct implementation? (LINK)

@kdmukai
Copy link
Contributor

kdmukai commented Apr 17, 2025

Is this a correct implementation?

I may be misreading the intention, but I think the "More Keys?" decision point can be removed altogether. "Choose Action" already has that internal looping flow covered.

@Advaitgaur004
Copy link
Author

I may be misreading the intention, but I think the "More Keys?" decision point can be removed altogether. "Choose Action" already has that internal looping flow covered.

You're correct...the "More Keys?" decision point is redundant since the "Choose Action" step already includes the option to loop back to loading another key..

Diagram Changed [LINK]

@kdmukai
Copy link
Contributor

kdmukai commented Apr 17, 2025

Yeah, that matches how I was imagining the flow now.

You'll need some new FLOW__SEEDXOR kind of logic (see the existing FLOW__* constants) to handle all the nested key loading side flows (e.g. hopping out to type in a mnemonic then resuming the SeedXOR flow). Not sure if that might get kind of messy or if it'll be mostly straightforward (or probably bits of both).

@Advaitgaur004
Copy link
Author

Advaitgaur004 commented Apr 17, 2025

I will provide an update on progress and briefly touch on the advancements I've made.

@Advaitgaur004 Advaitgaur004 changed the title [Feature] Seed XOR functionality in Seeds Menu [Draft] Seed XOR functionality in Seeds Menu Apr 19, 2025
@Advaitgaur004 Advaitgaur004 marked this pull request as draft April 19, 2025 11:22
@Advaitgaur004 Advaitgaur004 changed the title [Draft] Seed XOR functionality in Seeds Menu [DRAFT][FEATURE] Seed XOR functionality in Seeds Menu Apr 19, 2025
Advaitgaur004 and others added 6 commits June 7, 2025 00:57
- View for loading a component key in the SeedXOR rebuild flow.
-  Current combined fingerprint of the SeedXOR components
- View for selecting an existing seed to use as a SeedXOR component.
- View for finalizing the SeedXOR seed.
@Advaitgaur004
Copy link
Author

@jdlcdl could you review the latested changes, as you are building this in past

I start by loading a seed and choosing the rebuild SeedXOR option. Then I load each component of the seed....either by scanning a SeedQR, typing the mnemonic, or using a seed already in-memory. After all parts are loaded, it shows me a fingerprint to confirm (at least two components are needed to finalize).

  • If I try to finalize with just one seed, then that same seed stays inmemory as-is.

Then I choose what to do next, either add a passphrase or just finish it. Once I’m done, the full seed is in memory, and the first component I loaded is gone.

Question :
Do we really need the first component here? I mean, assuming we already loaded like 3 seed phrases, I do the XOR with all 3 components ,so in that case, the first component gets dropped and the XORed seed takes its place. I did try to fix the first component too...but failed so i plan to look this in some other day, I’ll look into it again if needed.

@Advaitgaur004 Advaitgaur004 marked this pull request as ready for review June 7, 2025 16:02
@Advaitgaur004
Copy link
Author

Yeah, that matches how I was imagining the flow now.

You'll need some new FLOW__SEEDXOR kind of logic (see the existing FLOW__* constants) to handle all the nested key loading side flows (e.g. hopping out to type in a mnemonic then resuming the SeedXOR flow). Not sure if that might get kind of messy or if it'll be mostly straightforward (or probably bits of both).

updated !!

@t0m7
Copy link

t0m7 commented Jun 10, 2025

Just compiled and tested on a real hardware pi0. Here's a few of my notes and thoughts:

  1. Inconsistent option naming: 'Tools' -> 'Seed XOR', but 'Seeds' -> 'Rebuild SeedXOR'

  2. Flow looping. When entering the Seed XOR view (whichever way of the above) and trying to exit it without doing anything, the flow goes one level up to "Load A Seed" view where trying another exit up unexpectedly brings us back to the SeedXOR view (ie. down again), creating a loop. The only way to leave now is clicking any of 'Enter/Create seed' and then exiting.

  3. Currently using just 1 component is accepted for Finalize the XOR creating false impression that something has been done. I think no Finalize button should be shown here and only option of adding another component should be presented. Or even simply throwing an exception would be more appropriate here, as the XOR function simply requires 2 arguments.

  4. There's no way to properly XOR seeds with BIP-39 passphrase, so none components should have one, sure. Unfortunately seeds finalized with BIP-39 passphrase are accepted as components and some resulting seed is produced from such XOR. I suspect the BIP-39 passphrase was simply dropped before the the XOR function, but I didn't check if it's really the case, cause it should simply not be allowed in the first place.

  5. I understand that intention of naming XOR components by no.1, no.2.. etc, is making them somehow distinguishable, but this numbers may wrongly suggest that order of XOR function is important. Obviously it's not, and it totally doesn't matter in which order we apply the components, the result will always be the same of course. While the ordering naming convention may be slightly misleading only, what I don't really understand is why after performing the operation, no.1 component is kept and no.2 discarded (or vice versa, I don't really remember now which one). In my opinion it's important to stick to the mathematical truth that all the XOR function components are absolutely equal, none is privileged, first or last, better or worse, etc.., as they can be easily swapped in any way giving the same final effect.

  6. With that being said, in my opinion, discarding one/some of the components after the operation while keeping other is logically inconsistent. They should be kept all, with creation of a new one as the XOR result, or all deleted. Personally I'm inclining to keeping them all, as probably it's easier to delete what's not needed than entering them again.

  7. In my humble opinion, previous idea called similar to "reverse polish notation" to operate on the seeds was better and more clean. The process of entering a seed itself is quite long and a bit time consuming, especially if the seed is 24-words long. We have to keep in mind that not everyone will be using QRcodes. Now imagine, someone wants to "rebuild" his Seed_XOR from a few 24-w seeds, like 3 of them, 4, 6 or maybe more (quite paranoid, but why not?). The whole process becomes extremely complicated and long lasting, if it starts from selecting Seed_XOR option, then entering the first long seed, then second one, then third one, etc, etc.. so that after the last one we can finally close and finalize the Seed_XOR. If someone is extremely patient it may work for him, but isn't it way simpler and straightforward to put the seeds into memory first, double check the checksums if they are correct, and then perform the Seed_XOR quickly just by choosing the components from the memory where they are already stored? We're simply dividing a long and a time consuming process into simple and easily verifiable tasks.
    I'm not trying to say we should definitely remove this flow possibility. Maybe someone will find it useful, like I said. But what I think is worth considering is to move the "Use Loaded Seeds" button to the very top of "SeedXOR Component" selection view at least, as it should be the preferred and the easiest way to work.

  8. Earlier works by @jdlcdl were sadly dropped after some discussion, which I really regretted, as me personally, I find a great potential in using Seed XOR. Although I must agree that using it as a substitute of a multisig n-of-n type or even instead of BIP-39 passphrase is really not a great idea. Those latter ones are developed and recommended for a reason and making one's own strange shortcuts might be devastating in the end. But. But for me I can see SeedXOR as a really convenient and powerful enhancement of available options and a great, universal tool in my awesome toolbox, which SeedSigner definitely is. That's also why I personally don't really love the "Rebuild SeedXOR" option name. Cause again, for me it's just a tool, a general function to apply, not a substitute for something else or any rebuilding stuff. So just a "SeedXOR" would be better IMHO cause it is this function. Simple as that, nothing more, nothing less. Having said that, I also think that making XOR of 2 equal components (same one used twice), should not be forbidden. How would I easier make a 0x0000000... (abandon abandon, abandon,...) seed provided that I don't have a proper QRcode handy ATM? :D Sometimes we really need this crazy-at-a-first-sight but yet powerful and convenient solutions.

I'm sorry for a way too long comment maybe but I'm hoping it might be somehow useful. :)

@Advaitgaur004 Advaitgaur004 changed the title [DRAFT][FEATURE] Seed XOR functionality in Seeds Menu [FEATURE] Seed XOR functionality in Seeds Menu Jun 12, 2025
@Advaitgaur004
Copy link
Author

@t0m7
Thank you for the detailed overview. I apologise for my late comment. Currently, I am out of town, but I will review the details once I return home. Thank you for your time.

Btw its helpful, wonderful.

@jdlcdl
Copy link

jdlcdl commented Jun 12, 2025

tom7, your argument in 7 above is solid.

dipunm had convinced me that loading all seeds in a loop before finalizing it would be best, but I'm such a fan of SeedQR that in my case I'd simply be using xor so that I don't have a single qr in one place that is the full secret... and could move short distances with booted signer to retrieve hidden SeedQR components. Being able to load one at a time into memory allows for mistakes to be made in the event that one of the seeds is loaded the hard way.

I'm not fully understanding the use case of 8 above, to remove a component seed from the collection. I wonder, if there was a list of the naked fingerprints in the collection (in the order they were added or sorted so they always look the same), then being able to load in any order, or to remove a seed might be more clear to the user.... assuming they could see a list of all components currently added on one screen. so users might record fingerprints like (a=01234567, b=89abcdef, c=deadbeef) and their records might say "My wallet is xor(a,b,c)" but Seedsigner would simply say their xor collection has fingerprints (01234567, 89abcdef, and deadbeef)... if they add deadbeef again, then it would disappear from the list and this would be clear. It could also help to enforce getting familiar with fingerprints, and would make dealing with selecting loaded seeds easy.

@kdmukai
Copy link
Contributor

kdmukai commented Jun 12, 2025

but isn't it way simpler and straightforward to put the seeds into memory first, double check the checksums if they are correct, and then perform the Seed_XOR quickly just by choosing the components from the memory where they are already stored? We're simply dividing a long and a time consuming process into simple and easily verifiable tasks.

I disagree. The steps:

  • entering a mnemonic (or scanning a SeedQR),
  • the ease or difficulty of verifying the result and possibly having to edit (I can't recall our flow if a mnemonic is invalid; presumably the user can go back and edit?),
  • moving on to repeat for the additional seeds

ALL require the SAME amount of effort no matter what the flow is (enter each seed individually and then activate a SeedXOR mode vs one unified SeedXOR flow).

IF a mnemonic is invalid, the user would simply stay in the sub-flow to either correct the mnemonic or discard it and enter a fresh one. But in the meantime, the meta-flow (SeedXOR) would be retained in memory -- along with all the previously entered SeedXOR sub-seeds/shards/whatever the terminology is. This is how all of our sub-flows work (or at least should work; I'm not sure what happens if the PSBT flow goes through Load A Seed and there are loading errors. Do we correctly resume the PSBT flow and re-prompt to load a seed?).

However, IF we load each SeedXOR component seed separately, then we have a messier situation where we now have 3+1, 4+1, or 6+1 seeds all in memory (expanding on @t0m7's example).

In the most likely use case, would the user do any further operations on those first 3, 4, or 6 seeds? If not, why keep them around?

And won't having all those seeds onboard potentially create user confusion? Yes, the user should focus on the fingerprint of the final SeedXOR target result, but why clutter the UX with those sub-seeds that are now irrelevant after the SeedXOR has been re-assembled?

@kdmukai
Copy link
Contributor

kdmukai commented Jun 12, 2025

Note that I have not reviewed the code changes nor have I used this PR hands-on yet, but most of the discussion seems to be at a more abstract "what's the best approach" level vs code nitty gritty details.

And in general, just to protect my own time, I'm more inclined to wait until the approach has been more vetted / refined before I invest time to do code review and extensive hands-on testing.


But other quick thoughts on @t0m7's thorough feedback (using his number references):

  1. I think the name could / should reflect the use case available at each entry point. Roughly thinking it through:
  • Load A Seed: Inherently the task is rebuilding / reassembling a SeedXOR.
  • Seed Options: If we stick to my preferred flow options, the only sensible task would be to Create SeedXOR Shards (or whatever noun) that can recompose into the currently active seed. Essentially a more ambitious "Backup" option in that menu.
  • Tools: ...actually not sure what SeedXOR task makes the most sense here. Maybe nothing since CREATE and REASSEMBLE have already been covered?

IF we have the flow where arbitrary in-memory seeds can after the fact be SeedXOR'ed together, then we have a muddier UX where, yes, probably makes sense to have multiple REASSEMBLE entry points (SeedOptions, Tools, Load A Seed). Another reason why I dislike this flow approach.


  1. Sounds like each SeedXOR sub-seed (wtf is the right term to use?!!) entry step needs "Next Shard", "Finalize" (but only if 2+ are entered), or "Cancel" options. It gets difficult to have 3 options onscreen at once. It's kind of crappy, but might have to break it out into two screens:
  • (you just entered sub-seed with fingerprint xyz1234): "Edit"(?), "Continue"
  • (user pressed "Continue") Maybe a trivial status screen (e.g. n sub-seeds loaded, current resulting SeedXOR fingerprint): "Continue" or "Cancel SeedXOR" (exit to Main Menu).
  • (user pressed "Continue") (not sure if any further info is needed, but maybe reiterate the current SeedXOR fingerprint): "Load Next Shard" or "Finalize SeedXOR".

  1. I agree. "Finalize" should not be an option after only loading one shard.

  1. 100%. The loading flow should check if it's in a SeedXOR meta-flow and skip bip39 passphrase options when loading shards.

  1. Agree. "n shards loaded" and "Next shard" should really be all the terminology we need.

  1. #TeamDeleteThemAll

  1. (see my earlier response above)

  1. That's also why I personally don't really love the "Rebuild SeedXOR" option name. Cause again, for me it's just a tool, a general function to apply, not a substitute for something else or any rebuilding stuff.

I don't understand this point. I view the verb selection as being the same as you would use for Shamir. You have a bunch of shards that are useless on their own. You want to reassemble (or "rebuild") them into their original form.

Building off my reply to 1.) above, I would want the verb to make it clear which operation is intended by the menu option: CREATE the SeedXOR shards or REASSEMBLE/REBUILD the shards into the original seed. Even a momentary "Wait, is this 'SeedXOR' button going to break apart or reassemble?" doubt would be unnecessary friction in our UX.


I almost always forget to say it, but thank you for the time and thought you invested in your review, @t0m7.

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

Successfully merging this pull request may close these issues.

4 participants