-
Notifications
You must be signed in to change notification settings - Fork 118
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
ARC60 (new) - arb data signing AUTH, random, fido2, caip122 #313
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: ehanoc <[email protected]>
Signed-off-by: ehanoc <[email protected]>
Signed-off-by: ehanoc <[email protected]>
Signed-off-by: ehanoc <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @ehanoc. I really hope that this iteration of ARC-60 finally makes it past the finish line!
| `data` | `string` | string representing the content to be signed for the specific `Scope`. This can be an encoded JSON object or any other data. It **MUST** be presented to the user in a human-readable format. | | ||
| `signer` | `bytes` | public key of the signer. This can the public related to an Algorand address or any other Ed25519 public key. | | ||
| `domain` | `string` | This is the domain requesting the signature. It can be a URL, a DID, or any other identifier. It **MUST** be presented to the user to inform them about the context of the signature. | | ||
| `requestId` | `string` | It is used to identify the request. It **MUST** be unique for each request. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can uniqueness be enforced? Would a wallet have to keep track of past requestIds
? That sounds impractical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not in reality, but tracking uuids for instance is pretty straightforward.
| `signer` | `bytes` | public key of the signer. This can the public related to an Algorand address or any other Ed25519 public key. | | ||
| `domain` | `string` | This is the domain requesting the signature. It can be a URL, a DID, or any other identifier. It **MUST** be presented to the user to inform them about the context of the signature. | | ||
| `requestId` | `string` | It is used to identify the request. It **MUST** be unique for each request. | | ||
| `authenticatedData` | `bytes` | It **MUST** include, at least, the `sha256` hash of the `domain` requesting a signture. The wallet **MUST** do an integrity check on the first 32 bytes of `authenticatedData` to match the hash. It **COULD** also include signature counters, network flags or any other unique data to prevent replay attacks or to trick user to sign unrelated data to the scope. The wallet **SHOULD** validate every field in `authenticatedData` before signing. Each `Scope` **MUST** specify if `authenticatedData` should be appended to the hash of the `data` before signing. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec states that wallets SHOULD validate every field in authenticatedData
. Given that authenticationData
is basically a binary blob, how should a wallet know what to validate? Who defines the binary serialization format of authenticationData
? Every individual scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes; can be scope specific.
The most important are the first 32 bytes that must be the SHA256(domain); also calculated by the wallet; ensures that the domain you are "talking" to is the same one you expect to be "connected" to and sending you data to sign.
All the remaining bytes, are context specific and depending on the scope validations could vary
|
||
Summarized signing process for `AUTH` scope: | ||
```plaintext | ||
EdDSA(SHA256(data) + SHA256(authenticatedData)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In previous iterations of ARC60 the requirement was stated that it must be possible to sign exactly the input data (e.g., a 32 byte hash). Here the input data is hashed before it is signed, which means it's not possible to sign exactly the input data. Is that requirement no longer necessary? Because that'd be great and would simplify things a lot compared to the past.
One concern that I have is that the resulting hash might still by chance (or by design from a bad actor) contain a Program
prefix, which makes the hash a valid logic signature. In the worst case, a logicsig could be crafted that takes control of the user's account.
D13 explained that a future version of this ARC will explicitly prevent Program
prefixes, which is great.
Another option is to add a fixed prefix like so: EdDSA('arc60' + SHA256(data) + SHA256(authenticatedData))
. Since we already modify the user's input before signing, I don't see a problem with adding a prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"prefix" are a really bad practice in my opinion; prevents any subject to validate signature crafted in algorand wallets / dapps and generically verify them without conforming to our own custom way of doing things.
We can add a check to prevent "Program"; sure. But the chances of you finding a hash with valid 7 bytes (Program) + the remainder of the program are extremely low.
Still, i can add a check to reject "Program"
| --- | --- | --- | | ||
| `data` | `string` | string representing the content to be signed for the specific `Scope`. This can be an encoded JSON object or any other data. It **MUST** be presented to the user in a human-readable format. | | ||
| `signer` | `bytes` | public key of the signer. This can the public related to an Algorand address or any other Ed25519 public key. | | ||
| `domain` | `string` | This is the domain requesting the signature. It can be a URL, a DID, or any other identifier. It **MUST** be presented to the user to inform them about the context of the signature. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must the domain also be matched against WalletConnect, for example? This means, if a user is connected to dapp example.com
through her wallet and the dapp asks the wallet to sign a payload, does the wallet have to check that example.com
is the domain in StdSignData
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup :)
const authenticationData: Uint8Array = new Uint8Array(createHash('sha256').update(caip122Request.domain).digest()) | ||
|
||
const signData: StdSigData = { | ||
data: Buffer.from(JSON.stringify(caip122Request)).toString('base64'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is JSON.stringify
deterministic? Since JSON objects are unordered, we might have to resort to something like canonical JSON to derive a deterministic byte-array from JSON data suitable for signing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point. Yes, it should be deterministic JSON
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated @k13n
Signed-off-by: ehanoc <[email protected]>
on custom (Algorand ecosystem internal usage) AUTH scope and custom auth message, this is 100% good to go and flawless, IMHO.
On the other hand, now that we have extension mechanisms in ARCs, may I suggest this ARC-60 to be limited only to custom and random arbitrary data signing (for which it is complete and good to go final IMHO) and then in the future after we add successful verification with standard well-known libraries on each sub-scope, we add those as extensions to this ARC. E.g. for Fido2/WebAuthn, for OAuth2/OIDC, and for simple JWT/JWS. |
NEW ARC60 Proposal
This new proposal achieves the following:
For the purpose of AUTH (at least for now) sign truly arbitrary data without colliding with known data structures (TXs, LSIGs, etc)
Compatibility with several authentication mechanisms. i.e custom auth mechanisms, FIDO2 / Passkeys / CAIP-122
Takes into consideration the implementation constraints of hardware wallets
Reference Implementation is available