# \[Request for Feedback\] API design of "K1"

**URL:** <https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027>\
**Category:** Using Swift\
**Tags:** cryptokit, api-design, api, cryptography, ecc\
**Created:** [March 28, 2023, 9:15am UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027 "2023-03-28T09:15:29Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [March 28, 2023, 9:15am UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/1 "2023-03-28T09:15:29Z")

</div>

I've created and I maintain a [Swift library called _K1_](https://github.com/Sajjon/K1), offering Elliptic Curve Cryptography over the curve `secp256k1` with API's that are heavily inspired by CryptoKit.

K1 vendors two fundamentally different signature schemes:

- Schnorr signatures (more modern)
- ECDSA signatures (older)

And for ECDSA it offers two different variants:

- ECDSA from which user can recover the public key from the signature (with message that was signed)
- ECDSA which does not offer public key recoverability (older and more common)

Apart from signing K1 also vendors key exchange (ECDH).

I've recently created an unmerged PR which changes the namespace / API structure from 3 top level namespaces to 4.

In main branch I have these 3 namespaces, one per feature:

- `K1.KeyAgreement`
- `K1.Schnorr`
- `K1.ECDSA`

I want feedback on if you prefer the 3 above, or these 4 below - [presented in my unmerged PR](https://github.com/Sajjon/K1/pull/34):

- `K1.KeyAgreement`
- `K1.Schnorr`
- `K1.ECDSAWithKeyRecovery `
- `K1.ECDSA `

I.e. I've flattened the two variants of `ECDSA` into two distinct namespaces. I put some more context and rational into the description of the PR linked to above.

What do you think makes the most sense and results in the most clear API?

The reason why all features are put inside the root level namespace `K1` is to be symmetric with CryptoKit's APIs which put all structs and operations relating to a curve under a namespace for that curve, see e.g. [`P256`](https://developer.apple.com/documentation/cryptokit/p256), the namespace for the Elliptic Curve `P256`.

---

<div class="post-metadata">

**Author:** ![rayx](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rayx/32/12204_2.png) [@rayx](https://forums.swift.org/u/rayx)\
**Post date:** [March 28, 2023, 11:38am UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/2 "2023-03-28T11:38:57Z")

</div>

I think both are good. If I have to choose one, it's maybe the new approach, due to the same reason you mentioned in the commit message - I can just use the API in ECDSA namespace and don't bother to figure out what the longer namespace means (this is the first time I read about recovering public key from signature). In contrast, in the current approach I have to figure out what recoverable or nonrecoverable means to choose the API (that said, it's not difficult to understand the concept).

BTW, did you consider the following approach? (I'm not an expert on API design, I just happened to think of it).

- K1.KeyAgreement
- K1.Schnorr
- K1.ECDSA
  - K1.ECDSA.KeyRecovery

It resolves the drawback you mentioned in your commit message. You can define the common structs in K1.ECDSA. This approach has one drawback in coding: one may think he defined a type X in K1.ECDSA.KeyRecovery name space but he didn't and hence misuse a type with the same name in parent namespace.

Another possible drawback is the implication that the recoverable variation is an enhancement (or based on) the non-recoverable variation. BTW, you said they are two variations. I wonder what exactly the difference is? Do they use different parameters or algorithm? I did a quick search but none of the materials I found suggested that recovering public key needs a different algorithm or parameter.

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [March 28, 2023, 6:46pm UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/3 "2023-03-28T18:46:12Z")

</div>

> [@rayx](#):
>
> BTW, did you consider the following approach?

Yes I did, but I discarded it because of asymmetry. The two variant are suddenly in different "hierarchy depths", which is never the case for CryptoKit. But maybe it is not that bad, maybe it is the way to go, thanks for bring it up! I will consider it some more!

> [@rayx](#):
>
> BTW, you said they are two variations. I wonder what exactly the difference is? Do they use different parameters or algorithm? I did a quick search but none of the materials I found suggested that recovering public key needs a different algorithm or parameter.

Good question, K1's underlying implementation is Bitcoin Core's `libsecp256k1` and (non recovery) ECDSA is declared in [`secp256k1.h` as `secp256k1_ecdsa_sign`](https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1.h#L664), where as the recovery variant is declared in [`secp256k1_recovery.h` as `secp256k1_ecdsa_sign_recoverable`](https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_recovery.h#L84) and the source file of recovery is the ["modules" folder](https://github.com/bitcoin-core/secp256k1/blob/master/src/modules/recovery/main_impl.h#L123). Further more they produce different structs, `secp256k1_ecdsa_signature *` vs `secp256k1_ecdsa_recoverable_signature *`.

A recoverable ECDSA signature can always be normalized to a (non recoverable) ECDSA signature.

Content wise, they only different in size, recoverable signatures contain _one single_ additional byte, sometimes called `V` or `RecoveryID` (which can be represented as an enum with values 0, 1, 2, 3).

I think what you might be getting at...? Did you perhaps wanna propose using a single namespace for ECDSA, but with either two `sign` methods, or a single `sign` method but accepting an argument `isRecoverable: Bool`? I actually used the same namespace with two different `sign` methods earlier. But it became quite confusing because each `sign` method has itself 3 variants, [see documentation](https://swiftpackageindex.com/sajjon/k1/0.3.6/documentation/k1/k1/ecdsa/nonrecoverable/privatekey)

Thus exposing users with 6 methods instead of 3 is really less than ideal.

So let's explore the ability of using still one sign method - just 3 variants, but letting `isRecoverable: Bool` be an argument. Suddenly the `recoveryID` becomes an optional in a merge of `K1.ECDSA.NonRecoverable.Signature` and `K1.ECDSA.Recoverable.Signature`, which makes suddenly the very straightforward serialization and deserialization API's of `K1.ECDSA.NonRecoverable.Signature` less easy to understand and use. The reason for this is that **only** `K1.ECDSA.NonRecoverable.Signature` is well defined, with standards.

Recoverable signature have not real standardized serialization format. Content wise, as mentioned above, apart from `R` and `S` scalars, the contain a single `V` byte. Internally, `libsecp256k1` stores the signature as `leR||leS||v` where `leR` and `leS` denotes inverted R, meaning little endian (at least on my M1). [See documentation of `libsecp256k`](https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_recovery.h#L13):

> The exact representation of data inside is implementation defined and not guaranteed to be portable between different platforms or versions

So to export it we use `secp256k1_ecdsa_recoverable_signature_serialize_compact`, [which converts to big endian scalars](https://github.com/bitcoin-core/secp256k1/blob/master/src/modules/recovery/main_impl.h#L69): `R || S || V`. But this is a non standardized (outside of Bitcoin) format, and Ethereum uses `V || R || S`.

Oh I see now that I've forgotten to add documentation to [`K1.ECDSA.Recoverable.Signature.Compact`](https://github.com/Sajjon/K1/blob/main/Sources/K1/K1/ECDSA/ECDSASignatureRecoverable.swift#L144), which is my swift variant of the output of `secp256k1_ecdsa_recoverable_signature_serialize_compact`.

Thus the API of the recoverable Signature is less clear than the API of the non recoverable one. which is an argument for keeping them apart. I could return a `protocol` but I think it less than ideal? If I know I do not care about recoverable signatures, I'd prefer it if I get an existential, with the clean API.

Hmm... what do you think?

---

<div class="post-metadata">

**Author:** ![rayx](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rayx/32/12204_2.png) [@rayx](https://forums.swift.org/u/rayx)\
**Post date:** [March 29, 2023, 3:39am UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/4 "2023-03-29T03:39:20Z")

</div>

Thanks for the detailed introduction.

> [@Sajjon](#):
>
> Did you perhaps wanna propose using a single namespace for ECDSA, but with either two `sign` methods, or a single `sign` method but accepting an argument `isRecoverable: Bool`?

Yes, the latter was what I had in mind. But after reading your reply, I doubt if it's a good idea. I think it's important to isolate the two sets of API as much as possible, because:

a) It will be easy for you to maintain  
b) It will be easy for user to use (it would be confusing when user import ECDSA module but see parameters/properties/types related to public key recovery which user doesn't need at all).

It would be great if it's possible to work out a single and easy-to-use API. But it would require a lot of addtional work on you side even if it's possible. Also that API would be more different than the underlying C API than your current API.

So I personaly suggest to use the approach in your PR. I think it's more like how the underlying C API is organized (according to the header files [here](https://github.com/bitcoin-core/secp256k1/tree/master/include), recovery APIs are considered a special case and isn't exposed in the secp256k1.h).

Regarding the drawback you mentioned in the PR, I can't think out a better approach than to put the shared structs in ECDSA module. I think it's fine (the C API resolves this issue by putting all common types in secp256k1.h).

---

<div class="post-metadata">

**Author:** ![rayx](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rayx/32/12204_2.png) [@rayx](https://forums.swift.org/u/rayx)\
**Post date:** [March 29, 2023, 12:11pm UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/5 "2023-03-29T12:11:02Z")

</div>

> [@Sajjon](#):
>
> So let's explore the ability of using still one sign method - just 3 variants, but letting `isRecoverable: Bool` be an argument. Suddenly the `recoveryID` becomes an optional in a merge of `K1.ECDSA.NonRecoverable.Signature` and `K1.ECDSA.Recoverable.Signature`, which makes suddenly the very straightforward serialization and deserialization API's of `K1.ECDSA.NonRecoverable.Signature` less easy to understand and use. The reason for this is that **only** `K1.ECDSA.NonRecoverable.Signature` is well defined, with standards.

I don't find the serialization API of non recoverable signature in your code, but I suppose you meant the two kinds of signatures have different serialization API (I see in your code that the recoverable one takes an enum because it doesn't have a standard format) and the difference makes it difficul to use a single struct to represent both signatures?

If so, below is a possible solution.

Assumptions:

- just one `sign` method (with 3 variants), and letting `isRecoverable: Bool` be an argument
- The name space has a hierarchy: ` K1.ECDSA` and `K1.ECDSA.KeyRecovery` (note: it works to just use a single name space and put recoverable related API in it. I think both are fine.)

API Design:

- The `sign` method returns a `Signature` struct
- The `Signature` struct has APIs for common operations (e.g. `verify`).
- Since most users don't use recovery featture, this is API they use.
- For users who would like to recover public key, they need to convert the `Signature` struct to `RecoverableSignature`, as folllows.

```swift
import K1.ECDSA
import K1.ECDSA.KeyRecovery

if let recoverableSig = sig.recoverable() {
    let key = recoverableSig.recover()
}

```

With this approach both kinds of signature share a common API (the `Signature` struct and its methods), but recovery related API is invisible to users who don't use the API (the only exception is the `isRecoverable` option, but I think it's fine and it can have a default `false` value).

* * *

EDIT: I just realized the design focuses on how to hide the recovery related APIs but doesn't deal with the different serialization API. How about this?

Assumptions:

- Non-recoverable signature's serialization API takes no arguments (because it uses a standard format).

API Design:

- The `Signature` struct has `serialize` api. If the signature is non-recoverable, it uses the standard format; otherwise it uses a default one (e.g. the one used by bitcoin).
- If user want to use a different format for recoverable signature, he needs to call `RecoverableSignature.serialize()` instead, which takes an enum.

```swift
import K1.ECDSA
import K1.ECDSA.KeyRecovery

if let recoverableSig = sig.recoverable() {
    let key = recoverableSig.serialize(.vrs)
}

```

I think this approach might be OK for this specific issue. But it feels like a workaround because it can't deal with the general version of the issue: what if both signatures have a "common" operation but takes different argument? I think the root cause is that in that case it's not really a common operation and hence shouldn't be put in `Signature` struct which serves as common interface. However, that would mean, to deal with a general issue like this, we'll have to define the name space hierarchy as:

- K1.ECDSA
  - K1.ECDSA.NonRecoverable
  - K1.ECDSA.Recoverable

Hopefully you don't such cases to deal with. Otherwise I don't suggest to use the approach I proposed.

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [March 29, 2023, 6:07pm UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/6 "2023-03-29T18:07:33Z")

</div>

> [@rayx](#):
>
> I don't find the serialization API of non recoverable signature in your code, but I suppose you meant the two kinds of signatures have different serialization API (I see in your code that the recoverable one takes an enum because it doesn't have a standard format) and the difference makes it difficul to use a single struct to represent both signatures?

Right, so `NonRecoverable.Signature` has [two computer properties](https://github.com/Sajjon/K1/blob/main/Sources/K1/K1/ECDSA/ECDSASignatureNonRecoverable.swift#L65-L85): `public var rawRepresentation: Data ` and `public var derRepresentation: Data` which are both kind of standardised formats, matching **exactly** the [API of CryptoKit's `P256.Signing.ECDSASignature`](https://developer.apple.com/documentation/cryptokit/p256/signing/ecdsasignature).

And yes as you said "the difference makes it difficult to use a single struct to represent both", I don't want users to think they can use `rawRepresentation` for Recoverable ones, because there is no standard for it, which I why users of K1 today have to do `recoverableSignatures.compact().serialize(format: .rsv)` (or `format: .vrs`). Since the order of `R`, `S` and `V` is non-standardized we cannot do `derRepresentation` either... So a shared Signature type is tricky, because for Recoverable I do not want serialization using `public var rawRepresentation: Data` nor `public var derRepresentation: Data` to be available.

Thank you very much for brainstorming with me @rayx, I really really appreciate it! I feel now that keeping them as seperate Signature (as in `main` today) is the right choice.

And I'm torn behind the namespaces. `K1.ECDSA.PrivateKey` and `K1.ECDSA.KeyRecovery.PrivateKey` is not that bad, the asymmetry I mean. I will try it in a PR! And ping you once the PR is up :)

---

<div class="post-metadata">

**Author:** ![Sajjon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sajjon/32/12044_2.png) [@Sajjon](https://forums.swift.org/u/Sajjon)\
**Post date:** [April 1, 2023, 7:35am UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/7 "2023-04-01T07:35:35Z")

</div>

@rayx I've made a PR for the other variant

> <https://github.com/Sajjon/K1/pull/37>
>
> This is an alternative PR to the \[other open rename namespaces PR\](https://githu…b.com/Sajjon/K1/pull/34)
> 
> This changes
> 1. \`ECDSA.NonRecoverable\` -\> \`ECDSA\` only
> 2. \`ECDSA.Recoverable\` -\> \`ECDSA.KeyRecovery\`
> 
> Summary of API:
> 
> Given:
> \`\`\`swift
> let hashed = Data(SHA256.hash(data: Data("Hey Bob!".utf8)))
> \`\`\`
> 
> \## ECDSA
> \`\`\`swift
> let privateKey: K1.ECDSA.PrivateKey = .init()
> let publicKey: K1.ECDSA.PublicKey = privateKey.publicKey
> let signature = try privateKey.signature(for: hashed)
> publicKey.isValidSignature(signature, hashed: hashed) // true
> \`\`\`
> 
> \## ECDSA With Recovery
> \`\`\`swift
> let privateKey: K1.ECDSA.KeyRecovery.PrivateKey = .init()
> let publicKey: K1.ECDSA.KeyRecovery.PublicKey = privateKey.publicKey
> let signature = try privateKey.signature(for: hashed)
> publicKey.isValidSignature(signature, hashed: hashed) // true
> \`\`\`
> 
> \## Schnorr (unchanged)
> \`\`\`swift
> let privateKey: K1.Schnorr.PrivateKey = .init()
> let publicKey: K1.Schnorr.PublicKey = privateKey.publicKey
> let signature = try privateKey.signature(for: hashed)
> publicKey.isValidSignature(signature, hashed: hashed) // true
> \`\`\`

vs

> <https://github.com/Sajjon/K1/pull/34>
>
> This PR changes two two namespaces:
> \`K1.ECDSA.Recoverable\` and \`K1.ECDSA.NonRec…overable\` into \`K1.ECDSAWithKeyRecovery\` and just \`K1.ECDSA\` respectively. 
> 
> The rationale is:
> \* This flattens the otherwise nested structure \`ECDSA\` (which was the namespace for \`Recoverable\` and \`NonRecoverable\` which was kind of asymmetric w.r.t. \`K1.Schnorr\`.
> \* \_Assumption\_: Most user are probably not in need of ECDSA signatures that do offer recoverability of public key, therefor being able to use the much shorter namespace \`K1.ECDSA\` instead of \`K1.ECDSA.NonRecoverable\` is preferred. 
> 
> Drawback:
> Both ECDSA variants have two structs with options used for signing and validation, named both before this PR and after this PR (i.e. unchanged) \`K1.ECDSA.SigningOptions\` and \`K1.ECDSA.ValidationOptions\`, and it before this PR it was hiearcally correct that these structs was defined in the \_shared namespace\_ \`ECDSA\`. If this PR were to be merged it is less elegant that it looks like these structs belong to the non recoverable ECDSA variant, since it is used by both. However this is mostly a detail.
> 
> I'm torn between what the best API... Unfortunately swift-crypto has not solved this particular problem of two variant for the same signature scheme, so I can't look for answer there (which I've otherwise done a lot when designing the API of this library). 
> 
> 
> Summary of API:
> 
> Given:
> \`\`\`swift
> let hashed = Data(SHA256.hash(data: Data("Hey Bob!".utf8)))
> \`\`\`
> 
> \## ECDSA
> \`\`\`swift
> let privateKey: K1.ECDSA.PrivateKey = .init()
> let publicKey: K1.ECDSA.PublicKey = privateKey.publicKey
> let signature = try privateKey.signature(for: hashed)
> publicKey.isValidSignature(signature, hashed: hashed) // true
> \`\`\`
> 
> \## ECDSA With Recovery
> \`\`\`swift
> let privateKey: K1.ECDSAWithKeyRecovery.PrivateKey = .init()
> let publicKey: K1.ECDSAWithKeyRecovery.PublicKey = privateKey.publicKey
> let signature = try privateKey.signature(for: hashed)
> publicKey.isValidSignature(signature, hashed: hashed) // true
> \`\`\`
> 
> \## Schnorr (unchanged)
> \`\`\`swift
> let privateKey: K1.Schnorr.PrivateKey = .init()
> let publicKey: K1.Schnorr.PublicKey = privateKey.publicKey
> let signature = try privateKey.signature(for: hashed)
> publicKey.isValidSignature(signature, hashed: hashed) // true
> \`\`\`

They are identical in `Schnorr`, `KeyAgreement` and `ECDSA` (non recoverable) and differ only for _recoverable_ ECDSA, with `ECDSA` for both looking like this:

```swift
let privateKey: K1.ECDSA.PrivateKey = .init()
let publicKey: K1.ECDSA.PublicKey = privateKey.publicKey
let signature: K1.ECDSA.Signature = try privateKey.signature(for: hashed)
publicKey.isValidSignature(signature, hashed: hashed) // true

```

And here are the shape of the different recoverable ECDSA API, where the first variant uses a _distinct_ namespace as previously discussed, and the second variant uses a _nested_ namespace (I've explicitly declared variables with their types so we can see them spelled out):

```swift
let privateKey: K1.ECDSAWithKeyRecovery.PrivateKey = .init()
let publicKey: K1.ECDSAWithKeyRecovery.PublicKey = privateKey.publicKey
let signature: K1.ECDSAWithKeyRecovery.Signature = try privateKey.signature(for: hashed)
publicKey.isValidSignature(signature, hashed: hashed) // true

```

vs

```swift
let privateKey: K1.ECDSA.KeyRecovery.PrivateKey = .init()
let publicKey: K1.ECDSA.KeyRecovery.PublicKey = privateKey.publicKey
let signature: K1.ECDSA.KeyRecovery.Signature = try privateKey.signature(for: hashed)
publicKey.isValidSignature(signature, hashed: hashed) // true

```

Which one do you like best?

---

<div class="post-metadata">

**Author:** ![rayx](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rayx/32/12204_2.png) [@rayx](https://forums.swift.org/u/rayx)\
**Post date:** [April 1, 2023, 8:13am UTC](https://forums.swift.org/t/request-for-feedback-api-design-of-k1/64027/8 "2023-04-01T08:13:40Z")

</div>

I like the first (the one in your original PR). The symmetry in the name space makes it more clear they are two sets of APIs. I think the second one is better if a sigle signature struct is reused for both unrecoverable and recoverable (as discussed above it's infeasible). Just my thoughts.
