Skip to content

Expand BIP85 to include ECC key types#1968

Open
3rdIteration wants to merge 7 commits intobitcoin:masterfrom
3rdIteration:BIP85-ExpandCurveTypes
Open

Expand BIP85 to include ECC key types#1968
3rdIteration wants to merge 7 commits intobitcoin:masterfrom
3rdIteration:BIP85-ExpandCurveTypes

Conversation

@3rdIteration
Copy link
Contributor

So I'm doing some work on extending BIP85 functionality on SeedSigner to include GPG key generation, file verification, encrypted messaging, etc, but the resource limitation on low power devices like the Raspberry Pi really makes RSA keys quite painful to use at any useful bit length. (RSA3072 takes 15-20 minutes for a primary key + 3 subs)

Basically I'm suggesting an expansion of the current functionality to add support for some common ECC curves and provide a bit of a process for future curves to be added in a way that doesn't break existing implementations. (The simplest solution is to just use ECC with key length on the existing RSA derivation, but that doesn't seem very consistent with how BIP85 works for other similar use cases where the derivation path should tell you everything you need to know about the output data/entropy/usage)

While it would have been nice to have a separate derivation level for key_type, that ship has probably sailed, so rather than add that, I am just suggesting a different applicationID for each key type. (Similar to how variations are handled with the existing password application type)

While I'm at it, I'm also suggesting some information about how additional and mixed key types should be handled.

Anyway, just keen to get some input on a way forward, can add some additional test vectors and stuff once others have provided some input about a preferred way forward.

@odudex
Copy link

odudex commented Sep 15, 2025

The inclusion of GPG ECC key specifications also interests me. I'm developing tools to derive secp256k1 GPG key material on Krux
My implementation differs slightly, as it relies on a PC-based coordinator app to handle metadata and prepare files for GPG signing.

Although the approach is different from @3rdIteration’s, he confirmed that the results are compatible, currently using the RSA derivation path in preliminary tests.

My long-term goal is to also support the creation of encryption subkeys.

Having two independent projects producing different, yet compatible implementations creates an excellent opportunity to include ECC keys in the BIP85 specifications.

@3rdIteration
Copy link
Contributor Author

I didn't include it in the body of the wiki, but I would suggest that basically this approach would mean that the 8283 prefix for applications types is reserved for GPG related keys

@jonatack jonatack added Proposed BIP modification Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified labels Sep 17, 2025
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.

Pinging @akarve and @scgbckbone for feedback, BIP85 is in use and in Final status (#1676), so no potentially breaking changes may be introduced.

===GPG Keys===

Application number: 828365'
Application number is dependant on the key type.
Copy link
Contributor

@akarve akarve Oct 6, 2025

Choose a reason for hiding this comment

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

I would recommend a single application number and then extend the path for different key types. That's more consistent with the rest of BIP85:

{same_app_number_for_all}/{key_type}/{key_bits}/{key_index}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would be the neatest solution


- Main key <code>m/83696968'/828365'/{key_bits}'/{key_index}'</code>
- Sub keys: <code>m/83696968'/828365'/{key_bits}'/{key_index}'/{sub_key}'</code>
- Primary key <code>m/83696968'/{key_type}'/{key_bits}'/{key_index}'</code>
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and elsewhere these list nested code blocks don't render as expected (... > View file)

Copy link
Contributor

Choose a reason for hiding this comment

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

In the description please motivate the need and intent of sub keys. It's not immediately obvious why this is needed above and beyond the standard key index.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's mostly about trying to follow the process elsewhere in BIP85 where a given derivation path will always map to a certain use case 1:1.

One solution to all of this is to basically just have BIP85 use the same derivation path for both primary keys and subkeys, regardless of the key algo being used.

- sub_key <code>2'</code> is usually used as SIGNATURE key

Note on timestamps:
Additional subkeys can be added to a primary key, including keys of a different key type, following the role pattern defined above, but the key_index MUST be incremented with each subkey.
Copy link
Contributor

@akarve akarve Oct 6, 2025

Choose a reason for hiding this comment

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

As for mixing types I would discourage that unless there's a really good reason. For one thing are we nesting derivation paths at that point? A worked example of mixed subkeys would help.

Copy link
Contributor

Choose a reason for hiding this comment

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

the use-case for this is basically scenarios where someone may want a strong and long-lived primary key that for their identity, but use different keys for the actual signing and stuff. (Whether due to limitations on smartcards, etc) For example, someone may have an ED25119 primary key and have subkeys use something like P256 or RSA subkeys for compatibility reasons.

reasonable enough. at that point why not turtles all the way down, e.g. the following or something like it?

{same_app_number_for_all}/
{key_type}/{key_bits}/key_index}/
{key_type}/{key_bits}/sub_key_index}/
...

Copy link
Contributor Author

@3rdIteration 3rdIteration Oct 6, 2025

Choose a reason for hiding this comment

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

the use-case for this is basically scenarios where someone may want a strong and long-lived primary key that for their identity, but use different keys for the actual signing and stuff. (Whether due to limitations on smartcards, etc) For example, someone may have an ED25119 primary key and have subkeys use something like P256 or RSA subkeys for compatibility reasons.

reasonable enough. at that point why not turtles all the way down, e.g. the following or something like it?

{same_app_number_for_all}/
{key_type}/{key_bits}/key_index}/
{key_type}/{key_bits}/sub_key_index}/
...

I think that would be a better approach overall, to have an app number to signify GPG keys generally and then an additional level of derivation for key type, but this would break compatibility with existing RSA implementations. (Even though it doesn't seem to me that there are any in the wild) The reality is that new key types will likely continue to be added to GPG, so the ability to add more in a neat way would be better long term. (With the rational for different applications for each key type being related to respecting the status of this BIP as 'Final')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As for mixing types I would discourage that unless there's a really good reason. For one thing are we nesting derivation paths at that point? A worked example of mixed subkeys would help.

Even with that example, it's not really an issue to just assert that mixed key types are beyond the scope of the spec. (As most people will just use the same key type for everything)

@akarve
Copy link
Contributor

akarve commented Oct 6, 2025

@3rdIteration howdy, feedback provided. please also do a patch entry to the changelog.

* DERIVED PWD=_s`{TW89)i4`

===RSA===
===GPG Keys===
Copy link
Contributor

Choose a reason for hiding this comment

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

Definitely need test vectors and expected outputs for all applications and sub applications. I can also add it to the reference implementation but won't be able to get to that right away. PRs welcome. https://github.com/akarve/bipsea

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to work up some test vectors and a reference implementation once there is some agreement on a general direction :)

3rdIteration and others added 2 commits October 6, 2025 14:28
Co-authored-by: Jon Atack <jon@atack.com>
Co-authored-by: Aneesh Karve <akarve@users.noreply.github.com>
@jonatack
Copy link
Member

jonatack commented Oct 14, 2025

Hi @3rdIteration, mind addressing the remaining feedback here?

Edit: per #1967 (comment), when in doubt might be most prudent to go with option c?

@jonatack jonatack added the PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author label Oct 14, 2025
@3rdIteration
Copy link
Contributor Author

3rdIteration commented Oct 14, 2025

Are there any that aren't addressed? (In terms of the other PR, I agree that option C makes the most sense)

It also gives more scope to make some changes that make this neater overall, even if it deviates from the original RSA-Only implementation.

@jonatack
Copy link
Member

Are there any that aren't addressed? (In terms of the other PR, I agree that option C makes the most sense)

@3rdIteration the changelog update, not sure for the rest of @akarve's feedback

@jonatack
Copy link
Member

#1967 has been merged, so this can now be rebased to master and the changelog updated.

@3rdIteration
Copy link
Contributor Author

#1967 has been merged, so this can now be rebased to master and the changelog updated.

Yep, will do in the next week or two.

I'll throw in some test vectors while I'm at it as well :)

@murchandamus
Copy link
Member

Hi @3rdIteration, are you still working on this PR?

@3rdIteration
Copy link
Contributor Author

Hi @3rdIteration, are you still working on this PR?

It's still on my list and just got delayed, but still very keen.

Will look at it next week :)

Copilot AI review requested due to automatic review settings February 28, 2026 03:24
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the BIP-0085 specification’s OpenPGP/GPG key derivation section to support additional (ECC) key types, aiming to make deterministic GPG key generation practical on low-power devices.

Changes:

  • Replaces the prior RSA-focused GPG section with a generalized “GPG Keys” section and introduces a key_type path component.
  • Adds a key type table covering RSA plus several ECC curve families.
  • Adds a new 2.0.0 changelog entry describing the ECC additions and the GPG section restructure.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 367 to 380
The derivation path format is: <code>m/83696968'/828365'/{key_type}'/{key_bits}'/{key_index}'</code>

The RSA key generator should use BIP85-DRNG as the input RNG function.
The key_type values are defined as follows:

===RSA GPG===
{|
!OpenPGP Key Type
!key_type value
|-
| RSA
| 0'
|-
| ECC(Curve25519)
| 1'
|-
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

The derivation path format already hardens the key_type component ("{key_type}'"), but the table also defines key_type values with a trailing apostrophe (e.g., "0'", "1'"). This is ambiguous and can lead to double-hardening / inconsistent interpretation across implementations. Define key_type as a plain integer (0..4) and state that the path component is always hardened, or remove the apostrophe from the path placeholder.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@copilot code review[agent] Can you add a note for the ECC key_types that mentions which key_bits lengths are valid

Comment on lines 367 to 388
The derivation path format is: <code>m/83696968'/828365'/{key_type}'/{key_bits}'/{key_index}'</code>

The RSA key generator should use BIP85-DRNG as the input RNG function.
The key_type values are defined as follows:

===RSA GPG===
{|
!OpenPGP Key Type
!key_type value
|-
| RSA
| 0'
|-
| ECC(Curve25519)
| 1'
|-
| ECC(secp256k1)
| 2'
|-
| ECC(NIST)
| 3'
|-
| ECC(Brainpool)
| 4'
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

The spec introduces ECC key types but does not define how {key_bits} selects a specific curve/algorithm for each key_type (e.g., which NIST curve corresponds to which key_bits, and similarly for Brainpool). For fixed-size curves like Curve25519 and secp256k1, it’s also unclear what {key_bits} is expected to be (and whether other values are invalid). Please add explicit, deterministic mappings/constraints so independent implementations derive identical keys for the same path.

Copilot uses AI. Check for mistakes.
** sub_key <code>1'</code> is used as the AUTHENTICATION key
** sub_key <code>2'</code> is usually used as SIGNATURE key

All subkeys SHOULD use the same key_type as the primary key. Mixed key types across primary and subkeys are out of scope for this specification. Additional subkeys may be added following the same role pattern, incrementing the sub_key index.
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

For Curve25519, OpenPGP typically uses different algorithms for different capabilities (e.g., Ed25519 for certify/sign/auth vs X25519/Curve25519 ECDH for encryption). The current text says all subkeys SHOULD use the same key_type, but doesn’t specify the required per-capability algorithm mapping within that key_type. Please clarify the exact OpenPGP public-key algorithm/curve used for primary vs encryption/auth/sign subkeys under key_type=Curve25519 to avoid incompatible key material generation.

Suggested change
All subkeys SHOULD use the same key_type as the primary key. Mixed key types across primary and subkeys are out of scope for this specification. Additional subkeys may be added following the same role pattern, incrementing the sub_key index.
All subkeys SHOULD use the same key_type (i.e. the same underlying curve family) as the primary key. Mixed key types across primary and subkeys are out of scope for this specification. For <code>key_type = Curve25519</code>, implementations MUST generate OpenPGP keys as follows: the primary CERTIFY key and any SIGNATURE or AUTHENTICATION subkeys MUST use Ed25519 (OpenPGP EdDSA with curve Ed25519), while ENCRYPTION subkeys MUST use X25519/Curve25519 ECDH (OpenPGP ECDH with curve Curve25519). Additional subkeys may be added following the same role pattern, incrementing the sub_key index.

Copilot uses AI. Check for mistakes.
|-
|}

The RSA key generator should use BIP85-DRNG as the input RNG function. Likewise, any ECC key types over 256 bits should use BIP85-DRNG.
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

The guidance on when to use BIP85-DRNG for ECC ("over 256 bits") doesn’t align with the earlier definition of BIP85-DRNG being needed when more than 64 bytes of random input are required. Consider specifying DRNG usage based on the keygen’s required randomness/bytes (or explicitly justify why >256-bit ECC needs DRNG), otherwise implementers may diverge on whether to use DRNG for common curves like P-384.

Suggested change
The RSA key generator should use BIP85-DRNG as the input RNG function. Likewise, any ECC key types over 256 bits should use BIP85-DRNG.
The RSA key generator should use BIP85-DRNG as the input RNG function. Likewise, any ECC key types whose key generation requires more than 64 bytes of random input should use BIP85-DRNG.

Copilot uses AI. Check for mistakes.

<b>Note on timestamps:</b>

The resulting RSA key can be used to create a GPG key where the creation date MUST be fixed to unix Epoch timestamp 1231006505 (the Bitcoin genesis block time <code>'2009-01-03 18:05:05'</code> UTC) because the key fingerprint is affected by the creation date (Epoch timestamp 0 was not chosen because of legacy behavior in GNUPG implementations for older keys). Additionally, when importing sub-keys under a key in GNUPG, the system time must be frozen to the same timestamp before importing (e.g. by use of <code>faketime</code>).
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

This section now covers multiple GPG key types, but the timestamp note still refers specifically to "The resulting RSA key". Either generalize the statement to apply to all OpenPGP keys derived here (including ECC), or explicitly scope the timestamp requirement to RSA-only if ECC behavior differs.

Copilot uses AI. Check for mistakes.
Application number: 828365'

The derivation path format is: <code>m/83696968'/828365'/{key_bits}'/{key_index}'</code>
The derivation path format is: <code>m/83696968'/828365'/{key_type}'/{key_bits}'/{key_index}'</code>
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

Changing the GPG derivation path from the previously-specified RSA format (which did not include {key_type}) will change the derived material for existing paths and break interoperability with any current implementations/vectors. If backward compatibility is a goal, consider keeping the legacy RSA path intact and allocating new application numbers for ECC (or otherwise defining a legacy/compat mode) rather than inserting a new path level under the same application number.

Copilot uses AI. Check for mistakes.
Comment on lines 476 to 480
* ECC GPG key types for OpenPGP: Curve25519 (key_type 1'), secp256k1 (key_type 2'), NIST (key_type 3'), Brainpool (key_type 4')

====Changed====

* GPG key section restructured to use a single application number (828365') with <code>key_type</code> as a path level component, replacing separate per-type application numbers
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

The 2.0.0 changelog entry says the GPG section now uses a single application number with key_type, "replacing separate per-type application numbers", which doesn’t match the earlier changelog/history in this document (it previously introduced an RSA application at 828365'). Also, this conflicts with the PR description’s stated approach of using different application IDs per key type. Please reconcile the changelog text (and/or the spec approach) so the intended compatibility strategy is clear.

Copilot uses AI. Check for mistakes.

==Changelog==

===2.0.0===
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

Changelog entries below include dates (e.g., "1.3.0 (2024-10-22)"), but the new "2.0.0" entry has no date. Add a date (or follow whatever versioning/date convention the document uses) to keep the changelog consistent.

Suggested change
===2.0.0===
===2.0.0 (2024-11-01)===

Copilot uses AI. Check for mistakes.
@3rdIteration 3rdIteration force-pushed the BIP85-ExpandCurveTypes branch from c080182 to 86a64f1 Compare February 28, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author Proposed BIP modification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants