summaryrefslogtreecommitdiff
path: root/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java')
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java274
1 files changed, 0 insertions, 274 deletions
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java
deleted file mode 100644
index fb4af63..0000000
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java
+++ /dev/null
@@ -1,274 +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.annotations.VisibleForTesting;
-import com.google.protobuf.ByteString;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.DeviceToDeviceMessage;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.Payload;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.PayloadType;
-import java.io.UnsupportedEncodingException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
-import java.util.Arrays;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * The full context of a secure connection. This object has methods to encode and decode messages
- * that are to be sent to another device.
- *
- * Subclasses keep track of the keys shared with the other device, and of the sequence in which the
- * messages are expected.
- */
-public abstract class D2DConnectionContext {
- private static final String UTF8 = "UTF-8";
- private final int protocolVersion;
-
- protected D2DConnectionContext(int protocolVersion) {
- this.protocolVersion = protocolVersion;
- }
-
- /**
- * @return the version of the D2D protocol.
- */
- public int getProtocolVersion() {
- return protocolVersion;
- }
-
- /**
- * Once initiator and responder have exchanged public keys, use this method to encrypt and
- * sign a payload. Both initiator and responder devices can use this message.
- *
- * @param payload the payload that should be encrypted.
- */
- public byte[] encodeMessageToPeer(byte[] payload) {
- incrementSequenceNumberForEncoding();
- DeviceToDeviceMessage message = createDeviceToDeviceMessage(
- payload, getSequenceNumberForEncoding());
- try {
- return D2DCryptoOps.signcryptPayload(
- new Payload(PayloadType.DEVICE_TO_DEVICE_MESSAGE,
- message.toByteArray()),
- getEncodeKey());
- } catch (InvalidKeyException e) {
- // should never happen, since we agreed on the key earlier
- throw new RuntimeException(e);
- } catch (NoSuchAlgorithmException e) {
- // should never happen
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Encrypting/signing a string for transmission to another device.
- *
- * @see #encodeMessageToPeer(byte[])
- *
- * @param payload the payload that should be encrypted.
- */
- public byte[] encodeMessageToPeer(String payload) {
- try {
- return encodeMessageToPeer(payload.getBytes(UTF8));
- } catch (UnsupportedEncodingException e) {
- // Should never happen - we should always be able to UTF-8-encode a string
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Once InitiatorHello and ResponderHello(AndPayload) are exchanged, use this method
- * to decrypt and verify a message received from the other device. Both initiator and
- * responder device can use this message.
- *
- * @param message the message that should be encrypted.
- * @throws SignatureException if the message from the remote peer did not pass verification
- */
- public byte[] decodeMessageFromPeer(byte[] message) throws SignatureException {
- try {
- Payload payload = D2DCryptoOps.verifydecryptPayload(message, getDecodeKey());
- if (!PayloadType.DEVICE_TO_DEVICE_MESSAGE.equals(payload.getPayloadType())) {
- throw new SignatureException("wrong message type in device-to-device message");
- }
-
- DeviceToDeviceMessage messageProto = DeviceToDeviceMessage.parseFrom(payload.getMessage());
- incrementSequenceNumberForDecoding();
- if (messageProto.getSequenceNumber() != getSequenceNumberForDecoding()) {
- throw new SignatureException("Incorrect sequence number");
- }
-
- return messageProto.getMessage().toByteArray();
- } catch (InvalidKeyException e) {
- throw new SignatureException(e);
- } catch (NoSuchAlgorithmException e) {
- // this shouldn't happen - the algorithms are hard-coded.
- throw new RuntimeException(e);
- } catch (InvalidProtocolBufferException e) {
- throw new SignatureException(e);
- }
- }
-
- /**
- * Once InitiatorHello and ResponderHello(AndPayload) are exchanged, use this method
- * to decrypt and verify a message received from the other device. Both initiator and
- * responder device can use this message.
- *
- * @param message the message that should be encrypted.
- */
- public String decodeMessageFromPeerAsString(byte[] message) throws SignatureException {
- try {
- return new String(decodeMessageFromPeer(message), UTF8);
- } catch (UnsupportedEncodingException e) {
- // Should never happen - we should always be able to UTF-8-encode a string
- throw new RuntimeException(e);
- }
- }
-
- // package-private
- static DeviceToDeviceMessage createDeviceToDeviceMessage(byte[] message, int sequenceNumber) {
- DeviceToDeviceMessage.Builder deviceToDeviceMessage = DeviceToDeviceMessage.newBuilder();
- deviceToDeviceMessage.setSequenceNumber(sequenceNumber);
- deviceToDeviceMessage.setMessage(ByteString.copyFrom(message));
- return deviceToDeviceMessage.build();
- }
-
- /**
- * Returns a cryptographic digest (SHA256) of the session keys prepended by the SHA256 hash
- * of the ASCII string "D2D"
- * @throws NoSuchAlgorithmException if SHA 256 doesn't exist on this platform
- */
- public abstract byte[] getSessionUnique() throws NoSuchAlgorithmException;
-
- /**
- * Increments the sequence number used for encoding messages.
- */
- protected abstract void incrementSequenceNumberForEncoding();
-
- /**
- * Increments the sequence number used for decoding messages.
- */
- protected abstract void incrementSequenceNumberForDecoding();
-
- /**
- * @return the last sequence number used to encode a message.
- */
- @VisibleForTesting
- abstract int getSequenceNumberForEncoding();
-
- /**
- * @return the last sequence number used to decode a message.
- */
- @VisibleForTesting
- abstract int getSequenceNumberForDecoding();
-
- /**
- * @return the {@link SecretKey} used for encoding messages.
- */
- @VisibleForTesting
- abstract SecretKey getEncodeKey();
-
- /**
- * @return the {@link SecretKey} used for decoding messages.
- */
- @VisibleForTesting
- abstract SecretKey getDecodeKey();
-
- /**
- * Creates a saved session that can later be used for resumption. Note, this must be stored in a
- * secure location.
- *
- * @return the saved session, suitable for resumption.
- */
- public abstract byte[] saveSession();
-
- /**
- * Parse a saved session info and attempt to construct a resumed context.
- * The first byte in a saved session info must always be the protocol version.
- * Note that an {@link IllegalArgumentException} will be thrown if the savedSessionInfo is not
- * properly formatted.
- *
- * @return a resumed context from a saved session.
- */
- public static D2DConnectionContext fromSavedSession(byte[] savedSessionInfo) {
- if (savedSessionInfo == null || savedSessionInfo.length == 0) {
- throw new IllegalArgumentException("savedSessionInfo null or too short");
- }
-
- int protocolVersion = savedSessionInfo[0] & 0xff;
-
- switch (protocolVersion) {
- case 0:
- // Version 0 has a 1 byte protocol version, a 4 byte sequence number,
- // and 32 bytes of AES key (1 + 4 + 32 = 37)
- if (savedSessionInfo.length != 37) {
- throw new IllegalArgumentException("Incorrect data length (" + savedSessionInfo.length
- + ") for v0 protocol");
- }
- int sequenceNumber = bytesToSignedInt(Arrays.copyOfRange(savedSessionInfo, 1, 5));
- SecretKey sharedKey = new SecretKeySpec(Arrays.copyOfRange(savedSessionInfo, 5, 37), "AES");
- return new D2DConnectionContextV0(sharedKey, sequenceNumber);
-
- case 1:
- // Version 1 has a 1 byte protocol version, two 4 byte sequence numbers,
- // and two 32 byte AES keys (1 + 4 + 4 + 32 + 32 = 73)
- if (savedSessionInfo.length != 73) {
- throw new IllegalArgumentException("Incorrect data length for v1 protocol");
- }
- int encodeSequenceNumber = bytesToSignedInt(Arrays.copyOfRange(savedSessionInfo, 1, 5));
- int decodeSequenceNumber = bytesToSignedInt(Arrays.copyOfRange(savedSessionInfo, 5, 9));
- SecretKey encodeKey =
- new SecretKeySpec(Arrays.copyOfRange(savedSessionInfo, 9, 41), "AES");
- SecretKey decodeKey =
- new SecretKeySpec(Arrays.copyOfRange(savedSessionInfo, 41, 73), "AES");
- return new D2DConnectionContextV1(encodeKey, decodeKey, encodeSequenceNumber,
- decodeSequenceNumber);
-
- default:
- throw new IllegalArgumentException("Cannot rebuild context, unkown protocol version: "
- + protocolVersion);
- }
- }
-
- /**
- * Convert 4 bytes in big-endian representation into a signed int.
- */
- static int bytesToSignedInt(byte[] bytes) {
- if (bytes.length != 4) {
- throw new IllegalArgumentException("Expected 4 bytes to encode int, but got: "
- + bytes.length + " bytes");
- }
-
- return ((bytes[0] << 24) & 0xff000000)
- | ((bytes[1] << 16) & 0x00ff0000)
- | ((bytes[2] << 8) & 0x0000ff00)
- | (bytes[3] & 0x000000ff);
- }
-
- /**
- * Convert a signed int into a 4 byte big-endian representation
- */
- static byte[] signedIntToBytes(int val) {
- byte[] bytes = new byte[4];
-
- bytes[0] = (byte) ((val >> 24) & 0xff);
- bytes[1] = (byte) ((val >> 16) & 0xff);
- bytes[2] = (byte) ((val >> 8) & 0xff);
- bytes[3] = (byte) (val & 0xff);
-
- return bytes;
- }
-}