• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Ukey2
2This is not an officially supported Google product
3
4**Coathored by:** Alexei Czeskis, Thai Duong, Eduardo' Vela'' \<Nava\>, and Adam Stubblefield.
5
6**Status:** Implemented in Java (aczeskis@google.com)
7
8**Design reviewers:** Thai Duong, Bruno Blanchet, Martin Abadi, and Bo Wang
9
10**Implementation reviewer**: Thai Duong
11
12**Last Updated:** roughly in September 2016
13
14
15
16# Overview
17
18UKEY2 is a Diffie-Hellman based authenticated key exchange protocol.
19
20At the end of a UKEY2 run, a client and server have a shared master secret that can be used to
21derive keys which can be used in a subsequent protocol. UKEY2 only implicitly guarantees that
22servers know that clients believe the protocol finished correctly; that is, until a server
23receives a message on the next protocol from the client it does not know that the handshake
24completed.
25
26The intended usage of UKEY2 is to establish a secure channel between two user devices,
27e.g., laptop with Chromecast, phone with Google Glass, etc. The secure channel then can be used to
28transmit passwords or other credentials. It is especially useful when one wants to connect a brand
29 new device to a password-protected WIFI network.  UKEY2 is also usable over low-bandwidth
30transports like Bluetooth Low Energy (see [Performance](#performance)).
31
32# Message Framing
33
34Each UKEY2 message is framed inside an outer protobuf message:
35
36
37```
38message Ukey2Message {
39  enum Type {
40    UNKNOWN_DO_NOT_USE = 0;
41    ALERT = 1;
42    CLIENT_INIT = 2;
43    SERVER_INIT = 3;
44    CLIENT_FINISH = 4;
45  }
46
47  optional Type message_type = 1;   // Identifies message type
48  optional bytes message_data = 2;  // Actual message, to be parsed according to
49                                    // message_type
50}
51```
52
53
54
55# Alerts
56
57In case an error occurs, the client and server will reply with an Alert:
58
59
60```
61message Ukey2Alert {
62  enum AlertType {
63    // Framing errors
64    BAD_MESSAGE = 1;             // The message could not be deserialized
65    BAD_MESSAGE_TYPE = 2;        // message_type has an undefined value
66    INCORRECT_MESSAGE = 3;       // message_type received does not correspond to expected
67                                 // type at this stage of the protocol
68    BAD_MESSAGE_DATA = 4;        // Could not deserialize message_data as per value in
69                                 // message_type
70
71    // ClientInit and ServerInit errors
72    BAD_VERSION = 100;           // version is invalid; server cannot find suitable version
73                                 // to speak with client.
74    BAD_RANDOM = 101;            // Random data is missing or of incorrect length
75    BAD_HANDSHAKE_CIPHER = 102;  // No suitable handshake ciphers were found
76    BAD_NEXT_PROTOCOL = 103;     // The next protocol is missing, unknown, or unsupported
77    BAD_PUBLIC_KEY = 104;        // The public key could not be parsed
78
79    // Other errors
80    INTERNAL_ERROR = 200;       // An internal error has occurred.  error_message may
81                                // contain additional details for logging and debugging.
82  }
83
84  optional AlertType type = 1;
85  optional string error_message = 2;
86}
87```
88
89
90The type corresponds to the error that caused the `Alert` to be sent. Upon encountering an error,
91clients and servers send an Alert of the proper type and close the connection; all alerts are
92fatal. Upon receiving an `Alert`, clients and servers must close the connection, even if they
93cannot parse the `Alert`.  The `Alert` message may contain an optional `error_message` string
94that may be used to describe error details for logging.
95
96# Handshake Ciphersuites
97
98UKEY2 supports negotiation of the cryptographic primitives used in the handshake. Two primitives
99are required, a Diffie-Hellman function and a cryptographic hash function, which are represented
100by a single enum:
101
102
103```
104enum Ukey2HandshakeCipher {
105  RESERVED = 0;
106  P256_SHA512 = 100;        // NIST P-256 used for ECDH, SHA512 used for commitment
107  CURVE25519_SHA512 = 200;  // Curve 25519 used for ECDH, SHA512 used for commitment
108}
109```
110
111
112The implementations of all primitives must resist timing side-channel attacks. A summary of
113handshake ciphersuite negotiation is (see ClientInit and ServerInit messages for full details):
114
115*   The client enumerates the primitives it supports and the server choose the highest (by enum value) cipher that it also supports.
116*   The server replies with a public key using the chosen cipher and sends its own list of supported handshake cipher suites so that the client can verify that the right selection was made.
117
118
119# Handshake Details
120
121The UKEY2 handshake consists of three messages. First, the client sends a `ClientInit` message to
122the server -- conceptually, this consists of a list of cipher suites and a commitment to an
123ephemeral public key for each suite. The server responds with a `ServerInit` -- conceptually,
124this is the server's chosen cipher suite and an ephemeral public key for the cipher suites
125selected by the server.  Finally, the client responds with a `ClientFinished` -- conceptually,
126this consists of an ephemeral public key matching the cipher suite selected by the server.
127
128After the handshake, both client and server derive authentication strings, which may be shown to
129users for visual comparison or sent over some other channel in order to authenticate the handshake.
130The client and server also derive session keys for the next protocol.
131
132## The `ClientInit` Message
133
134The `ClientInit` message is defined as follows:
135
136
137```
138message Ukey2ClientInit {
139  optional int32 version = 1;  // highest supported version for rollback protection
140  optional bytes random = 2;   // random bytes for replay/reuse protection
141
142  // One commitment (hash of ClientFinished containing public key) per supported cipher
143  message CipherCommitment {
144    optional Ukey2HandshakeCipher handshake_cipher = 1;
145    optional bytes commitment = 2;
146  }
147  repeated CipherCommitment cipher_commitments = 3;
148
149  // Next protocol that the client wants to speak.
150  optional string next_protocol = 4;
151}
152```
153
154
155The `version` field is the maximum version that the client supports.  It should be 1 for now. The `random` field is exactly 32 cryptographically secure random bytes.  The `cipher_commitment` field is a protobuf consisting of a handshake cipher and a commitment which is a hash of the `ClientFinished` message that would be sent if the cipher were selected (the serialized, including framing, raw bytes of the last handshake message sent by the client), calculated with the hash function and the Diffie-Hellman function from the handshake cipher. The client includes each commitment in the order of their preference.  Note that only one commitment per `handshake_cipher` is allowed.  The client also includes the `next_protocol` field that specifies that the client wants to use to speak to the server.  Note that this protocol must implicitly imply a key length.  UKEY2, however, does not provide a namespace for the `next_protocol` values in order to provide layers separation between the handshake and the next protocols.
156
157
158## Interpreting `ClientInit`
159
160Upon receiving the `ClientInit` message, the server should:
161
162
163
1641.  Deserialize the protobuf; send an `Alert.BAD_MESSAGE` message if deserialization fails.
1651.  Verify that `message_type == Type.CLIENT_INIT`; send an `Alert.BAD_MESSAGE_TYPE` message if mismatch occurs.
1661.  Deserialize `message_data` as a `ClientInit` message; send an `Alert.BAD_MESSAGE_DATA` message if deserialization fails.
1671.  Check that `version == 1`; send `Alert.BAD_VERSION` message if mismatch.
1681.  Check that `random` is exactly 32 bytes; send `Alert.BAD_RANDOM` message if not.
1691.  Check to see if any of the `handshake_cipher` in `cipher_commitment` are acceptable. Servers should select the first `handshake_cipher` that it finds acceptable to support clients signaling deprecated but supported HandshakeCiphers. If no `handshake_cipher` is acceptable (or there are no HandshakeCiphers in the message), the server sends an `Alert.BAD_HANDSHAKE_CIPHER` message.
1701.  Checks that `next_protocol` contains a protocol that the server supports.  Send an `Alert.BAD_NEXT_PROTOCOL` message if not.
171
172If no alerts have been sent, the server replies with the `ServerInit` message.
173
174
175## The `ServerInit` Message
176
177The `ServerInit` message is as follows
178
179
180```
181message Ukey2ServerInit {
182  optional int32 version = 1;  // highest supported version for rollback protection
183  optional bytes random = 2;   // random bytes for replay/reuse protection
184
185  // Selected Cipher and corresponding public key
186  optional Ukey2HandshakeCipher handshake_cipher = 3;
187  optional bytes public_key = 4;
188}
189```
190
191
192For now, `version` must be 1.  The random field is exactly 32 cryptographically secure random
193bytes.  The `handshake_cipher` field contains the server-chosen `HandshakeCipher`.  The
194`public_key` field contains the server-chosen corresponding public key.
195
196
197## Interpreting `ServerInit`
198
199When a client receives a `ServerInit` after having sent a `ClientInit`, it performs the following actions:
200
201
2021.  Deserialize the protobuf; send an `Alert.BAD_MESSAGE` message if deserialization fails.
2031.  Verify that `message_type == Type.SERVER_INIT`; send an `Alert.BAD_MESSAGE_TYPE` message if mismatch occurs.
2041.  Deserialize `message_data` as a `ServerInit` message; send an `Alert.BAD_MESSAGE_DATA` message if deserialization fails.
2051.  Check that `version == 1`; send `Alert.BAD_VERSION` message if mismatch.
2061.  Check that `random` is exactly 32 bytes; send `Alert.BAD_RANDOM` message if not.
2071.  Check that `handshake_cipher` matches a handshake cipher that was sent in
208`ClientInit.cipher_commitments`. If not, send an `Alert.BAD_HANDSHAKECIPHER` message.
2091.  Check that `public_key` parses into a correct public key structure.  If not, send an `Alert.BAD_PUBLIC_KEY` message.
210
211If no alerts have been sent, the client replies with the `ClientFinished` message.  After sending
212the `ClientFinished` message, the Client considers the handshake complete.
213
214
215**IMPORTANT:** The client should compute the authentication string `AUTH_STRING` and
216the next-protocol secret `NEXT_SECRET` (see below).  The client should use an out-of-band
217channel to verify the authentication string before proceeding to the next protocol.
218
219
220## The ClientFinished Message
221
222The `ClientFinished` message is as follows:
223
224
225```
226message Ukey2ClientFinished {
227  optional bytes public_key = 1;  // public key matching selected handshake cipher
228}
229```
230
231
232The `public_key` contains the Client's public key (whose commitment was sent in the `ClientInit`
233message) for the server-selected handshake cipher.
234
235
236## Interpreting ClientFinished
237
238When a server receives a `ClientFinished` after having sent a `ServerInit`, it performs the
239following actions:
240
241
2421.  Deserialize the protobuf; terminate the connection if deserialization fails.
2431.  Verify that `message_type == Type.CLIENT_FINISHED`; terminate the connection if mismatch occurs.
2441.  Verify that the hash of the `ClientFinished` matches the expected commitment for the chosen `handshake_cipher` from `ClientInit`.  Terminate the connection if the expected match fails.
2451.  Deserialize `message_data` as a `ClientFinished` message; terminate the connection if deserialization fails.
2461.  Check that `public_key` parses into a correct public key structure.  If not, terminate the connection.
247
248Note that because the client is not expecting a response, any error results in connection termination.
249
250After parsing the `ClientFinished` message, the Server considers the handshake complete.
251
252
253**IMPORTANT:** The server should compute the authentication string `AUTH_STRING` and the
254next-protocol secret `NEXT_SECRET` (see below).  The server should use an out-of-band channel to
255verify the authentication string before proceeding to the next protocol.
256
257
258# Deriving the Authentication String and the Next-Protocol Secret
259
260Let `DHS` = the negotiated Diffie-Hellman key derived from the Client and Server public keys.
261
262Let `M_1` = the serialized (including framing) raw bytes of the first message sent by
263the client
264
265Let `M_2` = the serialized (including framing) raw bytes of the first message sent by
266the server
267
268Let `Hash` = the hash from HandshakeCipher
269
270Let `L_auth` = length of authentication string in bytes.  Note that this length can
271be short (e.g., a 6 digit visual confirmation code).
272
273Let `L_next` = length of next protocol key
274
275Let `HKDF-Extract` and `HKDF-Expand` be as defined in [RFC5869](https://tools.ietf.org/html/rfc5869)
276instantiated with the hash from the `HandshakeCipher`.
277
278Let `PRK_AUTH = HKDF-Extract("UKEY2 v1 auth", DHS)`
279
280Let `PRK_NEXT = HKDF-Extract("UKEY2 v1 next", DHS)`
281
282Then `AUTH_STRING = HKDF-Expand(PRK_AUTH, M_1|M_2, L_auth)`
283
284Then `NEXT_SECRET = HKDF-Expand(PRK_NEXT, M_1|M_2, L_next)`
285
286
287# Security Discussion
288
289If client and server authenticate one-another using the `AUTH_STRING` through an out-of-band
290mechanism, we believe that this handshake is resistant to an active man-in-the-middle attacker.
291The attacker, whether he/she plays the role of the client or server, is forced to commit to a
292public key before seeing the other-party's public key.
293
294The authentication string and next secret are computed in such a way that knowledge of one does
295not allow an attacker to compute the other.  That is, if the attacker observed the `AUTH_STRING`
296(if it was shown on a monitor for example), the attacker could not compute `NEXT_SECRET`.
297Furthermore, both the authentication string and next secret depend on the full handshake
298transcript -- a manipulation of any handshake message by an adversary would change both the
299 authentication string and the next secret.  Note that although the last message is not directly
300 included in the HKDF computation, it is included as part of the commitment sent in `M_1.`
301
302@shabsi pointed out that by having the `HKDF` info field have bits that also go into making the
303`PRK`, this violates some security proof.  Those "shared" bits are the public keys that are sent
304in `M_2` and `M_3` and are also used to derive the DHS.  Though the "proof" may
305 not hold in theory, we do believe the security of the handshake is maintained in practice.
306
307A natural question may be why we didn't use
308[Short Authentication Strings](https://www.iacr.org/archive/crypto2005/36210303/36210303.pdf)
309(SAS).  The answer is two-fold.  First, traditional SAS does not incorporate a key exchange, only
310authentication; UKEY2 provides both.  Second, the paper does not give concrete primitives,
311instead describing abstract functions such as `commit() `and `open()`.  One concrete
312implementation of these functions would look similar to what UKEY2 does.
313
314Bruno Blanchet performed a formal proof of a simplified version of UKEY2.
315
316# Performance
317
318The messages are fairly compact.  Running a test where the client sent a single commitment for a
319`P256_SHA512` cipher and the `next_protocol` was set to "`AES_256_CBC-HMAC_SHA256"`, the total
320size of the messages were:
321
322
323| Message        | Length in Bytes |
324|:---------------|----------------:|
325|`ClientInit`    | 136             |
326|`ServerInit`    | 117             |
327|`ClientFinished`| 79              |
328
329
330