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