1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SECURITY_CRYPTAUTH_LIB_SECUREGCM_UKEY2_HANDSHAKE_H_ 16 #define SECURITY_CRYPTAUTH_LIB_SECUREGCM_UKEY2_HANDSHAKE_H_ 17 18 #include <map> 19 #include <memory> 20 21 #include "proto/ukey.pb.h" 22 #include "securegcm/d2d_connection_context_v1.h" 23 #include "securemessage/crypto_ops.h" 24 25 namespace securegcm { 26 27 // Implements UKEY2 and produces a |D2DConnectionContextV1|. 28 // This class should be kept compatible with the Java implementation in 29 // //java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java 30 // 31 // For usage examples, see ukey2_shell.cc. This file contains a shell exercising 32 // both the initiator and responder handshake roles. 33 class UKey2Handshake { 34 public: 35 // Handshake states: 36 // kInProgress: 37 // The handshake is in progress, caller should use 38 // |GetNextHandshakeMessage()| and |ParseHandshakeMessage()| to continue 39 // the handshake. 40 // 41 // kVerificationNeeded: 42 // The handshake is complete, but pending verification of the 43 // authentication string. Clients should use |GetVerificationString()| 44 // to get the verification string and use out-of-band methods to 45 // authenticate the handshake. 46 // 47 // kVerificationInProgress: 48 // The handshake is complete, verification string has been generated, 49 // but has not been confirmed. After authenticating the handshake 50 // out-of-band, use |VerifyHandshake()| to mark the handshake as 51 // verified. 52 // 53 // kFinished: 54 // The handshake is finished, and the caller can use 55 // |ToConnectionContext()| to produce a |D2DConnectionContextV1|. 56 // 57 // kAlreadyUsed: 58 // The hanshake has already been used and should be destroyed. 59 // 60 // kError: 61 // The handshake produced an error and should be destroyed. 62 enum class State { 63 kInProgress, 64 kVerificationNeeded, 65 kVerificationInProgress, 66 kFinished, 67 kAlreadyUsed, 68 kError, 69 }; 70 71 // Currently implemented UKEY2 handshake ciphers. Each cipher is a tuple 72 // consisting of a key negotiation cipher and a hash function used for a 73 // commitment. Currently the ciphers are: 74 // +-----------------------------------------------------+ 75 // | Enum | Key negotiation | Hash function | 76 // +-------------+-----------------------+---------------+ 77 // | P256_SHA512 | ECDH using NIST P-256 | SHA512 | 78 // +-----------------------------------------------------+ 79 // 80 // Note that these should correspond to values in 81 // device_to_device_messages.proto. 82 enum class HandshakeCipher : int { 83 // TODO(aczeskis): add CURVE25519_SHA512 84 85 P256_SHA512 = securegcm::P256_SHA512, 86 }; 87 88 // Creates a |UKey2Handshake| with a particular |cipher| that can be used by 89 // an initiator / client. 90 static std::unique_ptr<UKey2Handshake> ForInitiator(HandshakeCipher cipher); 91 92 // Creates a |UKey2Handshake| with a particular |cipher| that can be used by 93 // a responder / server. 94 static std::unique_ptr<UKey2Handshake> ForResponder(HandshakeCipher cipher); 95 96 // Returns the current state of the handshake. 97 State GetHandshakeState() const; 98 99 // Returns the last error message. Empty string if there was no error. 100 const string& GetLastError() const; 101 102 // Gets the next handshake message suitable for sending on the wire. 103 // If |nullptr| is returned, check |GetLastError()| for the error message. 104 std::unique_ptr<string> GetNextHandshakeMessage(); 105 106 // Parses the given |handshake_message|, updating the internal state. 107 struct ParseResult { 108 // True if |handshake_message| is parsed successfully. If |false|, call 109 // |GetLastError()| for the error message. 110 bool success; 111 112 // May be set if parsing fails. This value should be sent to the remote 113 // device before disconnecting. 114 std::unique_ptr<string> alert_to_send; 115 }; 116 ParseResult ParseHandshakeMessage(const string& handshake_message); 117 118 // Returns an authentication string suitable for authenticating the handshake 119 // out-of-band. Note that the authentication string can be short (e.g., a 6 120 // digit visual confirmation code). 121 // 122 // Note: This should only be called when the state returned from 123 // |GetHandshakeState()| is |State::VERIFICATION_NEEDED|, which means this can 124 // only be called once. 125 // 126 // |byte_length|: The length of the output. Min length is 1; max length is 32. 127 // If |nullptr| is returned, check |GetLastError()| for the error message. 128 std::unique_ptr<string> GetVerificationString(int byte_length); 129 130 // Invoked to let the handshake state machine know that caller has validated 131 // the authentication string obtained via |GetVerificationString()|. 132 // Note: This should only be called when the state returned by 133 // |GetHandshakeState()| is |State::VERIFICATION_IN_PROGRESS|. 134 // 135 // If |false| is returned, check |GetLastError()| for the error message. 136 bool VerifyHandshake(); 137 138 // Can be called to generate a |D2DConnectionContextV1|. Returns nullptr on 139 // failure. 140 // Note: This should only be called when the state returned by 141 // |GetHandshakeState()| is |State::FINISHED|. 142 // 143 // If |nullptr| is returned, check |GetLastError()| for the error message. 144 std::unique_ptr<D2DConnectionContextV1> ToConnectionContext(); 145 146 private: 147 // Enums for internal state machinery. 148 enum class InternalState : int { 149 CLIENT_START, 150 CLIENT_WAITING_FOR_SERVER_INIT, 151 CLIENT_AFTER_SERVER_INIT, 152 153 // Responder/server state 154 SERVER_START, 155 SERVER_AFTER_CLIENT_INIT, 156 SERVER_WAITING_FOR_CLIENT_FINISHED, 157 158 // Common completion state 159 HANDSHAKE_VERIFICATION_NEEDED, 160 HANDSHAKE_VERIFICATION_IN_PROGRESS, 161 HANDSHAKE_FINISHED, 162 HANDSHAKE_ALREADY_USED, 163 HANDSHAKE_ERROR, 164 }; 165 166 // Helps us remember our role in the handshake. 167 enum class HandshakeRole { 168 CLIENT, 169 SERVER 170 }; 171 172 // Prevent public instantiation. Callers should use |ForInitiator()| or 173 // |ForResponder()|. 174 UKey2Handshake(InternalState state, HandshakeCipher cipher); 175 176 // Attempts to parse Ukey2ClientInit, wrapped inside a Ukey2Message. 177 // See go/ukey2 for details. 178 ParseResult ParseClientInitUkey2Message(const string& handshake_message); 179 180 // Attempts to parse Ukey2ServerInit, wrapped inside a Ukey2Message. 181 // See go/ukey2 for details. 182 ParseResult ParseServerInitUkey2Message(const string& handshake_message); 183 184 // Attempts to parse Ukey2ClientFinish, wrapped inside a Ukey2Message. 185 // See go/ukey2 for details. 186 ParseResult ParseClientFinishUkey2Message(const string& handshake_message); 187 188 // Convenience function to set |last_error_| and create a ParseResult with a 189 // given alert. 190 ParseResult CreateFailedResultWithAlert(Ukey2Alert::AlertType alert_type, 191 const string& error_message); 192 193 // Convenience function to set |last_error_| and create a failed ParseResult 194 // without an alert. 195 ParseResult CreateFailedResultWithoutAlert(const string& error_message); 196 197 // Convenience function to create a successful ParseResult. 198 ParseResult CreateSuccessResult(); 199 200 // Verifies that the peer's commitment stored in |peer_commitment_| is the 201 // same as that obtained from |handshake_message|. 202 bool VerifyCommitment(const string& handshake_message); 203 204 // Generates a commitment for the P256_SHA512 cipher. 205 std::unique_ptr<Ukey2ClientInit::CipherCommitment> 206 GenerateP256Sha512Commitment(); 207 208 // Creates a serialized Ukey2Message, wrapping an inner ClientInit message. 209 std::unique_ptr<string> MakeClientInitUkey2Message(); 210 211 // Creates a serialized Ukey2Message, wrapping an inner ServerInit message. 212 std::unique_ptr<string> MakeServerInitUkey2Message(); 213 214 // Creates a serialized Ukey2Message of a given |type|, wrapping |data|. 215 std::unique_ptr<string> MakeUkey2Message(Ukey2Message::Type type, 216 const string& data); 217 218 // Called when an error occurs to set |handshake_state_| and |last_error_|. 219 void SetError(const string& error_message); 220 221 // The current state of the handshake. 222 InternalState handshake_state_; 223 224 // The cipher to use for the handshake. 225 const HandshakeCipher handshake_cipher_; 226 227 // The role to perform, i.e. client or server. 228 const HandshakeRole handshake_role_; 229 230 // A newly generated key-pair for this handshake. 231 std::unique_ptr<securemessage::CryptoOps::KeyPair> our_key_pair_; 232 233 // The peer's public key retrieved from a handshake message. 234 std::unique_ptr<securemessage::CryptoOps::PublicKey> their_public_key_; 235 236 // The secret key derived from |our_key_pair_| and |their_public_key_|. 237 std::unique_ptr<securemessage::CryptoOps::SecretKey> derived_secret_key_; 238 239 // The raw bytes of the Ukey2ClientInit, wrapped inside a Ukey2Message. 240 // Empty string if not initialized. 241 string wrapped_client_init_; 242 243 // The raw bytes of the Ukey2ServerInit, wrapped inside a Ukey2Message. 244 // Empty string if not initialized. 245 string wrapped_server_init_; 246 247 // The commitment of the peer retrieved from a handshake message. Empty string 248 // if not initialized. 249 string peer_commitment_; 250 251 // Map from ciphers to the raw bytes of message 3 (which is a wrapped 252 // Ukey2ClientFinished message). 253 // Note: Currently only one cipher is supported, so at most one entry exists 254 // in this map. 255 std::map<HandshakeCipher, string> raw_message3_map_; 256 257 // Contains the last error message. 258 string last_error_; 259 }; 260 261 } // namespace securegcm 262 263 #endif // SECURITY_CRYPTAUTH_LIB_SECUREGCM_UKEY2_HANDSHAKE_H_ 264