BlockchainCommons / Research

Blockchain Commons Research papers
Other
111 stars 38 forks source link

BCR-2020-007 Keypath/HD Keys: Master vs parent fingerprint #52

Closed craigraw closed 3 years ago

craigraw commented 3 years ago

In BCR-2020-007, the specification for a derived-key includes a crypto-keypath for the key origin. However, crypto-keypath specifies a parent-fingerprint field instead of a master-fingerprint field, as would be normal for key origin information. This leads to a number of issues, and I believe the specification for a derived-key needs to be changed.

Note that the parent fingerprint is necessary in order to construct the xpub from a crypto-hdkey UR encoding. Therefore, the example in Test Vector 2 encodes the parent fingerprint into the key origin crypto-keypath, instead of the master fingerprint. This means the xpub can be reconstructed, but the master fingerprint is lost.

The issue is more serious when considering BCR-2020-010: Output descriptors. Considering Test Vector 4, we see here that the master fingerprint is encoded into the origin crypto-keypath instead of the parent fingerprint. This means that the parent fingerprint (in this case 0x78412e3a) is lost, and the xpub - and therefore the output descriptor - cannot be reconstructed.

My suggestion to resolve these issues is to add a further crypto-keypath to derived-key in BCR-2020-007 which contains the parent fingerprint, one path component with the child number, and the depth the HD key was derived at (as per BIP32). This means all the fields necessary to derive the xpub are available independently of the origin crypto-keypath.

derived-key = (
        ...
        ? parent: #6.304(crypto-keypath)
)

...
parent = 8

Further, the definition for crypto-keypath should not reference parent-fingerprint but rather master-fingerprint.

wolfmcnally commented 3 years ago

My intent for crypto-keypath was that it could be used for partial derivations where a master key was not known, but only a parent. Of course the parent could be a master key, but it also may not. I think this was also the intent with BIP-32. I named the parent-fingerprint field after the description specified in BIP-32. If we change to specifying that this is always to be the master key fingerprint, and not merely the parent fingerprint, it would seem we're breaking BIP-32 isomorphism. Do you agree?

In the case of Test Vector 4, I think the working assumption is that in these cases the "parent" is always the master.

I don't consider myself to be a deep expert on these matters. Please continue to help me understand the issue so we can resolve it.

craigraw commented 3 years ago

Yes, I do agree - on reflection, let me amend to: the definition for crypto-keypath should not reference parent-fingerprint but rather fingerprint.

The specification for output descriptors (and hence PSBTs) has taken the original BIP32 "parent fingerprint" definition and used it for master fingerprints as well. I haven't seen it used for any other kind of relative position in a derivation path, but it's possible it might be in future. If the fingerprint in crypto-keypath is generic it can be defined by the map key it is associated with (in my proposal, origin or parent).

Re Test Vector 4, if the origin crypto-keypath contains the master fingerprint (which I think is correct), then we don't have access to the parent fingerprint to recreate the xpub as required by BIP32. Hence my suggestion of adding a further crypto-keypath to store the parent fingerprint.

wolfmcnally commented 3 years ago

I think I'm understanding the problem better. See whether this makes sense to you:

BIP-32 specifies a scheme for deriving a hierarchy of keys, and it specifies a binary format for encoding a single key. That binary format has a field for the "parent fingerprint", which in the context of BIP-32 appears to specifically mean the direct ancestor of the presented key; this why if the key has no parent, i.e., it is a master key, the fingerprint should be zero. The encoding specified by BIP-32 does not contain a derivation path.

On the other hand, the crypto-keypath structure contains a derivation path and a fingerprint, and does not encode an actual key. Its job is solely to specify a derivation or set of derivations allowed from another key, possibly a key having a specified fingerprint. That key may or may not be a master key, and while the specified fingerprint is definitely of an ancestor key, it will commonly not be the direct parent in the sense that BIP-32 means.

This means the use of the name parent-fingerprint in the crypto-keypath definition is confusing. We should rename it, but then the question arises of maintaining BIP-32 isomorphism.

One solution would be:

  1. In crypto-keypath, rename parent-fingerprint to source-fingerprint. This field, if present, will be the fingerprint of the key to which this derivation path is expected to be applied. When used in the origin field of the derived-key structure, this is the fingerprint of the key from which the presented key was derived. It should not be used in the children field of the derived-key structure, as the fingerprint would be the fingerprint of the presented key, and can thus be derived if needed.
  2. Add a new parent-fingerprint field to the derived-key CBOR structure. This field, if present, would have the exact same meaning as BIP-32: the fingerprint of the presented key's direct ancestor.

Does this deal with the use-cases you're considering?

ChristopherA commented 3 years ago

I'm not sure that this is relevant, but I'd always hoped that one scenario with LetheKit was that the seed and the master key (m) were never imported into Gordian Signer. Instead, you'd derive something like m/48’/0’/0’/12’/* for each new account map, such that the leaf keys are m/48’/0’/0’/12’/*/0,1… and QR it in your multisig signer app. This way if your signer is compromised, only one account map needs to be swept (presumably by the other keys in the descriptor).

If it was important that your mulitsig signer app could participate in adding additional new account maps (say because it need to make advance commitments for a musig2 ceremony), it would instead save m/48’/0’/0’/2’ (one level up) as the local root to derive all the of the account maps for that signer. In this case if your signer is compromised, you'd have to sweep all of the sub-account maps that are associated with that local root, and you'd move to m/48’/0’/0’/13’/* for all new account maps. This would be safe as your seed was never only online, only briefly the non-networked LetheKit.

With this approach, you only need one offline seed to safely participate in many multisig account accounts, and to be able to sweep them forward without create a new seed. Or create a business/family/personal local root and keep them explicitly seperate.

/cc @Fonta1n3 @jollyjoker992 @gorazdko

craigraw commented 3 years ago

@wolfmcnally Your understanding is correct, and the solution you have proposed would solve the issue.

I think it's also necessary to amend the following comment to:

; To maintain isomorphism with [BIP32] and allow keys to be derived from
; this key, `chain-code`, `parent-fingerprint` and `origin` must be present

This is because the binary format for encoding a key in BIP32 also requires

This information should be present in the origin crypto-keypath field components, so it is required for BIP32 isomorphism.