• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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