Help with ed25519 from cryptonite for tor ADD_ONION

Hello! I’m feeling out of my depths here so I thought I would ask for help. I am trying to spin up an onion address that is constant from another key I am storing.

Something like this does seem to work to get a constant key

import Crypto.PubKey.Ed25519 (secretKey)  

let CryptoPassed ed = secretKey . SHA.hash $ kp

What the tor documentation says about the keyblob I need to create is

a “ED25519-V3” key is the Base64 encoding of the concatenation of the 32-byte ed25519 secret scalar in little-endian and the 32-byte ed25519 PRF secret.

From the cryptonite documentation it is hard to see how to get these values / what format they use (the keys underlying ByteString is only 32) and ScrubbedBytes / ByteArrayAccess seem like they can easily be misused. Am I on the right track here using Crypto.PubKey.Ed25519?

1 Like

It looks like a Tor ED25519-V3 key requires use of non-deterministic signatures, but cryptonite only exposes deterministic Ed25519 signatures. From the tor spec, right below the documentation you quoted:

[Note: The ED25519-V3 format is not the same as, e.g., SUPERCOP
ed25519/ref, which stores the concatenation of the 32-byte ed25519
hash seed concatenated with the 32-byte public key, and which derives
the secret scalar and PRF secret by expanding the hash seed with
SHA-512. Our key blinding scheme is incompatible with storing
private keys as seeds, so we store the secret scalar alongside the
PRF secret, and just pay the cost of recomputing the public key when
importing an ED25519-V3 key.]

There is a bit of cryptography history here to be known: non-deterministic signatures have been in use for the last few decades, but over time the spectre of poor randomness and nonce-misuse has raised the potential for leaking secrets, and so deterministic signatures have become preferred, as they provide a harder-to-misuse interface while providing no less security. This has caused much confusion, if the (lack or presence of) determinism is not made explicit.

2 Likes

Is signing relevant here? Aren’t we providing secret and any signing is handled by tor itself? Or is that PFR secret essentially a deterministic nonce?

Interestingly I appear to get the functionality I am looking for by just using:

formatForTor s = do
    r <- getEntropy 32 
    let keyblob = convertToBase Base64 . BS.concat $     
         [ s -- hash
         , r -- randomness 
         ]

This creates a constant onion that works . . . But I know messing around with crypto you don’t understand / misusing tor is a good way to betray the user . . . so I’m not at all comfortable

I am not familiar with the tor spec (I am just reading the same documentation as you), but it depends on how and what you are using the key for.

Determinism only affects signing, not key generation or verification - that is, all Ed25519 keys and signatures (whether deterministic or not) are valid Ed25519 keys and signatures, as to remain backwards-compatible.

So, if you are only generating the key and secret, and sending it to the server to sign things for you (which it sounds like), then what you have done is perfectly valid - they require an additional 32 bytes of entropy for which they will use to generate a nonce instead of using it directly a la standard Ed25519. So, the signatures will be different from standard Ed25519, but you will still be able to verify that it is your signature using any Ed25519 verifier.

If you specifically wish to locally check that it is specifically your signature signed with that nonce, you would have to remain aware that it is a non-standard signature and be sure to use the tor ED25519-V3 algorithm to obtain an identical signature.

However that is unlikely, and you should never reuse nonces regardless, so it is likely that what you have done is fine, especially if you are still randomizing that nonce on server startup (which I would assume unless tor otherwise specifically indicates that it needs you to preserve that random value).

Side note: The endian-ness might matter, but again, only if you are doing local / manual verification. If you’re just giving the server a key to use on your behalf, its probably fine so long as you are aware that it is a tor ED25519-V3 key.