summaryrefslogtreecommitdiff
path: root/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java')
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java342
1 files changed, 0 insertions, 342 deletions
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
deleted file mode 100644
index 2b73653..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
+++ /dev/null
@@ -1,342 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// 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
-//
-// https://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 com.google.security.cryptauth.lib.securegcm;
-
-import com.google.common.io.BaseEncoding;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ProcessBuilder.Redirect;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.annotation.Nullable;
-
-/**
- * A wrapper to execute and interact with the //security/cryptauth/lib/securegcm:ukey2_shell binary.
- *
- * <p>This binary is a shell over the C++ implementation of the UKEY2 protocol, so this wrapper is
- * used to test compatibility between the C++ and Java implementations.
- *
- * <p>The ukey2_shell is invoked as follows:
- *
- * <pre>{@code
- * ukey2_shell --mode=<mode> --verification_string_length=<length>
- * }</pre>
- *
- * where {@code mode={initiator, responder}} and {@code verification_string_length} is a positive
- * integer.
- */
-public class Ukey2ShellCppWrapper {
- // The path the the ukey2_shell binary.
- private static final String BINARY_PATH = "build/src/main/cpp/src/securegcm/ukey2_shell";
-
- // The time to wait before timing out a read or write operation to the shell.
- @SuppressWarnings("GoodTime") // TODO(b/147378611): store a java.time.Duration instead
- private static final long IO_TIMEOUT_MILLIS = 5000;
-
- public enum Mode {
- INITIATOR,
- RESPONDER
- }
-
- private final Mode mode;
- private final int verificationStringLength;
- private final ExecutorService executorService;
-
- @Nullable private Process shellProcess;
- private boolean secureContextEstablished;
-
- /**
- * @param mode The mode to run the shell in (initiator or responder).
- * @param verificationStringLength The length of the verification string used in the handshake.
- */
- public Ukey2ShellCppWrapper(Mode mode, int verificationStringLength) {
- this.mode = mode;
- this.verificationStringLength = verificationStringLength;
- this.executorService = Executors.newSingleThreadExecutor();
- }
-
- /**
- * Begins execution of the ukey2_shell binary.
- *
- * @throws IOException
- */
- public void startShell() throws IOException {
- if (shellProcess != null) {
- throw new IllegalStateException("Shell already started.");
- }
-
- String modeArg = "--mode=" + getModeString();
- String verificationStringLengthArg = "--verification_string_length=" + verificationStringLength;
-
- final ProcessBuilder builder =
- new ProcessBuilder(BINARY_PATH, modeArg, verificationStringLengthArg);
-
- // Merge the shell's stderr with the stderr of the current process.
- builder.redirectError(Redirect.INHERIT);
-
- shellProcess = builder.start();
- }
-
- /**
- * Stops execution of the ukey2_shell binary.
- *
- * @throws IOException
- */
- public void stopShell() {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
- shellProcess.destroy();
- }
-
- /**
- * @return the handshake message read from the shell.
- * @throws IOException
- */
- public byte[] readHandshakeMessage() throws IOException {
- return readFrameWithTimeout();
- }
-
- /**
- * Sends the handshake message to the shell.
- *
- * @param message
- * @throws IOException
- */
- public void writeHandshakeMessage(byte[] message) throws IOException {
- writeFrameWithTimeout(message);
- }
-
- /**
- * Reads the auth string from the shell and compares it with {@code authString}. If verification
- * succeeds, then write "ok" back as a confirmation.
- *
- * @param authString the auth string to compare to.
- * @throws IOException
- */
- public void confirmAuthString(byte[] authString) throws IOException {
- byte[] shellAuthString = readFrameWithTimeout();
- if (!Arrays.equals(authString, shellAuthString)) {
- throw new IOException(
- String.format(
- "Unable to verify auth string: 0x%s != 0x%s",
- BaseEncoding.base16().encode(authString),
- BaseEncoding.base16().encode(shellAuthString)));
- }
- writeFrameWithTimeout("ok".getBytes());
- secureContextEstablished = true;
- }
-
- /**
- * Sends {@code payload} to be encrypted by the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @param payload the data to be encrypted.
- * @return the encrypted message returned by the shell.
- * @throws IOException
- */
- public byte[] sendEncryptCommand(byte[] payload) throws IOException {
- writeFrameWithTimeout(createExpression("encrypt", payload));
- return readFrameWithTimeout();
- }
-
- /**
- * Sends {@code message} to be decrypted by the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @param message the data to be decrypted.
- * @return the decrypted payload returned by the shell.
- * @throws IOException
- */
- public byte[] sendDecryptCommand(byte[] message) throws IOException {
- writeFrameWithTimeout(createExpression("decrypt", message));
- return readFrameWithTimeout();
- }
-
- /**
- * Requests the session unique value from the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @return the session unique value returned by the shell.
- * @throws IOException
- */
- public byte[] sendSessionUniqueCommand() throws IOException {
- writeFrameWithTimeout(createExpression("session_unique", null));
- return readFrameWithTimeout();
- }
-
- /**
- * Reads a frame from the shell's stdout with a timeout.
- *
- * @return The contents of the frame.
- * @throws IOException
- */
- private byte[] readFrameWithTimeout() throws IOException {
- Future<byte[]> future =
- executorService.submit(
- new Callable<byte[]>() {
- @Override
- public byte[] call() throws Exception {
- return readFrame();
- }
- });
-
- try {
- return future.get(IO_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Writes a frame to the shell's stdin with a timeout.
- *
- * @param contents the contents of the frame.
- * @throws IOException
- */
- private void writeFrameWithTimeout(final byte[] contents) throws IOException {
- Future<?> future =
- executorService.submit(
- new Runnable() {
- @Override
- public void run() {
- try {
- writeFrame(contents);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- });
-
- try {
- future.get(IO_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Reads a frame from the shell's stdout, which has the format:
- *
- * <pre>{@code
- * +---------------------+-----------------+
- * | 4-bytes | |length| bytes |
- * +---------------------+-----------------+
- * | (unsigned) length | contents |
- * +---------------------+-----------------+
- * }</pre>
- *
- * @return the contents that were read
- * @throws IOException
- */
- private byte[] readFrame() throws IOException {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
-
- InputStream inputStream = shellProcess.getInputStream();
- byte[] lengthBytes = new byte[4];
- if (inputStream.read(lengthBytes) != lengthBytes.length) {
- throw new IOException("Failed to read length.");
- }
-
- int length = ByteBuffer.wrap(lengthBytes).order(ByteOrder.BIG_ENDIAN).getInt();
- if (length < 0) {
- throw new IOException("Length too large: " + Arrays.toString(lengthBytes));
- }
-
- byte[] contents = new byte[length];
- int bytesRead = inputStream.read(contents);
- if (bytesRead != length) {
- throw new IOException("Failed to read entire contents: " + bytesRead + " != " + length);
- }
-
- return contents;
- }
-
- /**
- * Writes a frame to the shell's stdin, which has the format:
- *
- * <pre>{@code
- * +---------------------+-----------------+
- * | 4-bytes | |length| bytes |
- * +---------------------+-----------------+
- * | (unsigned) length | contents |
- * +---------------------+-----------------+
- * }</pre>
- *
- * @param contents the contents to send.
- * @throws IOException
- */
- private void writeFrame(byte[] contents) throws IOException {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
-
- // The length is big-endian encoded, network byte order.
- long length = contents.length;
- byte[] lengthBytes = new byte[4];
- lengthBytes[0] = (byte) (length >> 32 & 0xFF);
- lengthBytes[1] = (byte) (length >> 16 & 0xFF);
- lengthBytes[2] = (byte) (length >> 8 & 0xFF);
- lengthBytes[3] = (byte) (length >> 0 & 0xFF);
-
- OutputStream outputStream = shellProcess.getOutputStream();
- outputStream.write(lengthBytes);
- outputStream.write(contents);
- outputStream.flush();
- }
-
- /**
- * Creates an expression to be processed when a secure connection is established, after the
- * handshake is done.
- *
- * @param command The command to send.
- * @param argument The argument of the command. Can be null.
- * @return the expression that can be sent to the shell.
- * @throws IOException.
- */
- private byte[] createExpression(String command, @Nullable byte[] argument) throws IOException {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- outputStream.write(command.getBytes());
- outputStream.write(" ".getBytes());
- if (argument != null) {
- outputStream.write(argument);
- }
- return outputStream.toByteArray();
- }
-
- /** @return the mode string to use in the argument to start the ukey2_shell process. */
- private String getModeString() {
- switch (mode) {
- case INITIATOR:
- return "initiator";
- case RESPONDER:
- return "responder";
- default:
- throw new IllegalArgumentException("Uknown mode " + mode);
- }
- }
-}