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 package com.google.security.cryptauth.lib.securegcm; 16 17 import com.google.protobuf.ByteString; 18 import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.AlertException; 19 import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.HandshakeCipher; 20 import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.State; 21 import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientFinished; 22 import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientInit; 23 import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientInit.CipherCommitment; 24 import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2Message; 25 import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ServerInit; 26 import java.util.Arrays; 27 import java.util.HashSet; 28 import java.util.Set; 29 import junit.framework.TestCase; 30 import org.junit.Assert; 31 32 /** 33 * Android compatible tests for the {@link Ukey2Handshake} class. 34 */ 35 public class Ukey2HandshakeTest extends TestCase { 36 37 private static final int MAX_AUTH_STRING_LENGTH = 32; 38 39 @Override setUp()40 protected void setUp() throws Exception { 41 KeyEncodingTest.installSunEcSecurityProviderIfNecessary(); 42 super.setUp(); 43 } 44 45 /** 46 * Tests correct use 47 */ testHandshake()48 public void testHandshake() throws Exception { 49 if (KeyEncoding.isLegacyCryptoRequired()) { 50 // this means we're running on an old SDK, which doesn't support the 51 // necessary crypto. Let's not test anything in this case. 52 return; 53 } 54 55 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 56 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 57 byte[] handshakeMessage; 58 59 assertEquals(State.IN_PROGRESS, client.getHandshakeState()); 60 assertEquals(State.IN_PROGRESS, server.getHandshakeState()); 61 62 // Message 1 (Client Init) 63 handshakeMessage = client.getNextHandshakeMessage(); 64 server.parseHandshakeMessage(handshakeMessage); 65 assertEquals(State.IN_PROGRESS, client.getHandshakeState()); 66 assertEquals(State.IN_PROGRESS, server.getHandshakeState()); 67 68 // Message 2 (Server Init) 69 handshakeMessage = server.getNextHandshakeMessage(); 70 client.parseHandshakeMessage(handshakeMessage); 71 assertEquals(State.IN_PROGRESS, client.getHandshakeState()); 72 assertEquals(State.IN_PROGRESS, server.getHandshakeState()); 73 74 // Message 3 (Client Finish) 75 handshakeMessage = client.getNextHandshakeMessage(); 76 server.parseHandshakeMessage(handshakeMessage); 77 assertEquals(State.VERIFICATION_NEEDED, client.getHandshakeState()); 78 assertEquals(State.VERIFICATION_NEEDED, server.getHandshakeState()); 79 80 // Get the auth string 81 byte[] clientAuthString = client.getVerificationString(MAX_AUTH_STRING_LENGTH); 82 byte[] serverAuthString = server.getVerificationString(MAX_AUTH_STRING_LENGTH); 83 Assert.assertArrayEquals(clientAuthString, serverAuthString); 84 assertEquals(State.VERIFICATION_IN_PROGRESS, client.getHandshakeState()); 85 assertEquals(State.VERIFICATION_IN_PROGRESS, server.getHandshakeState()); 86 87 // Verify the auth string 88 client.verifyHandshake(); 89 server.verifyHandshake(); 90 assertEquals(State.FINISHED, client.getHandshakeState()); 91 assertEquals(State.FINISHED, server.getHandshakeState()); 92 93 // Make a context 94 D2DConnectionContext clientContext = client.toConnectionContext(); 95 D2DConnectionContext serverContext = server.toConnectionContext(); 96 assertContextsCompatible(clientContext, serverContext); 97 assertEquals(State.ALREADY_USED, client.getHandshakeState()); 98 assertEquals(State.ALREADY_USED, server.getHandshakeState()); 99 } 100 101 /** 102 * Verify enums for ciphers match the proto values 103 */ testCipherEnumValuesCorrect()104 public void testCipherEnumValuesCorrect() { 105 assertEquals( 106 "You added a cipher, but forgot to change the test", 1, HandshakeCipher.values().length); 107 108 assertEquals(UkeyProto.Ukey2HandshakeCipher.P256_SHA512, 109 HandshakeCipher.P256_SHA512.getValue()); 110 } 111 112 /** 113 * Tests incorrect use by callers (client and servers accidentally sending the wrong message at 114 * the wrong time) 115 */ testHandshakeClientAndServerSendRepeatedOutOfOrderMessages()116 public void testHandshakeClientAndServerSendRepeatedOutOfOrderMessages() throws Exception { 117 if (KeyEncoding.isLegacyCryptoRequired()) { 118 // this means we're running on an old SDK, which doesn't support the 119 // necessary crypto. Let's not test anything in this case. 120 return; 121 } 122 123 // Client sends ClientInit (again) instead of ClientFinished 124 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 125 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 126 byte[] handshakeMessage = client.getNextHandshakeMessage(); 127 server.parseHandshakeMessage(handshakeMessage); 128 server.getNextHandshakeMessage(); // do this to avoid illegal state 129 try { 130 server.parseHandshakeMessage(handshakeMessage); 131 fail("Expected Alert for client sending ClientInit twice"); 132 } catch (HandshakeException e) { 133 // success 134 } 135 assertEquals(State.ERROR, server.getHandshakeState()); 136 137 // Server sends ClientInit back to client instead of ServerInit 138 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 139 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 140 handshakeMessage = client.getNextHandshakeMessage(); 141 try { 142 client.parseHandshakeMessage(handshakeMessage); 143 fail("Expected Alert for server sending ClientInit back to client"); 144 } catch (AlertException e) { 145 // success 146 } 147 assertEquals(State.ERROR, client.getHandshakeState()); 148 149 // Clients sends ServerInit back to client instead of ClientFinished 150 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 151 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 152 handshakeMessage = client.getNextHandshakeMessage(); 153 server.parseHandshakeMessage(handshakeMessage); 154 handshakeMessage = server.getNextHandshakeMessage(); 155 try { 156 server.parseHandshakeMessage(handshakeMessage); 157 fail("Expected Alert for client sending ServerInit back to server"); 158 } catch (HandshakeException e) { 159 // success 160 } 161 assertEquals(State.ERROR, server.getHandshakeState()); 162 } 163 164 /** 165 * Tests that verification codes are different for different handshake runs. Also tests a full 166 * man-in-the-middle attack. 167 */ testVerificationCodeUniqueToSession()168 public void testVerificationCodeUniqueToSession() throws Exception { 169 if (KeyEncoding.isLegacyCryptoRequired()) { 170 // this means we're running on an old SDK, which doesn't support the 171 // necessary crypto. Let's not test anything in this case. 172 return; 173 } 174 175 // Client 1 and Server 1 176 Ukey2Handshake client1 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 177 Ukey2Handshake server1 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 178 byte[] handshakeMessage = client1.getNextHandshakeMessage(); 179 server1.parseHandshakeMessage(handshakeMessage); 180 handshakeMessage = server1.getNextHandshakeMessage(); 181 client1.parseHandshakeMessage(handshakeMessage); 182 handshakeMessage = client1.getNextHandshakeMessage(); 183 server1.parseHandshakeMessage(handshakeMessage); 184 byte[] client1AuthString = client1.getVerificationString(MAX_AUTH_STRING_LENGTH); 185 byte[] server1AuthString = server1.getVerificationString(MAX_AUTH_STRING_LENGTH); 186 Assert.assertArrayEquals(client1AuthString, server1AuthString); 187 188 // Client 2 and Server 2 189 Ukey2Handshake client2 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 190 Ukey2Handshake server2 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 191 handshakeMessage = client2.getNextHandshakeMessage(); 192 server2.parseHandshakeMessage(handshakeMessage); 193 handshakeMessage = server2.getNextHandshakeMessage(); 194 client2.parseHandshakeMessage(handshakeMessage); 195 handshakeMessage = client2.getNextHandshakeMessage(); 196 server2.parseHandshakeMessage(handshakeMessage); 197 byte[] client2AuthString = client2.getVerificationString(MAX_AUTH_STRING_LENGTH); 198 byte[] server2AuthString = server2.getVerificationString(MAX_AUTH_STRING_LENGTH); 199 Assert.assertArrayEquals(client2AuthString, server2AuthString); 200 201 // Make sure the verification strings differ 202 assertFalse(Arrays.equals(client1AuthString, client2AuthString)); 203 } 204 205 /** 206 * Test an attack where the adversary swaps out the public key in the final message (i.e., 207 * commitment doesn't match public key) 208 */ testPublicKeyDoesntMatchCommitment()209 public void testPublicKeyDoesntMatchCommitment() throws Exception { 210 if (KeyEncoding.isLegacyCryptoRequired()) { 211 // this means we're running on an old SDK, which doesn't support the 212 // necessary crypto. Let's not test anything in this case. 213 return; 214 } 215 216 // Run handshake as usual, but stop before sending client finished 217 Ukey2Handshake client1 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 218 Ukey2Handshake server1 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 219 byte[] handshakeMessage = client1.getNextHandshakeMessage(); 220 server1.parseHandshakeMessage(handshakeMessage); 221 handshakeMessage = server1.getNextHandshakeMessage(); 222 223 // Run another handshake and get the final client finished 224 Ukey2Handshake client2 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 225 Ukey2Handshake server2 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 226 handshakeMessage = client2.getNextHandshakeMessage(); 227 server2.parseHandshakeMessage(handshakeMessage); 228 handshakeMessage = server2.getNextHandshakeMessage(); 229 client2.parseHandshakeMessage(handshakeMessage); 230 handshakeMessage = client2.getNextHandshakeMessage(); 231 232 // Now use the client finished from second handshake in first handshake (simulates where an 233 // attacker switches out the last message). 234 try { 235 server1.parseHandshakeMessage(handshakeMessage); 236 fail("Expected server to catch mismatched ClientFinished"); 237 } catch (HandshakeException e) { 238 // success 239 } 240 assertEquals(State.ERROR, server1.getHandshakeState()); 241 242 // Make sure caller can't actually do anything with the server now that an error has occurred 243 try { 244 server1.getVerificationString(MAX_AUTH_STRING_LENGTH); 245 fail("Server allows operations post error"); 246 } catch (IllegalStateException e) { 247 // success 248 } 249 try { 250 server1.verifyHandshake(); 251 fail("Server allows operations post error"); 252 } catch (IllegalStateException e) { 253 // success 254 } 255 try { 256 server1.toConnectionContext(); 257 fail("Server allows operations post error"); 258 } catch (IllegalStateException e) { 259 // success 260 } 261 } 262 263 /** 264 * Test commitment having unsupported version 265 */ testClientInitUnsupportedVersion()266 public void testClientInitUnsupportedVersion() throws Exception { 267 if (KeyEncoding.isLegacyCryptoRequired()) { 268 // this means we're running on an old SDK, which doesn't support the 269 // necessary crypto. Let's not test anything in this case. 270 return; 271 } 272 273 // Get ClientInit and modify the version to be too big 274 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 275 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 276 byte[] handshakeMessage = client.getNextHandshakeMessage(); 277 278 Ukey2Message.Builder message = Ukey2Message.newBuilder( 279 Ukey2Message.parseFrom(handshakeMessage)); 280 Ukey2ClientInit.Builder clientInit = 281 Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData())); 282 clientInit.setVersion(Ukey2Handshake.VERSION + 1); 283 message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray())); 284 handshakeMessage = message.build().toByteArray(); 285 286 try { 287 server.parseHandshakeMessage(handshakeMessage); 288 fail("Server did not catch unsupported version (too big) in ClientInit"); 289 } catch (AlertException e) { 290 // success 291 } 292 assertEquals(State.ERROR, server.getHandshakeState()); 293 294 // Get ClientInit and modify the version to be too big 295 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 296 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 297 handshakeMessage = client.getNextHandshakeMessage(); 298 299 message = Ukey2Message.newBuilder( 300 Ukey2Message.parseFrom(handshakeMessage)); 301 clientInit = Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData())); 302 clientInit.setVersion(0 /* minimum version is 1 */); 303 message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray())); 304 handshakeMessage = message.build().toByteArray(); 305 306 try { 307 server.parseHandshakeMessage(handshakeMessage); 308 fail("Server did not catch unsupported version (too small) in ClientInit"); 309 } catch (AlertException e) { 310 // success 311 } 312 assertEquals(State.ERROR, server.getHandshakeState()); 313 } 314 315 /** 316 * Tests that server catches wrong number of random bytes in ClientInit 317 */ testWrongNonceLengthInClientInit()318 public void testWrongNonceLengthInClientInit() throws Exception { 319 if (KeyEncoding.isLegacyCryptoRequired()) { 320 // this means we're running on an old SDK, which doesn't support the 321 // necessary crypto. Let's not test anything in this case. 322 return; 323 } 324 325 // Get ClientInit and modify the nonce 326 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 327 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 328 byte[] handshakeMessage = client.getNextHandshakeMessage(); 329 330 Ukey2Message.Builder message = Ukey2Message.newBuilder( 331 Ukey2Message.parseFrom(handshakeMessage)); 332 Ukey2ClientInit.Builder clientInit = 333 Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData())); 334 clientInit.setRandom( 335 ByteString.copyFrom( 336 Arrays.copyOf( 337 clientInit.getRandom().toByteArray(), 338 31 /* as per go/ukey2, nonces must be 32 bytes long */))); 339 message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray())); 340 handshakeMessage = message.build().toByteArray(); 341 342 try { 343 server.parseHandshakeMessage(handshakeMessage); 344 fail("Server did not catch nonce being too short in ClientInit"); 345 } catch (AlertException e) { 346 // success 347 } 348 assertEquals(State.ERROR, server.getHandshakeState()); 349 } 350 351 /** 352 * Test that server catches missing commitment in ClientInit message 353 */ testServerCatchesMissingCommitmentInClientInit()354 public void testServerCatchesMissingCommitmentInClientInit() throws Exception { 355 if (KeyEncoding.isLegacyCryptoRequired()) { 356 // this means we're running on an old SDK, which doesn't support the 357 // necessary crypto. Let's not test anything in this case. 358 return; 359 } 360 361 // Get ClientInit and modify the commitment 362 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 363 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 364 byte[] handshakeMessage = client.getNextHandshakeMessage(); 365 366 Ukey2Message.Builder message = Ukey2Message.newBuilder( 367 Ukey2Message.parseFrom(handshakeMessage)); 368 Ukey2ClientInit clientInit = 369 Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData())) 370 .build(); 371 Ukey2ClientInit.Builder badClientInit = Ukey2ClientInit.newBuilder() 372 .setVersion(clientInit.getVersion()) 373 .setRandom(clientInit.getRandom()); 374 for (CipherCommitment commitment : clientInit.getCipherCommitmentsList()) { 375 badClientInit.addCipherCommitments(commitment); 376 } 377 378 message.setMessageData(ByteString.copyFrom(badClientInit.build().toByteArray())); 379 handshakeMessage = message.build().toByteArray(); 380 381 try { 382 server.parseHandshakeMessage(handshakeMessage); 383 fail("Server did not catch missing commitment in ClientInit"); 384 } catch (AlertException e) { 385 // success 386 } 387 } 388 389 /** 390 * Test that client catches invalid version in ServerInit 391 */ testServerInitUnsupportedVersion()392 public void testServerInitUnsupportedVersion() throws Exception { 393 if (KeyEncoding.isLegacyCryptoRequired()) { 394 // this means we're running on an old SDK, which doesn't support the 395 // necessary crypto. Let's not test anything in this case. 396 return; 397 } 398 399 // Get ServerInit and modify the version to be too big 400 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 401 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 402 byte[] handshakeMessage = client.getNextHandshakeMessage(); 403 server.parseHandshakeMessage(handshakeMessage); 404 handshakeMessage = server.getNextHandshakeMessage(); 405 406 Ukey2Message.Builder message = Ukey2Message.newBuilder( 407 Ukey2Message.parseFrom(handshakeMessage)); 408 Ukey2ServerInit serverInit = 409 Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData())) 410 .setVersion(Ukey2Handshake.VERSION + 1) 411 .build(); 412 message.setMessageData(ByteString.copyFrom(serverInit.toByteArray())); 413 handshakeMessage = message.build().toByteArray(); 414 415 try { 416 client.parseHandshakeMessage(handshakeMessage); 417 fail("Client did not catch unsupported version (too big) in ServerInit"); 418 } catch (AlertException e) { 419 // success 420 } 421 assertEquals(State.ERROR, client.getHandshakeState()); 422 423 // Get ServerInit and modify the version to be too big 424 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 425 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 426 handshakeMessage = client.getNextHandshakeMessage(); 427 server.parseHandshakeMessage(handshakeMessage); 428 handshakeMessage = server.getNextHandshakeMessage(); 429 430 message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage)); 431 serverInit = 432 Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData())) 433 .setVersion(0 /* minimum version is 1 */) 434 .build(); 435 message.setMessageData(ByteString.copyFrom(serverInit.toByteArray())); 436 handshakeMessage = message.build().toByteArray(); 437 438 try { 439 client.parseHandshakeMessage(handshakeMessage); 440 fail("Client did not catch unsupported version (too small) in ServerInit"); 441 } catch (AlertException e) { 442 // success 443 } 444 assertEquals(State.ERROR, client.getHandshakeState()); 445 } 446 447 /** 448 * Tests that client catches wrong number of random bytes in ServerInit 449 */ testWrongNonceLengthInServerInit()450 public void testWrongNonceLengthInServerInit() throws Exception { 451 if (KeyEncoding.isLegacyCryptoRequired()) { 452 // this means we're running on an old SDK, which doesn't support the 453 // necessary crypto. Let's not test anything in this case. 454 return; 455 } 456 457 // Get ServerInit and modify the nonce 458 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 459 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 460 byte[] handshakeMessage = client.getNextHandshakeMessage(); 461 server.parseHandshakeMessage(handshakeMessage); 462 handshakeMessage = server.getNextHandshakeMessage(); 463 464 Ukey2Message.Builder message = Ukey2Message.newBuilder( 465 Ukey2Message.parseFrom(handshakeMessage)); 466 Ukey2ServerInit.Builder serverInitBuilder = 467 Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData())); 468 Ukey2ServerInit serverInit = serverInitBuilder.setRandom(ByteString.copyFrom(Arrays.copyOf( 469 serverInitBuilder.getRandom().toByteArray(), 470 31 /* as per go/ukey2, nonces must be 32 bytes long */))) 471 .build(); 472 message.setMessageData(ByteString.copyFrom(serverInit.toByteArray())); 473 handshakeMessage = message.build().toByteArray(); 474 475 try { 476 client.parseHandshakeMessage(handshakeMessage); 477 fail("Client did not catch nonce being too short in ServerInit"); 478 } catch (AlertException e) { 479 // success 480 } 481 assertEquals(State.ERROR, client.getHandshakeState()); 482 } 483 484 /** 485 * Test that client catches missing or incorrect handshake cipher in serverInit 486 */ testMissingOrIncorrectHandshakeCipherInServerInit()487 public void testMissingOrIncorrectHandshakeCipherInServerInit() throws Exception { 488 if (KeyEncoding.isLegacyCryptoRequired()) { 489 // this means we're running on an old SDK, which doesn't support the 490 // necessary crypto. Let's not test anything in this case. 491 return; 492 } 493 494 // Get ServerInit 495 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 496 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 497 byte[] handshakeMessage = client.getNextHandshakeMessage(); 498 server.parseHandshakeMessage(handshakeMessage); 499 handshakeMessage = server.getNextHandshakeMessage(); 500 Ukey2Message.Builder message = Ukey2Message.newBuilder( 501 Ukey2Message.parseFrom(handshakeMessage)); 502 Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData()); 503 504 // remove handshake cipher 505 Ukey2ServerInit badServerInit = Ukey2ServerInit.newBuilder() 506 .setPublicKey(serverInit.getPublicKey()) 507 .setRandom(serverInit.getRandom()) 508 .setVersion(serverInit.getVersion()) 509 .build(); 510 511 message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray())); 512 handshakeMessage = message.build().toByteArray(); 513 514 try { 515 client.parseHandshakeMessage(handshakeMessage); 516 fail("Client did not catch missing handshake cipher in ServerInit"); 517 } catch (AlertException e) { 518 // success 519 } 520 assertEquals(State.ERROR, client.getHandshakeState()); 521 522 // Get ServerInit 523 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 524 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 525 handshakeMessage = client.getNextHandshakeMessage(); 526 server.parseHandshakeMessage(handshakeMessage); 527 handshakeMessage = server.getNextHandshakeMessage(); 528 message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage)); 529 serverInit = Ukey2ServerInit.parseFrom(message.getMessageData()); 530 531 // put in a bad handshake cipher 532 badServerInit = Ukey2ServerInit.newBuilder() 533 .setPublicKey(serverInit.getPublicKey()) 534 .setRandom(serverInit.getRandom()) 535 .setVersion(serverInit.getVersion()) 536 .setHandshakeCipher(UkeyProto.Ukey2HandshakeCipher.RESERVED) 537 .build(); 538 539 message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray())); 540 handshakeMessage = message.build().toByteArray(); 541 542 try { 543 client.parseHandshakeMessage(handshakeMessage); 544 fail("Client did not catch bad handshake cipher in ServerInit"); 545 } catch (AlertException e) { 546 // success 547 } 548 assertEquals(State.ERROR, client.getHandshakeState()); 549 } 550 551 /** 552 * Test that client catches missing or incorrect public key in serverInit 553 */ testMissingOrIncorrectPublicKeyInServerInit()554 public void testMissingOrIncorrectPublicKeyInServerInit() throws Exception { 555 if (KeyEncoding.isLegacyCryptoRequired()) { 556 // this means we're running on an old SDK, which doesn't support the 557 // necessary crypto. Let's not test anything in this case. 558 return; 559 } 560 561 // Get ServerInit 562 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 563 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 564 byte[] handshakeMessage = client.getNextHandshakeMessage(); 565 server.parseHandshakeMessage(handshakeMessage); 566 handshakeMessage = server.getNextHandshakeMessage(); 567 Ukey2Message.Builder message = Ukey2Message.newBuilder( 568 Ukey2Message.parseFrom(handshakeMessage)); 569 Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData()); 570 571 // remove public key 572 Ukey2ServerInit badServerInit = Ukey2ServerInit.newBuilder() 573 .setRandom(serverInit.getRandom()) 574 .setVersion(serverInit.getVersion()) 575 .setHandshakeCipher(serverInit.getHandshakeCipher()) 576 .build(); 577 578 message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray())); 579 handshakeMessage = message.build().toByteArray(); 580 581 try { 582 client.parseHandshakeMessage(handshakeMessage); 583 fail("Client did not catch missing public key in ServerInit"); 584 } catch (AlertException e) { 585 // success 586 } 587 assertEquals(State.ERROR, client.getHandshakeState()); 588 589 // Get ServerInit 590 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 591 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 592 handshakeMessage = client.getNextHandshakeMessage(); 593 server.parseHandshakeMessage(handshakeMessage); 594 handshakeMessage = server.getNextHandshakeMessage(); 595 message = Ukey2Message.newBuilder( 596 Ukey2Message.parseFrom(handshakeMessage)); 597 serverInit = Ukey2ServerInit.parseFrom(message.getMessageData()); 598 599 // put in a bad public key 600 badServerInit = Ukey2ServerInit.newBuilder() 601 .setPublicKey(ByteString.copyFrom(new byte[] {42, 12, 1})) 602 .setRandom(serverInit.getRandom()) 603 .setVersion(serverInit.getVersion()) 604 .setHandshakeCipher(serverInit.getHandshakeCipher()) 605 .build(); 606 607 message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray())); 608 handshakeMessage = message.build().toByteArray(); 609 610 try { 611 client.parseHandshakeMessage(handshakeMessage); 612 fail("Client did not catch bad public key in ServerInit"); 613 } catch (AlertException e) { 614 // success 615 } 616 assertEquals(State.ERROR, client.getHandshakeState()); 617 } 618 619 /** 620 * Test that client catches missing or incorrect public key in clientFinished 621 */ testMissingOrIncorrectPublicKeyInClientFinished()622 public void testMissingOrIncorrectPublicKeyInClientFinished() throws Exception { 623 if (KeyEncoding.isLegacyCryptoRequired()) { 624 // this means we're running on an old SDK, which doesn't support the 625 // necessary crypto. Let's not test anything in this case. 626 return; 627 } 628 629 // Get ClientFinished 630 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 631 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 632 byte[] handshakeMessage = client.getNextHandshakeMessage(); 633 server.parseHandshakeMessage(handshakeMessage); 634 handshakeMessage = server.getNextHandshakeMessage(); 635 client.parseHandshakeMessage(handshakeMessage); 636 handshakeMessage = client.getNextHandshakeMessage(); 637 Ukey2Message.Builder message = Ukey2Message.newBuilder( 638 Ukey2Message.parseFrom(handshakeMessage)); 639 640 // remove public key 641 Ukey2ClientFinished.Builder badClientFinished = Ukey2ClientFinished.newBuilder(); 642 643 message.setMessageData(ByteString.copyFrom(badClientFinished.build().toByteArray())); 644 handshakeMessage = message.build().toByteArray(); 645 646 try { 647 server.parseHandshakeMessage(handshakeMessage); 648 fail("Server did not catch missing public key in ClientFinished"); 649 } catch (HandshakeException e) { 650 // success 651 } 652 assertEquals(State.ERROR, server.getHandshakeState()); 653 654 // Get ClientFinished 655 client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 656 server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 657 handshakeMessage = client.getNextHandshakeMessage(); 658 server.parseHandshakeMessage(handshakeMessage); 659 handshakeMessage = server.getNextHandshakeMessage(); 660 client.parseHandshakeMessage(handshakeMessage); 661 handshakeMessage = client.getNextHandshakeMessage(); 662 message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage)); 663 664 // remove public key 665 badClientFinished = Ukey2ClientFinished.newBuilder() 666 .setPublicKey(ByteString.copyFrom(new byte[] {42, 12, 1})); 667 668 message.setMessageData(ByteString.copyFrom(badClientFinished.build().toByteArray())); 669 handshakeMessage = message.build().toByteArray(); 670 671 try { 672 server.parseHandshakeMessage(handshakeMessage); 673 fail("Server did not catch bad public key in ClientFinished"); 674 } catch (HandshakeException e) { 675 // success 676 } 677 assertEquals(State.ERROR, server.getHandshakeState()); 678 } 679 680 /** 681 * Tests that items (nonces, commitments, public keys) that should be random are at least 682 * different on every run. 683 */ testRandomItemsDifferentOnEveryRun()684 public void testRandomItemsDifferentOnEveryRun() throws Exception { 685 if (KeyEncoding.isLegacyCryptoRequired()) { 686 // this means we're running on an old SDK, which doesn't support the 687 // necessary crypto. Let's not test anything in this case. 688 return; 689 } 690 691 int numberOfRuns = 50; 692 693 // Search for collisions 694 Set<Integer> commitments = new HashSet<>(numberOfRuns); 695 Set<Integer> clientNonces = new HashSet<>(numberOfRuns); 696 Set<Integer> serverNonces = new HashSet<>(numberOfRuns); 697 Set<Integer> serverPublicKeys = new HashSet<>(numberOfRuns); 698 Set<Integer> clientPublicKeys = new HashSet<>(numberOfRuns); 699 700 for (int i = 0; i < numberOfRuns; i++) { 701 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 702 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 703 byte[] handshakeMessage = client.getNextHandshakeMessage(); 704 Ukey2Message message = Ukey2Message.parseFrom(handshakeMessage); 705 Ukey2ClientInit clientInit = Ukey2ClientInit.parseFrom(message.getMessageData()); 706 707 server.parseHandshakeMessage(handshakeMessage); 708 handshakeMessage = server.getNextHandshakeMessage(); 709 message = Ukey2Message.parseFrom(handshakeMessage); 710 Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData()); 711 712 client.parseHandshakeMessage(handshakeMessage); 713 handshakeMessage = client.getNextHandshakeMessage(); 714 message = Ukey2Message.parseFrom(handshakeMessage); 715 Ukey2ClientFinished clientFinished = Ukey2ClientFinished.parseFrom(message.getMessageData()); 716 717 // Clean up to save some memory (b/32054837) 718 client = null; 719 server = null; 720 handshakeMessage = null; 721 message = null; 722 System.gc(); 723 724 // ClientInit randomness 725 Integer nonceHash = Integer.valueOf(Arrays.hashCode(clientInit.getRandom().toByteArray())); 726 if (clientNonces.contains(nonceHash) || serverNonces.contains(nonceHash)) { 727 fail("Nonce in ClientINit has repeated!"); 728 } 729 clientNonces.add(nonceHash); 730 731 Integer commitmentHash = 0; 732 for (CipherCommitment commitement : clientInit.getCipherCommitmentsList()) { 733 commitmentHash += Arrays.hashCode(commitement.toByteArray()); 734 } 735 if (commitments.contains(nonceHash)) { 736 fail("Commitment has repeated!"); 737 } 738 commitments.add(commitmentHash); 739 740 // ServerInit randomness 741 nonceHash = Integer.valueOf(Arrays.hashCode(serverInit.getRandom().toByteArray())); 742 if (serverNonces.contains(nonceHash) || clientNonces.contains(nonceHash)) { 743 fail("Nonce in ServerInit repeated!"); 744 } 745 serverNonces.add(nonceHash); 746 747 Integer publicKeyHash = 748 Integer.valueOf(Arrays.hashCode(serverInit.getPublicKey().toByteArray())); 749 if (serverPublicKeys.contains(publicKeyHash) || clientPublicKeys.contains(publicKeyHash)) { 750 fail("Public Key in ServerInit repeated!"); 751 } 752 serverPublicKeys.add(publicKeyHash); 753 754 // Client Finished randomness 755 publicKeyHash = Integer.valueOf(Arrays.hashCode(clientFinished.getPublicKey().toByteArray())); 756 if (serverPublicKeys.contains(publicKeyHash) || clientPublicKeys.contains(publicKeyHash)) { 757 fail("Public Key in ClientFinished repeated!"); 758 } 759 clientPublicKeys.add(publicKeyHash); 760 } 761 } 762 763 /** 764 * Tests that {@link Ukey2Handshake#getVerificationString(int)} enforces sane verification string 765 * lengths. 766 */ testGetVerificationEnforcesSaneLengths()767 public void testGetVerificationEnforcesSaneLengths() throws Exception { 768 if (KeyEncoding.isLegacyCryptoRequired()) { 769 // this means we're running on an old SDK, which doesn't support the 770 // necessary crypto. Let's not test anything in this case. 771 return; 772 } 773 774 // Run the protocol 775 Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512); 776 Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512); 777 byte[] handshakeMessage = client.getNextHandshakeMessage(); 778 server.parseHandshakeMessage(handshakeMessage); 779 handshakeMessage = server.getNextHandshakeMessage(); 780 client.parseHandshakeMessage(handshakeMessage); 781 handshakeMessage = client.getNextHandshakeMessage(); 782 server.parseHandshakeMessage(handshakeMessage); 783 784 // Try to get too short verification string 785 try { 786 client.getVerificationString(0); 787 fail("Too short verification string allowed"); 788 } catch (IllegalArgumentException e) { 789 // success 790 } 791 792 // Try to get too long verification string 793 try { 794 server.getVerificationString(MAX_AUTH_STRING_LENGTH + 1); 795 fail("Too long verification string allowed"); 796 } catch (IllegalArgumentException e) { 797 // success 798 } 799 } 800 801 /** 802 * Asserts that the given client and server contexts are compatible 803 */ assertContextsCompatible( D2DConnectionContext clientContext, D2DConnectionContext serverContext)804 private void assertContextsCompatible( 805 D2DConnectionContext clientContext, D2DConnectionContext serverContext) { 806 assertNotNull(clientContext); 807 assertNotNull(serverContext); 808 assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, clientContext.getProtocolVersion()); 809 assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, serverContext.getProtocolVersion()); 810 assertEquals(clientContext.getEncodeKey(), serverContext.getDecodeKey()); 811 assertEquals(clientContext.getDecodeKey(), serverContext.getEncodeKey()); 812 assertFalse(clientContext.getEncodeKey().equals(clientContext.getDecodeKey())); 813 assertEquals(0, clientContext.getSequenceNumberForEncoding()); 814 assertEquals(0, clientContext.getSequenceNumberForDecoding()); 815 assertEquals(0, serverContext.getSequenceNumberForEncoding()); 816 assertEquals(0, serverContext.getSequenceNumberForDecoding()); 817 } 818 } 819