George Kadianakis desnacked@riseup.net writes:
[ text/plain ] Hello,
we've reached the point in prop224 development where we need to pin down the precise cell formats, so that we can start implementing them. HS client authorization has been one of those areas that are not yet finalized and are still influencing cell format.
Here are some topics based on special's old notes, plus some further recent discussion with David and Yawning.
Hello again,
I read the feedback on the thread and thought some more about this. Here are some thoughts based on received feedback. A torspec branch coming soon if people agree with my points below.
I'd also like to introduce a new topic of discussion here:
d) Should we introduce the concept of stealth auth again?
IIUC the current prop224 client auth solutions are not providing all the security properties that stealth auth did. Specifically, if Alice is an ex-authorized-client of a hidden service and she got revoked, she can still fetch the descriptor of a hidden service and hence learn the uptime/presense of the HS. IIUC, with stealth auth this was not previously possible.
a) I think the most important problem here is that the authorization-key logic in the current prop224 is very suboptimal. Specifically, prop224 uses a global authorization-key to ensure that descriptors are only read by authorized clients. However, since that key is global, if we ever want to revoke a single client we need to change the keys for all clients. The current rend-spec.txt does not suffer from this issue, hence I adapted the current technique to prop224.
Please see my torspec branch `prop224_client_auth` for the proposed changes: https://gitweb.torproject.org/user/asn/torspec.git/log/?h=prop224_client_aut...
Some further questions here:
i) Should we fake the client-auth-desc-key blob in case client authorization is not enabled? Otherwise, we leak to the HSDir whether client auth is enabled. The drawback here is the desc size increase (by about 330 bytes).
Alternatively, we can try to put it in the encrypted part of the descriptor. So that we require subcredential knowledge to access the encrypted part, and then client_auth_cookie knowledge to get the encryption key to decrypt the intro points etc. I feel that this double-encryption design might be too annoying to implement, but perhaps it's worth it?
Seems like people preferred the double-encryption idea here, so that we reveal the least amount of information possible in the plaintext part of the desc.
I think this is a reasonable point since if we put the auth keys in the plaintext part of the descriptor, and we always pad (or fake clients) up to N authorized clients, it will be obvious to an HSDir if a hidden service has more than N authorized clients (since we will need to fake 2*N clients then).
---
WRT protocol, I guess the idea here is that if client auth is enabled, then we add some client authorization fields in the top of the encrypted section of the descriptor, that can be used to find the client-auth descriptor encryption key. Then we add another client-auth-encrypted blob inside the encrypted part, which contains the intro points etc. and is encrypted using the descriptor encryption key found above.
So the first layer is encrypted using the onion address, and the second layer is encrypted using the client auth descriptor key. This won't be too hard to implement, but it's also different from what's currently coded in #17238.
Do people feel OK with this?
Also, what should happen if client auth is not used? Should we fall back to the current descriptor format, or should we fake authorized clients and add a fake client-auth-encrypted-blob for uniformity? Feedback is welcome here, and I think the main issue here is engineering time and reuse of the current code.
---
Now WRT security, even if we do the double-encryption thing, and we consider an HSDir adversary that knows the onion address but is not an authorized client,we still need to add fake clients, otherwise that adversary will know the exact number of authorized clients. So fake clients will probably need to be introduced anyhow.
As David pointed out, this all boils down to how much we pad the encrypted part of the descriptor, otherwise we always leak info. If we are hoping for a leakless strategy here, we should be generous with our padding.
Let's see how much padding we need:
- Each intro point adds about 1.1k bytes to the descriptor (according to david).
- Each block of 16 authorized clients adds about 1k bytes to the descriptor (according to the format described below).
- Apart from intro points and authorized clients, the rest of the descriptor is not that heavy: less than 1k bytes (right?)
To get an average size here, let's consider a normal descriptor with 5 intro points and 16 authorized clients. With the above values, the overhead on the encrypted part of the descriptor is about 7k bytes.
To get a maximum size here, let's consider a phat descriptor that contains 20 intro points and 160 authorized clients. With the above values, the overhead on the encrypted part of the descriptor will be 32k bytes.
Hence, here are some suggestions (read: magic numbers):
- We always pad the encrypted section of the descriptor to the nearest multiple of 10k bytes (read: we pad the plaintext before we encrypt).
This should be enough to obfuscate the number of IPs and authorized clients on most hidden services out there.
- If client auth is enabled, we always include a multiple of 16 authorized clients (and fake the extra if needed) in the encrypted portion.
- We set the maximum allowed size of descriptors on HSDirs to 40k bytes. This should be enough to accomodate the fat descriptor described above.
As said, I was quite generous with the max size here. even though I doubt any actual hidden services will have such enormous descriptors, but I guess allowing those might prove to be a good idea in the future.
I don't think 40k is that much in terms of size, especially when compared to things like the microdesc-consensus which is like 1.4MB, and is required for Tor to run.
The main issue with big max sizes here, are assholes using our DHT as cloud storage. I don't think 40k is that bad in this regard, but I'm not sure how to evaluate this properly.
ii) Should we use the descriptor ASCII format to encode all the client-auth-desc-key data? Or is that weird binary format OK?
People said this is a good idea and I agree.
Here is a suggested informal format, that gets placed in the beginning of the encrypted section of the descriptor:
desc-auth-type <auth-type> desc-auth-nonce <8-byte-nonce-base64> auth-client <client-id> <iv> <encrypted-cookie>
and we always include a multiple of 16 clients. Here is how it would look like in real life:
======================================================================= desc-auth-type cookie desc-auth-nonce JMk/8BTbhB4 auth-client dkW2nw OTqqSv29icTL5TSZ5TVQ3A +PIt0D9oWlDfbpGtRxGmeA auth-client z0/MMQ dw+pwJcLk9LB/FPfxFBL3g rFX9f6WUVZVUPEwFet428Q auth-client tH/BEQ zFWL1T9H/1fyV6bYW5Ol/Q /hjW1SgF0S3BANJhZZZ/OQ auth-client 2lxnoQ ggm/IraIMQ+L56V3R0OyHQ gI9Lh5azwxcunYwyFXxJSg auth-client S88yFw S4072NBKCwbwGep7/bJv+Q j3GdtDLAiZWI2jv0z6wfNw auth-client T7KbqA zhj5vu+HghqcMBRYpsGE0Q nQQtScbK91xx1G5l5gUWYg auth-client xPROzQ /OAH9FwXOufKGmFlBkqEJQ sqzeo6n4uMnqyghv3Vj3ZA auth-client l7lqEQ iZrRNH1Lg636j32tg7XfLQ HXeqg6nViGb7H4T1dYMK9Q auth-client +9ZUZw FReeAD5/mQD03J+YiffTKw oK1q7l/4JX+P08dLKYOmlw auth-client 0L9rXg xp9hvTWcWSmLBcyLN96Msg THWHP2nLlHBWWrwECOIg+A auth-client +kJcyQ nl7dkTOA9r10jk3Bo6I5WQ sGqMNtLMOiLDVDOr9YxJAw auth-client sa5PQQ oGqjP0Ko72fopFw2aAm2QA f+enrvjiDSXGJ3t77vDfAQ auth-client m87zTQ Pl5ITgw/6nb5zJPXjl9GPA X0lIhGNjXZqhGf+oHDX/wQ auth-client t8Ki0g GOPiP3WM+FQlDXLK1vUEOg 8bBZRrlxj6Ca392exkNuog auth-client 1D9wbQ 0Y5FZJGg30M2WPWu+xahbQ aXwcRLMS5MFAYcBrGEibVA auth-client UoLbLw jwM4/d5BUfch4FLpGogouQ r9P/aNX3pWseC7tlXx1I5Q ======================================================================
with a total size of 1090 bytes.
I think this looks much nicer than the binary format and easier to parse with the routerparse API as well.
iii) Should we use a fresh IV for each ENCRYPTED_DESC_COOKIE? rend-spec.txt does not do that, and IIUC that's OK because it uses a fresh key for every encryption (even though the plaintext and IV is the same).
People said this is a good idea, so in the example above I did it.
My main counter argument is the size increase, but perhaps being stingy here is stupid.
In any case, the size overhead comes to 23 bytes of base64 for every IV, so it's not that bad.