/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car.encryptionrunner; import android.annotation.NonNull; /** * A generalized interface that allows for generating shared secrets as well as encrypting * messages. * * To use this interface: * *

1. As a client. * * {@code * HandshakeMessage initialClientMessage = clientRunner.initHandshake(); * sendToServer(initialClientMessage.getNextMessage()); * byte message = getServerResponse(); * HandshakeMessage message = clientRunner.continueHandshake(message); * } * *

If it is a first-time connection, * * {@code message.getHandshakeState()} should be VERIFICATION_NEEDED, show user the verification * code and ask to verify. * After user confirmed, {@code HandshakeMessage lastMessage = clientRunner.verifyPin();} otherwise * {@code clientRunner.invalidPin(); } * * Use {@code lastMessage.getKey()} to get the key for encryption. * *

If it is a reconnection, * * {@code message.getHandshakeState()} should be RESUMING_SESSION, PIN has been verified blindly, * send the authentication message over to server, then authenticate the message from server. * * {@code * clientMessage = clientRunner.initReconnectAuthentication(previousKey) * sendToServer(clientMessage.getNextMessage()); * HandshakeMessage lastMessage = clientRunner.authenticateReconnection(previousKey, message) * } * * {@code lastMessage.getHandshakeState()} should be FINISHED if reconnection handshake is done. * *

2. As a server. * * {@code * byte[] initialMessage = getClientMessageBytes(); * HandshakeMessage message = serverRunner.respondToInitRequest(initialMessage); * sendToClient(message.getNextMessage()); * byte[] clientMessage = getClientResponse(); * HandshakeMessage message = serverRunner.continueHandshake(clientMessage);} * *

if it is a first-time connection, * * {@code message.getHandshakeState()} should be VERIFICATION_NEEDED, show user the verification * code and ask to verify. * After PIN is confirmed, {@code HandshakeMessage lastMessage = serverRunner.verifyPin}, otherwise * {@code clientRunner.invalidPin(); } * Use {@code lastMessage.getKey()} to get the key for encryption. * *

If it is a reconnection, * * {@code message.getHandshakeState()} should be RESUMING_SESSION,PIN has been verified blindly, * waiting for client message. * After client message been received, * {@code serverMessage = serverRunner.authenticateReconnection(previousKey, message); * sendToClient(serverMessage.getNextMessage());} * {@code serverMessage.getHandshakeState()} should be FINISHED if reconnection handshake is done. * * Also see {@link EncryptionRunnerTest} for examples. */ public interface EncryptionRunner { String TAG = "EncryptionRunner"; /** * Starts an encryption handshake. * * @return A handshake message with information about the handshake that is started. */ @NonNull HandshakeMessage initHandshake(); /** * Starts an encryption handshake where the device that is being communicated with already * initiated the request. * * @param initializationRequest the bytes that the other device sent over. * @return a handshake message with information about the handshake. * @throws HandshakeException if initialization request is invalid. */ @NonNull HandshakeMessage respondToInitRequest(@NonNull byte[] initializationRequest) throws HandshakeException; /** * Continues a handshake after receiving another response from the connected device. * * @param response the response from the other device. * @return a message that can be used to continue the handshake. * @throws HandshakeException if unexpected bytes in response. */ @NonNull HandshakeMessage continueHandshake(@NonNull byte[] response) throws HandshakeException; /** * Verifies the pin shown to the user. The result is the next handshake message and will * typically contain an encryption key. * * @throws HandshakeException if not in state to verify pin. */ @NonNull HandshakeMessage verifyPin() throws HandshakeException; /** * Notifies the encryption runner that the user failed to validate the pin. After calling this * method the runner should not be used, and will throw exceptions. */ void invalidPin(); /** * Verifies the reconnection message. * *

The message passed to this method should have been generated by * {@link #initReconnectAuthentication(byte[] previousKey)}. * *

If the message is valid, then a {@link HandshakeMessage} will be returned that contains * the encryption key and a handshake message which can be used to verify the other side of the * connection. * * @param previousKey previously stored key. * @param message message from the client * @return a handshake message with an encryption key if verification succeed. * @throws HandshakeException if the message does not match. */ @NonNull HandshakeMessage authenticateReconnection(@NonNull byte[] message, @NonNull byte[] previousKey) throws HandshakeException; /** * Initiates the reconnection verification by generating a message that should be sent to the * device that is being reconnected to. * * @param previousKey previously stored key. * @return a handshake message with client's message which will be sent to server. * @throws HandshakeException when get encryption key's unique session fail. */ @NonNull HandshakeMessage initReconnectAuthentication(@NonNull byte[] previousKey) throws HandshakeException; /** * De-serializes a previously serialized key generated by an instance of this encryption runner. * * @param serialized the serialized bytes of the key. * @return the Key object used for encryption. */ @NonNull Key keyOf(@NonNull byte[] serialized); /** * Set the signal if it is a reconnection process. * * @param isReconnect {@code true} if it is a reconnect. */ void setIsReconnect(boolean isReconnect); }