• Home
Name Date Size #Lines LOC

..--

cmake/03-May-2024-113101

src/main/03-May-2024-13,2828,425

third_party/03-May-2024-

.gitignoreD03-May-202429 43

.gitmodulesD03-May-2024508 1716

Android.bpD03-May-20241.2 KiB4944

CMakeLists.txtD03-May-20241.3 KiB5140

CONTRIBUTING.mdD03-May-20241.1 KiB2920

LICENSED03-May-202411.1 KiB203169

METADATAD03-May-2024326 1412

MODULE_LICENSE_APACHE2D03-May-20240

READMED03-May-20240

README.mdD03-May-202416.5 KiB393269

build.gradleD03-May-20241.5 KiB6052

README.md

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:**
7Implemented in Java by Alexei Czeskis (aczeskis@google.com)
8Ported from Java to C++ by Tim Song (tengs@google.com)
9
10**Design reviewers:** Thai Duong, Bruno Blanchet, Martin Abadi, and Bo Wang
11
12**Implementation reviewer**: Thai Duong
13
14**Last Updated:** roughly in September 2016
15
16
17
18# Overview
19
20UKEY2 is a Diffie-Hellman based authenticated key exchange protocol.
21
22At the end of a UKEY2 run, a client and server have a shared master secret that can be used to
23derive keys which can be used in a subsequent protocol. UKEY2 only implicitly guarantees that
24servers know that clients believe the protocol finished correctly; that is, until a server
25receives a message on the next protocol from the client it does not know that the handshake
26completed.
27
28The intended usage of UKEY2 is to establish a secure channel between two user devices,
29e.g., laptop with Chromecast, phone with Google Glass, etc. The secure channel then can be used to
30transmit passwords or other credentials. It is especially useful when one wants to connect a brand
31 new device to a password-protected WIFI network.  UKEY2 is also usable over low-bandwidth
32transports like Bluetooth Low Energy (see [Performance](#performance)).
33
34# Message Framing
35
36Each UKEY2 message is framed inside an outer protobuf message:
37
38
39```
40message Ukey2Message {
41  enum Type {
42    UNKNOWN_DO_NOT_USE = 0;
43    ALERT = 1;
44    CLIENT_INIT = 2;
45    SERVER_INIT = 3;
46    CLIENT_FINISH = 4;
47  }
48
49  optional Type message_type = 1;   // Identifies message type
50  optional bytes message_data = 2;  // Actual message, to be parsed according to
51                                    // message_type
52}
53```
54
55
56
57# Alerts
58
59In case an error occurs, the client and server will reply with an Alert:
60
61
62```
63message Ukey2Alert {
64  enum AlertType {
65    // Framing errors
66    BAD_MESSAGE = 1;             // The message could not be deserialized
67    BAD_MESSAGE_TYPE = 2;        // message_type has an undefined value
68    INCORRECT_MESSAGE = 3;       // message_type received does not correspond to expected
69                                 // type at this stage of the protocol
70    BAD_MESSAGE_DATA = 4;        // Could not deserialize message_data as per value in
71                                 // message_type
72
73    // ClientInit and ServerInit errors
74    BAD_VERSION = 100;           // version is invalid; server cannot find suitable version
75                                 // to speak with client.
76    BAD_RANDOM = 101;            // Random data is missing or of incorrect length
77    BAD_HANDSHAKE_CIPHER = 102;  // No suitable handshake ciphers were found
78    BAD_NEXT_PROTOCOL = 103;     // The next protocol is missing, unknown, or unsupported
79    BAD_PUBLIC_KEY = 104;        // The public key could not be parsed
80
81    // Other errors
82    INTERNAL_ERROR = 200;       // An internal error has occurred.  error_message may
83                                // contain additional details for logging and debugging.
84  }
85
86  optional AlertType type = 1;
87  optional string error_message = 2;
88}
89```
90
91
92The type corresponds to the error that caused the `Alert` to be sent. Upon encountering an error,
93clients and servers send an Alert of the proper type and close the connection; all alerts are
94fatal. Upon receiving an `Alert`, clients and servers must close the connection, even if they
95cannot parse the `Alert`.  The `Alert` message may contain an optional `error_message` string
96that may be used to describe error details for logging.
97
98# Handshake Ciphersuites
99
100UKEY2 supports negotiation of the cryptographic primitives used in the handshake. Two primitives
101are required, a Diffie-Hellman function and a cryptographic hash function, which are represented
102by a single enum:
103
104
105```
106enum Ukey2HandshakeCipher {
107  RESERVED = 0;
108  P256_SHA512 = 100;        // NIST P-256 used for ECDH, SHA512 used for commitment
109  CURVE25519_SHA512 = 200;  // Curve 25519 used for ECDH, SHA512 used for commitment
110}
111```
112
113
114The implementations of all primitives must resist timing side-channel attacks. A summary of
115handshake ciphersuite negotiation is (see ClientInit and ServerInit messages for full details):
116
117*   The client enumerates the primitives it supports and the server choose the highest (by enum value) cipher that it also supports.
118*   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.
119
120
121# Handshake Details
122
123The UKEY2 handshake consists of three messages. First, the client sends a `ClientInit` message to
124the server -- conceptually, this consists of a list of cipher suites and a commitment to an
125ephemeral public key for each suite. The server responds with a `ServerInit` -- conceptually,
126this is the server's chosen cipher suite and an ephemeral public key for the cipher suites
127selected by the server.  Finally, the client responds with a `ClientFinished` -- conceptually,
128this consists of an ephemeral public key matching the cipher suite selected by the server.
129
130After the handshake, both client and server derive authentication strings, which may be shown to
131users for visual comparison or sent over some other channel in order to authenticate the handshake.
132The client and server also derive session keys for the next protocol.
133
134## The `ClientInit` Message
135
136The `ClientInit` message is defined as follows:
137
138
139```
140message Ukey2ClientInit {
141  optional int32 version = 1;  // highest supported version for rollback protection
142  optional bytes random = 2;   // random bytes for replay/reuse protection
143
144  // One commitment (hash of ClientFinished containing public key) per supported cipher
145  message CipherCommitment {
146    optional Ukey2HandshakeCipher handshake_cipher = 1;
147    optional bytes commitment = 2;
148  }
149  repeated CipherCommitment cipher_commitments = 3;
150
151  // Next protocol that the client wants to speak.
152  optional string next_protocol = 4;
153}
154```
155
156
157The `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.
158
159
160## Interpreting `ClientInit`
161
162Upon receiving the `ClientInit` message, the server should:
163
164
165
1661.  Deserialize the protobuf; send an `Alert.BAD_MESSAGE` message if deserialization fails.
1671.  Verify that `message_type == Type.CLIENT_INIT`; send an `Alert.BAD_MESSAGE_TYPE` message if mismatch occurs.
1681.  Deserialize `message_data` as a `ClientInit` message; send an `Alert.BAD_MESSAGE_DATA` message if deserialization fails.
1691.  Check that `version == 1`; send `Alert.BAD_VERSION` message if mismatch.
1701.  Check that `random` is exactly 32 bytes; send `Alert.BAD_RANDOM` message if not.
1711.  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.
1721.  Checks that `next_protocol` contains a protocol that the server supports.  Send an `Alert.BAD_NEXT_PROTOCOL` message if not.
173
174If no alerts have been sent, the server replies with the `ServerInit` message.
175
176
177## The `ServerInit` Message
178
179The `ServerInit` message is as follows
180
181
182```
183message Ukey2ServerInit {
184  optional int32 version = 1;  // highest supported version for rollback protection
185  optional bytes random = 2;   // random bytes for replay/reuse protection
186
187  // Selected Cipher and corresponding public key
188  optional Ukey2HandshakeCipher handshake_cipher = 3;
189  optional bytes public_key = 4;
190}
191```
192
193
194For now, `version` must be 1.  The random field is exactly 32 cryptographically secure random
195bytes.  The `handshake_cipher` field contains the server-chosen `HandshakeCipher`.  The
196`public_key` field contains the server-chosen corresponding public key.
197
198
199## Interpreting `ServerInit`
200
201When a client receives a `ServerInit` after having sent a `ClientInit`, it performs the following actions:
202
203
2041.  Deserialize the protobuf; send an `Alert.BAD_MESSAGE` message if deserialization fails.
2051.  Verify that `message_type == Type.SERVER_INIT`; send an `Alert.BAD_MESSAGE_TYPE` message if mismatch occurs.
2061.  Deserialize `message_data` as a `ServerInit` message; send an `Alert.BAD_MESSAGE_DATA` message if deserialization fails.
2071.  Check that `version == 1`; send `Alert.BAD_VERSION` message if mismatch.
2081.  Check that `random` is exactly 32 bytes; send `Alert.BAD_RANDOM` message if not.
2091.  Check that `handshake_cipher` matches a handshake cipher that was sent in
210`ClientInit.cipher_commitments`. If not, send an `Alert.BAD_HANDSHAKECIPHER` message.
2111.  Check that `public_key` parses into a correct public key structure.  If not, send an `Alert.BAD_PUBLIC_KEY` message.
212
213If no alerts have been sent, the client replies with the `ClientFinished` message.  After sending
214the `ClientFinished` message, the Client considers the handshake complete.
215
216
217**IMPORTANT:** The client should compute the authentication string `AUTH_STRING` and
218the next-protocol secret `NEXT_SECRET` (see below).  The client should use an out-of-band
219channel to verify the authentication string before proceeding to the next protocol.
220
221
222## The ClientFinished Message
223
224The `ClientFinished` message is as follows:
225
226
227```
228message Ukey2ClientFinished {
229  optional bytes public_key = 1;  // public key matching selected handshake cipher
230}
231```
232
233
234The `public_key` contains the Client's public key (whose commitment was sent in the `ClientInit`
235message) for the server-selected handshake cipher.
236
237
238## Interpreting ClientFinished
239
240When a server receives a `ClientFinished` after having sent a `ServerInit`, it performs the
241following actions:
242
243
2441.  Deserialize the protobuf; terminate the connection if deserialization fails.
2451.  Verify that `message_type == Type.CLIENT_FINISHED`; terminate the connection if mismatch occurs.
2461.  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.
2471.  Deserialize `message_data` as a `ClientFinished` message; terminate the connection if deserialization fails.
2481.  Check that `public_key` parses into a correct public key structure.  If not, terminate the connection.
249
250Note that because the client is not expecting a response, any error results in connection termination.
251
252After parsing the `ClientFinished` message, the Server considers the handshake complete.
253
254
255**IMPORTANT:** The server should compute the authentication string `AUTH_STRING` and the
256next-protocol secret `NEXT_SECRET` (see below).  The server should use an out-of-band channel to
257verify the authentication string before proceeding to the next protocol.
258
259
260# Deriving the Authentication String and the Next-Protocol Secret
261
262Let `DHS` = the negotiated Diffie-Hellman key derived from the Client and Server public keys.
263
264Let `M_1` = the serialized (including framing) raw bytes of the first message sent by
265the client
266
267Let `M_2` = the serialized (including framing) raw bytes of the first message sent by
268the server
269
270Let `Hash` = the hash from HandshakeCipher
271
272Let `L_auth` = length of authentication string in bytes.  Note that this length can
273be short (e.g., a 6 digit visual confirmation code).
274
275Let `L_next` = length of next protocol key
276
277Let `HKDF-Extract` and `HKDF-Expand` be as defined in [RFC5869](https://tools.ietf.org/html/rfc5869)
278instantiated with the hash from the `HandshakeCipher`.
279
280Let `PRK_AUTH = HKDF-Extract("UKEY2 v1 auth", DHS)`
281
282Let `PRK_NEXT = HKDF-Extract("UKEY2 v1 next", DHS)`
283
284Then `AUTH_STRING = HKDF-Expand(PRK_AUTH, M_1|M_2, L_auth)`
285
286Then `NEXT_SECRET = HKDF-Expand(PRK_NEXT, M_1|M_2, L_next)`
287
288
289# Security Discussion
290
291If client and server authenticate one-another using the `AUTH_STRING` through an out-of-band
292mechanism, we believe that this handshake is resistant to an active man-in-the-middle attacker.
293The attacker, whether he/she plays the role of the client or server, is forced to commit to a
294public key before seeing the other-party's public key.
295
296The authentication string and next secret are computed in such a way that knowledge of one does
297not allow an attacker to compute the other.  That is, if the attacker observed the `AUTH_STRING`
298(if it was shown on a monitor for example), the attacker could not compute `NEXT_SECRET`.
299Furthermore, both the authentication string and next secret depend on the full handshake
300transcript -- a manipulation of any handshake message by an adversary would change both the
301 authentication string and the next secret.  Note that although the last message is not directly
302 included in the HKDF computation, it is included as part of the commitment sent in `M_1.`
303
304@shabsi pointed out that by having the `HKDF` info field have bits that also go into making the
305`PRK`, this violates some security proof.  Those "shared" bits are the public keys that are sent
306in `M_2` and `M_3` and are also used to derive the DHS.  Though the "proof" may
307 not hold in theory, we do believe the security of the handshake is maintained in practice.
308
309A natural question may be why we didn't use
310[Short Authentication Strings](https://www.iacr.org/archive/crypto2005/36210303/36210303.pdf)
311(SAS).  The answer is two-fold.  First, traditional SAS does not incorporate a key exchange, only
312authentication; UKEY2 provides both.  Second, the paper does not give concrete primitives,
313instead describing abstract functions such as `commit() `and `open()`.  One concrete
314implementation of these functions would look similar to what UKEY2 does.
315
316Bruno Blanchet performed a formal proof of a simplified version of UKEY2.
317
318# Performance
319
320The messages are fairly compact.  Running a test where the client sent a single commitment for a
321`P256_SHA512` cipher and the `next_protocol` was set to "`AES_256_CBC-HMAC_SHA256"`, the total
322size of the messages were:
323
324
325| Message        | Length in Bytes |
326|:---------------|----------------:|
327|`ClientInit`    | 136             |
328|`ServerInit`    | 117             |
329|`ClientFinished`| 79              |
330
331
332# Checking out source code
333
334```
335git clone https://github.com/google/ukey2
336cd ukey2
337git submodule update --init --recursive
338```
339
340# Building and tesging C++ code
341
342## Build
343```
344cd <source root>
345mkdir build; cd build
346cmake -Dukey2_USE_LOCAL_PROTOBUF=ON -Dukey2_USE_LOCAL_ABSL=ON ..
347make
348```
349## Running C++ tests
350```
351cd <source root>/build
352ctest -V
353```
354
355# Buillding Java library and running Java Tests
356
357NOTE: c++ build must be completed as described above, before running java tests.
358This requirement exists because Java build runs a c++/java compatibility test, and
359this test depends on c++ test helper binary (found in build/src/main/cpp/test/securegcm/ukey2_test).
360Gradle build does not know how to build this artifact. Java test uses a relative
361path to the artifact, and expects tests to be run from <source root> as follows:
362
363Pre-reqs: gradle
364
3651. Create gradle wrapper for a specific gradle version.
366This project was built with Gradle-6.1.1.
367If you have an incompatible version of gradle it is recommended that
368you setup gradle wrapper first.
3691.1. The simplest is to run
370```
371cd <source root>
372gradle wrapper --gradle-version=6.1.1
373
374```
375
3761.2. If this fails, this is likely because current gradle version is unable to parse the build.gradle
377file. In this case, create an empty directory outside your project tree, and create a wrapper there.
378```
379mkdir -p $HOME/scratch/gradle-wrapper-611
380cd $HOME/scratch/gradle-wrapper-611
381gradle wrapper --gradle-version=6.1.1
382cp -a gradle gradlew gradlew.bat <source root>
383```
384
3852. Once you get gradle wrapper installed, run test command
386
387```
388cd <source root>
389./gradlew test -i
390```
391
392This will build and execute all the tests.
393