Skip to content

Add support for custom bytes in Wax.Challenge struct #47

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

Merged
merged 6 commits into from
Apr 13, 2025

Conversation

armanm
Copy link
Contributor

@armanm armanm commented Mar 3, 2025

This PR adds support for supplying custom bytes when creating a %Wax.Challenge{} struct.

This enhancement is significant because these bytes (referred to as "cryptographic challenge" in the Passkey standard) are signed by the authenticator, with the signature returned as part of the AuthenticatorAssertionResponse.signature.

The practical value is that this signature creates a cryptographic connection between the passkey and the provided bytes, serving as strong proof of an activity. For example, in our workflow where passkey presentation is required before performing a legal action, we can use the resulting signature as irrefutable proof that a user has taken a very specific action.

@tanguilp
Copy link
Owner

tanguilp commented Mar 3, 2025

Hi, and thanks for the PR!

2 questions:

  • do you mean that's the hash of the document that is passed as a challenge? I think I already read about this use-case
  • out of curiosity, do you need to handle timestamping? If so how do you do it?

Gonna take a look at your PR today.

@armanm
Copy link
Contributor Author

armanm commented Mar 4, 2025

  • do you mean that's the hash of the document that is passed as a challenge? I think I already read about this use-case

That's correct. We intend to add the passkey signature as metadata to the PDF and therefore must calculate the document digest from the content section of the PDF.

  • out of curiosity, do you need to handle timestamping? If so how do you do it?

Sadly our solution does not include time stamping that's equivalent to how they work in signed PDFs. Ideally we would sign PDF docs using a proper document signing certificate which gives you a green tick on the signature panel in Acrobat Reader but sadly they are expensive to buy, have to be renewed every year and have hard storage requirements. There are PDF signing services too but their cost also does not scale for us.

I'm now sharing my own opinion – my understanding of the timestamp in signed PDFs is to show it happened while the certificate was valid and helps to validate the document years after its signing certificate is expired. When signing documents with passkeys, timestamps may not matter as Passkeys never expire – they either exist in an authenticator or they don't. I could be truly wrong about what I'm sharing here 😂 please don't quote me on it.

@tanguilp
Copy link
Owner

tanguilp commented Mar 4, 2025

That's correct. We intend to add the passkey signature as metadata to the PDF and therefore must calculate the document digest from the content section of the PDF.

Thing is the specification seems to explicitly prohibit that: https://www.w3.org/TR/webauthn-3/#sctn-cryptographic-challenges

We'll have to dig it further, as you can't make such changes to a security library without proper security analysis. Feel free to give me as much as input as you can (articles, libraries already supporting it...).

I could be truly wrong about what I'm sharing here 😂 please don't quote me on it.

Fair enough, it was really out of curiosity :)

@armanm
Copy link
Contributor Author

armanm commented Mar 5, 2025

I agree, you raised an important point about the WebAuthn specification and potential security implications. So I'll elaborate on the nuanced considerations:

The WebAuthn specification indeed emphasizes the importance of using unique, random challenges to prevent replay attacks in authentication scenarios. However, our use case presents an interesting edge case where we're looking to create a cryptographic binding between a user's passkey and a specific document.

While the standard session authentication flow should be done using random challenges, our approach aims to leverage the passkey as a form of document-specific signature. This differs from traditional authentication in that we're essentially using the passkey as a cryptographic attestation mechanism for a specific resource.

I know at least one project that does something similar. The banana-passkey-eoa project provides an interesting precedent for our approach. However, your point is well-taken that we cannot casually modify a security library without thorough analysis.

The other point to make is whether the proposed changes make it easy to make mistakes that were previous impossible. I don't believe is that is the case – there are existing opportunities for challenge reuse:

  • in the window between Challenge.new/1 call in the server and passing the challenge to WebAuthn in the browser the user can mishandle things or
  • manually override bytes via manipulation of a %Challenge{} struct

Thanks for referencing the other locations to update the docs. I will add them too.

EDIT

One more thought. I am not sure if Wax is currently doing anything to prevent replay attacks. I presume the challenge needs to be persisted in a more permanent basis to detect reuse 🤔

end

test "does not override bytes when provided as an option" do
assert %{bytes: "abcd"} = Challenge.new(bytes: "abcd")
Copy link
Owner

Choose a reason for hiding this comment

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

A clearer test would be:

challenge = Challenge.new(...)

assert challenge.bytes == "abcd"

@tanguilp
Copy link
Owner

@armanm any update? Did you see my comments? :)

@tanguilp tanguilp merged commit 21c5c39 into tanguilp:master Apr 13, 2025
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.

2 participants