ARM4FS Designfoo: Difference between revisions
Line 117: | Line 117: | ||
==== List of Commands ==== |
==== List of Commands ==== |
||
===== authenticate ===== |
|||
{| |
|||
|- style="vertical-align: top;" |
|||
! style="text-align: right;" | Synopsis: |
|||
| colspan="2" | <code>authenticate <var>pubkey</var> <var>response_value</var></code><br/>→ ∅ |
|||
|- style="vertical-align: top;" |
|||
! style="text-align: right;" | Arguments: |
|||
| colspan="2" | <code><var>pubkey</var></code>: The clients public key, BASE64 encoded.<br/><code><var>response_value</var></code>: The response to the challenge. |
|||
|- style="vertical-align: top;" |
|||
! style="text-align: right;" | Description: |
|||
| colspan="2" | Completes the authentication procedure. |
|||
|- style="vertical-align: top;" |
|||
! style="text-align: right;" rowspan="2" | Response: |
|||
! style="text-align: right; width: 1em;" | pos.: |
|||
| none |
|||
|- style="vertical-align: top;" |
|||
! style="text-align: right; width: 1em;" | neg.: |
|||
| none |
|||
|} |
|||
===== challenge ===== |
===== challenge ===== |
||
{| |
{| |
||
|- style="vertical-align: top;" |
|- style="vertical-align: top;" |
||
! style="text-align: right;" | |
! style="text-align: right;" | Synopsis: |
||
| colspan="2" | <code>challenge</code><br/>→<code><var>challenge_value</var></code> |
| colspan="2" | <code>challenge</code><br/>→ <code><var>challenge_value</var></code> |
||
|- style="vertical-align: top;" |
|- style="vertical-align: top;" |
||
! style="text-align: right;" | Arguments: |
! style="text-align: right;" | Arguments: |
||
Line 131: | Line 151: | ||
|- style="vertical-align: top;" |
|- style="vertical-align: top;" |
||
! style="text-align: right;" rowspan="2" | Response: |
! style="text-align: right;" rowspan="2" | Response: |
||
! style="text-align: right;" | pos.: |
! style="text-align: right; width: 1em;" | pos.: |
||
| <code><var>challenge_value</var></code>: The challenge, a BASE64 encoded string |
| <code><var>challenge_value</var></code>: The challenge, a BASE64 encoded opaque string. |
||
|- style="vertical-align: top;" |
|- style="vertical-align: top;" |
||
! style="text-align: right;" | neg.: |
! style="text-align: right; width: 1em;" | neg.: |
||
| none |
| none |
||
|} |
|} |
Revision as of 03:39, 8 May 2006
- UserID
- Public/private key pair, maybe with additional attributes
- User management
- RP has a database with two unrelated tables:
- UserIDs and accompanying data
- Realnames/IDs (X.509-DN?) who already have an account.
- User creation, in cooperation with an identity provider:
- Identity provider asserts identity
- Reputation provider checks whether this identity already has an account, refusing creation if this is the case (counters Sybil attack)
- Create a new UserID and enter it into the database; at the same time enter the realname as 'used' into the database.
- Two possible approaches to account for eventual deletion of UserIDs together with their realname entries (makes it possible to apply for another UserID thereafter):
- Save a hash of the realname together with the UserID. Bad idea, because a simple scan could correlate the UserID and realname.
- Give a signed (+ encrypted?) realname/UserID pair to the user on account creation. The user can then either delete this pair if she is positive that she won't need it in the future (or is afraid of the risk that this pair may be found on her computer), or use it in the future to have the reputation provider delete both entries (UserID table and realname table).
- RP has a database with two unrelated tables:
- Document submission
- session = RP.createSession(UserID) ObjectKey = SP.storeTemporary(Content, timeout) AuthorTag = session.createAuthorTag(ObjectKey) SP.storePermanent(AuthorTag) session.destroy()
- Reputation query
- session = RP.createSession(UserID) session.getReputation(AuthorTag) session.destroy()
- Content feedback
- session = RP.createSession(UserID) session.submitFeedback(AuthorTag, feedback) session.destroy()
- AuthorTag
- signed_RP(UserPseudonym, ObjectKey)
- Sessions
- Authenticated, encrypted channel to the Reputation Provider. Maybe integrate Off-the-Record here? Would that be beneficial? In any case: it's fine to route the channel through TOR or similar.
- createAuthorTag()
- UserPseudonym_x is a completely random value which will only be used to lookup the UserID in the Reputation Provider's database. Rationale: If anonymity is supposed to be the highest goal of this system then it won't be such a good idea to have any relationship between the public UserPseudonym and the secret UserID. Even encrypting isn't a good idea: If the encryption key is broken or compromised then all anonymity for all users for all past transactions would be void. (Break one, break all.) It might be possible to segment the effects of possible key compromises by using multiple keys (either separated by time or by user group), but that's only mildly effective and has problems of its own (e.g. by correlating on the key that was used it would be possible for an attacker to find out when an AuthorTag has been created, or which group a user belongs to; now that I think about it: this could be countered by randomly using many keys, but still doesn't look very good). The underlying threat model is that an administrator of the system might be 'convinced' into smuggling the key into the hands of the attacker. Certainly it would be a lot harder to transmit a complete database of UserID<->UserPseudonym relations than it would be to transmit a single key (about 4096 bits). On the other hand: It is easier to safeguard a key (e.g. in a security module such as a smart card) than it is to safeguard a complete database. Maybe we should combine both approaches? The public UserPseudonym would then be an encrypted random string. Note: The signature key for AuthorTag signatures is not so much of a problem. If this key is compromised then only the correctness of the system will be endangered and not the anonymity of its users.
- ObjectKey
- URI, preferably URL with (schema specific) embedded hash of the contents. For starters:
http://SP/get?h=hash
.
Musings On The Server<->Client Protocol
The Reputation Provider acts as a server and listens on port 2323.
Encryption
The server<->client protocol, or the sessions, should provide protection against:
- all passive attacks
- most active attacks
Specifically it seems to only be possible to protect against active attacks (man in the middle) if a trust relationship has already been established, e.g. the client knows the server's public key. (In order to provide confidentiality the server does not need to know the client's public key. This key only becomes important for authenticating clients. The user creation process is possible even for an unauthenticated client.)
To minimize the possibility of protocol design or implementation errors it would be best to use an already existing protocol and implementation, e.g. SSL/TLS and OpenSSL. Problem: SSL uses X.509 certificates and not raw keys. Certificates pose some dangers of introducing accidental side channels, for example through serial numbers, but add next to no additional value. Also: SSL is quite a complex protocol and widely deployed implementation errors (ASN.1!) have not been unheard of.
A homemade protocol would look something like this:
- diffie-hellmann between client and server
- server signs own diffie-hellmann parameters with own secret key
- when the client has verified this signature it knows that it is in fact talking to the right server
- connection is then in a state in which the client may
- transmit a user creation request, or
- authenticate itself by sending a signed version of its own diffie-hellmann parameter
Discussion
own protocol | SSL/TLS | |
---|---|---|
pro |
|
|
contra |
|
|
One compromise approach might be: Use SSL and a 'real' certificate for the server (allows verification through PKI) but only a raw key for the client. Then when the connection is set up and authentication is necessary, have the client transmit its public key and sign something with that key. Preferably some secret from the SSL connection (session key or master key perhaps?), but it's probably not possible to get access to this. Alternatively let the server send a challenge and have the client sign that (be wary of replay attacks and the such).
Payload
The cleartext protocol is simple and text-based. All commands are sent by the client. All commands and responses are single lines that end with a single newline ("\n"). The terminating newline is implied and not mentioned explicitly for the rest of this document because it is not technically considered to be a part of the command/response. A command or response MUST NOT have any newline anywhere except for the terminating newline.
Failure to follow any of the regulations in this protocol MAY lead to immediate termination of the connection.
Commands
Commands are single words with no space, followed by optional command specific data that is separated from the command by a single space. The command specific data generally consists of several parameters (with no spaces) concatenated with spaces.
Error Responses
Error responses start with an unhappy smiley, a single space and a three-digit error code, followed by an optional human readable error message. They MUST thus match the following regular expression (PCRE syntax):
/^:-\( \d{3}.*$/
Error codes
Code | Message | Explanation |
---|---|---|
400 | Bad command | The command was malformed or invalid. Note that the server has the option to immediately terminate the connection when it encounters a protocol error and is not obliged to send this response and keep going. |
401 | Authorization required | The command was not executed because the client must authenticate first. See the section on Authentication. A server MAY terminate the connection instead of sending this response if it is extremely paranoid. This is the recommended behaviour. (Remember: Just because you're paranoid doesn't mean that they're not after you.) |
Positive Responses
Positive responses start with a happy smiley, a single space and command specific data. Thus they MUST match the following regular expression (PCRE syntax):
/^:-\) .*$/
Authentication
Connections can be in two states: authenticated and unauthenticated. On unauthenticated connections only a minor subset of commands is available. When an unauthenticated client tries to access a command that needs authentication the server either sends a 401 error code or terminates the connection.
In order to initiate the authentication procedure:
- The client sends an
challenge
command with no parameter. - The server sends a positive response with the challenge.
- The client sends an
authenticate
command with its own public key and the signed challenge - Either
- the server acknowledges the successful authentication with a positive response, if the signature checks out, or
- should the authentication not be successful for any reason, then the server silently terminates the connection with no error message.
Authentication may be tried exactly once per connection. No other command may be sent between the start of the procedure (the challenge
command) and the reception of the positive response to the authenticate
command. Any deviation from this protocol is forbidden and MUST lead to a termination of the connection.
List of Commands
authenticate
Synopsis: | authenticate pubkey response_value → ∅ | |
---|---|---|
Arguments: | pubkey : The clients public key, BASE64 encoded.response_value : The response to the challenge.
| |
Description: | Completes the authentication procedure. | |
Response: | pos.: | none |
neg.: | none |
challenge
Synopsis: | challenge → challenge_value
| |
---|---|---|
Arguments: | none | |
Description: | Initiates the authentication procedure. The server responds with a challenge which the client must sign and send back in an authenticate command.
| |
Response: | pos.: | challenge_value : The challenge, a BASE64 encoded opaque string.
|
neg.: | none |