1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.car.encryptionrunner; 18 19 import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake; 20 21 /** 22 * An {@link EncryptionRunner} that uses Ukey2 as the underlying implementation, and generates a 23 * longer token for the out of band verification step. 24 * 25 * <p>To use this class: 26 * 27 * <p>1. As a client. 28 * 29 * <p>{@code 30 * HandshakeMessage initialClientMessage = clientRunner.initHandshake(); 31 * sendToServer(initialClientMessage.getNextMessage()); 32 * byte message = getServerResponse(); 33 * HandshakeMessage message = clientRunner.continueHandshake(message); 34 * } 35 * 36 * <p>If it is a first-time connection, 37 * 38 * <p>{@code message.getHandshakeState()} should be OOB_VERIFICATION_NEEDED. Wait for an encrypted 39 * message sent from the server, and decrypt that message with an out of band key that was generated 40 * before the start of the handshake. 41 * 42 * <p>After confirming that the decrypted message matches the verification code, send an encrypted 43 * message back to the server, and call {@code HandshakeMessage lastMessage = 44 * clientRunner.verifyPin();} otherwise {@code clientRunner.invalidPin(); } 45 * 46 * <p>Use {@code lastMessage.getKey()} to get the key for encryption. 47 * 48 * <p>If it is a reconnection, 49 * 50 * <p>{@code message.getHandshakeState()} should be RESUMING_SESSION, PIN has been verified blindly, 51 * send the authentication message over to server, then authenticate the message from server. 52 * 53 * <p>{@code 54 * clientMessage = clientRunner.initReconnectAuthentication(previousKey) 55 * sendToServer(clientMessage.getNextMessage()); 56 * HandshakeMessage lastMessage = clientRunner.authenticateReconnection(previousKey, message) 57 * } 58 * 59 * <p>{@code lastMessage.getHandshakeState()} should be FINISHED if reconnection handshake is done. 60 * 61 * <p>2. As a server. 62 * 63 * <p>{@code 64 * byte[] initialMessage = getClientMessageBytes(); 65 * HandshakeMessage message = serverRunner.respondToInitRequest(initialMessage); 66 * sendToClient(message.getNextMessage()); 67 * byte[] clientMessage = getClientResponse(); 68 * HandshakeMessage message = serverRunner.continueHandshake(clientMessage);} 69 * 70 * <p>if it is a first-time connection, 71 * 72 * <p>{@code message.getHandshakeState()} should be OOB_VERIFICATION_NEEDED, send the verification 73 * code to the client, encrypted using an out of band key generated before the start of the 74 * handshake, and wait for a response from the client. 75 * If the decrypted message from the client matches the verification code, call {@code 76 * HandshakeMessage lastMessage = serverRunner.verifyPin}, otherwise 77 * {@code clientRunner.invalidPin(); } 78 * Use {@code lastMessage.getKey()} to get the key for encryption. 79 * 80 * <p>If it is a reconnection, 81 * 82 * <p>{@code message.getHandshakeState()} should be RESUMING_SESSION,PIN has been verified blindly, 83 * waiting for client message. 84 * After client message been received, 85 * {@code serverMessage = serverRunner.authenticateReconnection(previousKey, message); 86 * sendToClient(serverMessage.getNextMessage());} 87 * {@code serverMessage.getHandshakeState()} should be FINISHED if reconnection handshake is done. 88 * 89 * <p>Also see {@link EncryptionRunnerTest} for examples. 90 */ 91 public final class OobUkey2EncryptionRunner extends Ukey2EncryptionRunner { 92 // Choose max verification string length supported by Ukey2 93 private static final int VERIFICATION_STRING_LENGTH = 32; 94 95 @Override continueHandshake(byte[] response)96 public HandshakeMessage continueHandshake(byte[] response) throws HandshakeException { 97 checkInitialized(); 98 99 Ukey2Handshake uKey2Client = getUkey2Client(); 100 101 try { 102 if (uKey2Client.getHandshakeState() != Ukey2Handshake.State.IN_PROGRESS) { 103 throw new IllegalStateException( 104 "handshake is not in progress, state =" + uKey2Client.getHandshakeState()); 105 } 106 uKey2Client.parseHandshakeMessage(response); 107 108 // Not obvious from ukey2 api, but getting the next message can change the state. 109 // calling getNext message might go from in progress to verification needed, on 110 // the assumption that we already send this message to the peer. 111 byte[] nextMessage = null; 112 if (uKey2Client.getHandshakeState() == Ukey2Handshake.State.IN_PROGRESS) { 113 nextMessage = uKey2Client.getNextHandshakeMessage(); 114 } 115 116 byte[] verificationCode = null; 117 if (uKey2Client.getHandshakeState() == Ukey2Handshake.State.VERIFICATION_NEEDED) { 118 // getVerificationString() needs to be called before notifyPinVerified(). 119 verificationCode = uKey2Client.getVerificationString(VERIFICATION_STRING_LENGTH); 120 if (isReconnect()) { 121 HandshakeMessage handshakeMessage = verifyPin(); 122 return HandshakeMessage.newBuilder() 123 .setHandshakeState(handshakeMessage.getHandshakeState()) 124 .setNextMessage(nextMessage) 125 .build(); 126 } 127 } 128 129 return HandshakeMessage.newBuilder() 130 .setHandshakeState(HandshakeMessage.HandshakeState.OOB_VERIFICATION_NEEDED) 131 .setNextMessage(nextMessage) 132 .setOobVerificationCode(verificationCode) 133 .build(); 134 } catch (com.google.security.cryptauth.lib.securegcm.HandshakeException 135 | Ukey2Handshake.AlertException e) { 136 throw new HandshakeException(e); 137 } 138 } 139 } 140