summaryrefslogtreecommitdiff
path: root/src/main/cpp/src/securegcm
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/cpp/src/securegcm')
-rw-r--r--src/main/cpp/src/securegcm/CMakeLists.txt47
-rw-r--r--src/main/cpp/src/securegcm/d2d_connection_context_v1.cc228
-rw-r--r--src/main/cpp/src/securegcm/d2d_crypto_ops.cc151
-rw-r--r--src/main/cpp/src/securegcm/java_util.cc60
-rw-r--r--src/main/cpp/src/securegcm/ukey2_handshake.cc715
-rw-r--r--src/main/cpp/src/securegcm/ukey2_shell.cc297
6 files changed, 0 insertions, 1498 deletions
diff --git a/src/main/cpp/src/securegcm/CMakeLists.txt b/src/main/cpp/src/securegcm/CMakeLists.txt
deleted file mode 100644
index b562756..0000000
--- a/src/main/cpp/src/securegcm/CMakeLists.txt
+++ /dev/null
@@ -1,47 +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.
-
-add_library(ukey2 STATIC
- d2d_connection_context_v1.cc
- d2d_crypto_ops.cc
- java_util.cc
- ukey2_handshake.cc
-)
-
-target_include_directories(ukey2
- PUBLIC
- ${PROJECT_SOURCE_DIR}/src/main/cpp/include
-)
-
-target_link_libraries(ukey2
- PUBLIC
- proto_device_to_device_messages_cc_proto
- proto_securegcm_cc_proto
- proto_ukey_cc_proto
- securemessage
-)
-
-add_executable(ukey2_shell
- ukey2_shell.cc
-)
-
-target_link_libraries(ukey2_shell
- PUBLIC
- securemessage
- ukey2
- absl::base
- absl::container
- absl::flags
- absl::flags_parse
-)
diff --git a/src/main/cpp/src/securegcm/d2d_connection_context_v1.cc b/src/main/cpp/src/securegcm/d2d_connection_context_v1.cc
deleted file mode 100644
index 8a9a612..0000000
--- a/src/main/cpp/src/securegcm/d2d_connection_context_v1.cc
+++ /dev/null
@@ -1,228 +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.
-
-#include "securegcm/d2d_connection_context_v1.h"
-
-#include <limits>
-#include <sstream>
-
-#include "proto/device_to_device_messages.pb.h"
-#include "proto/securegcm.pb.h"
-#include "securegcm/d2d_crypto_ops.h"
-#include "securegcm/java_util.h"
-#include "securemessage/secure_message_builder.h"
-#include "securemessage/util.h"
-
-namespace securegcm {
-
-using securemessage::CryptoOps;
-using securemessage::ByteBuffer;
-using securemessage::Util;
-
-namespace {
-
-// Fields to fill in the GcmMetadata proto.
-const Type kGcmMetadataType = DEVICE_TO_DEVICE_MESSAGE;
-
-// Represents the version of this context.
-const uint8_t kProtocolVersion = 1;
-
-// The following represent the starting positions of the each entry within
-// the string representation of this D2DConnectionContextV1.
-//
-// The saved session has a 1 byte protocol version, two 4 byte sequence numbers,
-// and two 32 byte AES keys: (1 + 4 + 4 + 32 + 32 = 73).
-
-// The two sequence numbers are 4 bytes each.
-const int kSequenceNumberLength = 4;
-
-// 32 byte AES keys.
-const int kAesKeyLength = 32;
-
-// The encode sequence number starts at 1 to account for the 1 byte version
-// number.
-const int kEncodeSequenceStart = 1;
-const int kEncodeSequenceEnd = kEncodeSequenceStart + kSequenceNumberLength;
-
-const int kDecodeSequenceStart = kEncodeSequenceEnd;
-const int kDecodeSequenceEnd = kDecodeSequenceStart + kSequenceNumberLength;
-
-const int kEncodeKeyStart = kDecodeSequenceEnd;
-const int kEncodeKeyEnd = kEncodeKeyStart + kAesKeyLength;
-
-const int kDecodeKeyStart = kEncodeKeyEnd;
-const int kSavedSessionLength = kDecodeKeyStart + kAesKeyLength;
-
-// Convenience function to creates a DeviceToDeviceMessage proto with |payload|
-// and |sequence_number|.
-DeviceToDeviceMessage CreateDeviceToDeviceMessage(const std::string& payload,
- uint32_t sequence_number) {
- DeviceToDeviceMessage device_to_device_message;
- device_to_device_message.set_sequence_number(sequence_number);
- device_to_device_message.set_message(payload);
- return device_to_device_message;
-}
-
-// Convert 4 bytes in big-endian representation into an unsigned int.
-uint32_t BytesToUnsignedInt(std::vector<uint8_t> bytes) {
- return bytes[0] << 24 | bytes[1] << 12 | bytes[2] << 8 | bytes[3];
-}
-
-// Convert an unsigned int into a 4 byte big-endian representation.
-std::vector<uint8_t> UnsignedIntToBytes(uint32_t val) {
- return {static_cast<uint8_t>(val >> 24), static_cast<uint8_t>(val >> 12),
- static_cast<uint8_t>(val >> 8), static_cast<uint8_t>(val)};
-}
-
-} // namespace
-
-D2DConnectionContextV1::D2DConnectionContextV1(
- const CryptoOps::SecretKey& encode_key,
- const CryptoOps::SecretKey& decode_key, uint32_t encode_sequence_number,
- uint32_t decode_sequence_number)
- : encode_key_(encode_key),
- decode_key_(decode_key),
- encode_sequence_number_(encode_sequence_number),
- decode_sequence_number_(decode_sequence_number) {}
-
-std::unique_ptr<std::string> D2DConnectionContextV1::EncodeMessageToPeer(
- const std::string& payload) {
- encode_sequence_number_++;
- const DeviceToDeviceMessage message =
- CreateDeviceToDeviceMessage(payload, encode_sequence_number_);
-
- const D2DCryptoOps::Payload payload_with_type(kGcmMetadataType,
- message.SerializeAsString());
- return D2DCryptoOps::SigncryptPayload(payload_with_type, encode_key_);
-}
-
-std::unique_ptr<std::string> D2DConnectionContextV1::DecodeMessageFromPeer(
- const std::string& message) {
- std::unique_ptr<D2DCryptoOps::Payload> payload =
- D2DCryptoOps::VerifyDecryptPayload(message, decode_key_);
- if (!payload) {
- Util::LogError("DecodeMessageFromPeer: Failed to verify message.");
- return nullptr;
- }
-
- if (kGcmMetadataType != payload->type()) {
- Util::LogError("DecodeMessageFromPeer: Wrong message type in D2D message.");
- return nullptr;
- }
-
- DeviceToDeviceMessage d2d_message;
- if (!d2d_message.ParseFromString(payload->message())) {
- Util::LogError("DecodeMessageFromPeer: Unable to parse D2D message proto.");
- return nullptr;
- }
-
- decode_sequence_number_++;
- if (d2d_message.sequence_number() != decode_sequence_number_) {
- std::ostringstream stream;
- stream << "DecodeMessageFromPeer: Seqno in D2D message ("
- << d2d_message.sequence_number()
- << ") does not match expected seqno (" << decode_sequence_number_
- << ").";
- Util::LogError(stream.str());
- return nullptr;
- }
-
- return std::unique_ptr<std::string>(d2d_message.release_message());
-}
-
-std::unique_ptr<std::string> D2DConnectionContextV1::GetSessionUnique() {
- const ByteBuffer encode_key_data = encode_key_.data();
- const ByteBuffer decode_key_data = decode_key_.data();
- const int32_t encode_key_hash = java_util::JavaHashCode(encode_key_data);
- const int32_t decode_key_hash = java_util::JavaHashCode(decode_key_data);
-
- const ByteBuffer& first_buffer =
- encode_key_hash < decode_key_hash ? encode_key_data : decode_key_data;
- const ByteBuffer& second_buffer =
- encode_key_hash < decode_key_hash ? decode_key_data : encode_key_data;
-
- ByteBuffer data_to_hash(D2DCryptoOps::kSalt, D2DCryptoOps::kSaltLength);
- data_to_hash = ByteBuffer::Concat(data_to_hash, first_buffer);
- data_to_hash = ByteBuffer::Concat(data_to_hash, second_buffer);
-
- std::unique_ptr<ByteBuffer> hash = CryptoOps::Sha256(data_to_hash);
- if (!hash) {
- Util::LogError("GetSessionUnique: SHA-256 hash failed.");
- return nullptr;
- }
-
- return std::unique_ptr<std::string>(new std::string(hash->String()));
-}
-
-// Structure of saved session is:
-//
-// +---------------------------------------------------------------------------+
-// | 1 Byte | 4 Bytes | 4 Bytes | 32 Bytes | 32 Bytes |
-// +---------------------------------------------------------------------------+
-// | Version | encode seq number | decode seq number | encode key | decode key |
-// +---------------------------------------------------------------------------+
-//
-// The sequence numbers are represented in big-endian.
-std::unique_ptr<std::string> D2DConnectionContextV1::SaveSession() {
- ByteBuffer byteBuffer = ByteBuffer(&kProtocolVersion, static_cast<size_t>(1));
-
- // Append encode sequence number.
- std::vector<uint8_t> encode_sequence_number_bytes =
- UnsignedIntToBytes(encode_sequence_number_);
- for (int i = 0; i < encode_sequence_number_bytes.size(); i++) {
- byteBuffer.Append(static_cast<size_t>(1), encode_sequence_number_bytes[i]);
- }
-
- // Append decode sequence number.
- std::vector<uint8_t> decode_sequence_number_bytes =
- UnsignedIntToBytes(decode_sequence_number_);
- for (int i = 0; i < decode_sequence_number_bytes.size(); i++) {
- byteBuffer.Append(static_cast<size_t>(1), decode_sequence_number_bytes[i]);
- }
-
- // Append encode key.
- byteBuffer = ByteBuffer::Concat(byteBuffer, encode_key_.data());
-
- // Append decode key.
- byteBuffer = ByteBuffer::Concat(byteBuffer, decode_key_.data());
-
- return std::unique_ptr<std::string>(new std::string(byteBuffer.String()));
-}
-
-// static.
-std::unique_ptr<D2DConnectionContextV1>
-D2DConnectionContextV1::FromSavedSession(const std::string& savedSessionInfo) {
- ByteBuffer byteBuffer = ByteBuffer(savedSessionInfo);
-
- if (byteBuffer.size() != kSavedSessionLength) {
- return nullptr;
- }
-
- uint32_t encode_sequence_number = BytesToUnsignedInt(
- byteBuffer.SubArray(kEncodeSequenceStart, kEncodeSequenceEnd)->Vector());
- uint32_t decode_sequence_number = BytesToUnsignedInt(
- byteBuffer.SubArray(kDecodeSequenceStart, kDecodeSequenceEnd)->Vector());
-
- const CryptoOps::SecretKey encode_key =
- CryptoOps::SecretKey(*byteBuffer.SubArray(kEncodeKeyStart, kAesKeyLength),
- CryptoOps::KeyAlgorithm::AES_256_KEY);
- const CryptoOps::SecretKey decode_key =
- CryptoOps::SecretKey(*byteBuffer.SubArray(kDecodeKeyStart, kAesKeyLength),
- CryptoOps::KeyAlgorithm::AES_256_KEY);
-
- return std::unique_ptr<D2DConnectionContextV1>(new D2DConnectionContextV1(
- encode_key, decode_key, encode_sequence_number, decode_sequence_number));
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/d2d_crypto_ops.cc b/src/main/cpp/src/securegcm/d2d_crypto_ops.cc
deleted file mode 100644
index 49f0b85..0000000
--- a/src/main/cpp/src/securegcm/d2d_crypto_ops.cc
+++ /dev/null
@@ -1,151 +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.
-
-#include "securegcm/d2d_crypto_ops.h"
-
-#include <sstream>
-
-#include "securemessage/secure_message_builder.h"
-#include "securemessage/secure_message_parser.h"
-#include "securemessage/util.h"
-
-namespace securegcm {
-
-using securemessage::CryptoOps;
-using securemessage::HeaderAndBody;
-using securemessage::SecureMessage;
-using securemessage::SecureMessageBuilder;
-using securemessage::SecureMessageParser;
-using securemessage::Util;
-
-namespace {
-
-// The current protocol version.
-const int kSecureGcmProtocolVersion = 1;
-
-// The number of bytes in an expected AES256 key.
-const int kAes256KeyLength = 32;
-}
-
-// static.
-const uint8_t D2DCryptoOps::kSalt[] = {
- 0x82, 0xAA, 0x55, 0xA0, 0xD3, 0x97, 0xF8, 0x83, 0x46, 0xCA, 0x1C,
- 0xEE, 0x8D, 0x39, 0x09, 0xB9, 0x5F, 0x13, 0xFA, 0x7D, 0xEB, 0x1D,
- 0x4A, 0xB3, 0x83, 0x76, 0xB8, 0x25, 0x6D, 0xA8, 0x55, 0x10};
-
-// static.
-const size_t D2DCryptoOps::kSaltLength = sizeof(D2DCryptoOps::kSalt);
-
-D2DCryptoOps::Payload::Payload(Type type, const string& message)
- : type_(type), message_(message) {}
-
-D2DCryptoOps::D2DCryptoOps() {}
-
-// static.
-std::unique_ptr<string> D2DCryptoOps::SigncryptPayload(
- const Payload& payload, const CryptoOps::SecretKey& secret_key) {
- GcmMetadata gcm_metadata;
- gcm_metadata.set_type(payload.type());
- gcm_metadata.set_version(kSecureGcmProtocolVersion);
-
- SecureMessageBuilder builder;
- builder.SetPublicMetadata(gcm_metadata.SerializeAsString());
-
- std::unique_ptr<SecureMessage> secure_message =
- builder.BuildSignCryptedMessage(secret_key, CryptoOps::HMAC_SHA256,
- secret_key, CryptoOps::AES_256_CBC,
- payload.message());
- if (!secure_message) {
- Util::LogError("Unable to encrypt payload.");
- return nullptr;
- }
-
- return std::unique_ptr<string>(
- new string(secure_message->SerializeAsString()));
-}
-
-// static.
-std::unique_ptr<D2DCryptoOps::Payload> D2DCryptoOps::VerifyDecryptPayload(
- const string& signcrypted_message, const CryptoOps::SecretKey& secret_key) {
- SecureMessage secure_message;
- if (!secure_message.ParseFromString(signcrypted_message)) {
- Util::LogError("VerifyDecryptPayload: error parsing SecureMessage.");
- return nullptr;
- }
-
- std::unique_ptr<HeaderAndBody> header_and_body =
- SecureMessageParser::ParseSignCryptedMessage(
- secure_message, secret_key, CryptoOps::HMAC_SHA256, secret_key,
- CryptoOps::AES_256_CBC, string() /* associated_data */);
- if (!header_and_body) {
- Util::LogError("VerifyDecryptPayload: error verifying SecureMessage.");
- return nullptr;
- }
-
- if (!header_and_body->header().has_public_metadata()) {
- Util::LogError("VerifyDecryptPayload: no public metadata in header.");
- return nullptr;
- }
-
- GcmMetadata metadata;
- if (!metadata.ParseFromString(header_and_body->header().public_metadata())) {
- Util::LogError("VerifyDecryptPayload: Failed to parse GcmMetadata.");
- return nullptr;
- }
-
- if (metadata.version() != kSecureGcmProtocolVersion) {
- std::ostringstream stream;
- stream << "VerifyDecryptPayload: Unsupported protocol version "
- << metadata.version();
- Util::LogError(stream.str());
- return nullptr;
- }
-
- return std::unique_ptr<Payload>(
- new Payload(metadata.type(), header_and_body->body()));
-}
-
-// static.
-std::unique_ptr<CryptoOps::SecretKey> D2DCryptoOps::DeriveNewKeyForPurpose(
- const securemessage::CryptoOps::SecretKey& master_key,
- const string& purpose) {
- if (master_key.data().size() != kAes256KeyLength) {
- Util::LogError("DeriveNewKeyForPurpose: Invalid master_key length.");
- return nullptr;
- }
-
- if (purpose.empty()) {
- Util::LogError("DeriveNewKeyForPurpose: purpose is empty.");
- return nullptr;
- }
-
- std::unique_ptr<string> raw_derived_key = CryptoOps::Hkdf(
- master_key.data().String(),
- string(reinterpret_cast<const char *>(kSalt), kSaltLength),
- purpose);
- if (!raw_derived_key) {
- Util::LogError("DeriveNewKeyForPurpose: hkdf failed.");
- return nullptr;
- }
-
- if (raw_derived_key->size() != kAes256KeyLength) {
- Util::LogError("DeriveNewKeyForPurpose: Unexpected size of derived key.");
- return nullptr;
- }
-
- return std::unique_ptr<CryptoOps::SecretKey>(
- new CryptoOps::SecretKey(*raw_derived_key, CryptoOps::AES_256_KEY));
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/java_util.cc b/src/main/cpp/src/securegcm/java_util.cc
deleted file mode 100644
index 1ce4d7b..0000000
--- a/src/main/cpp/src/securegcm/java_util.cc
+++ /dev/null
@@ -1,60 +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.
-
-#include "securegcm/java_util.h"
-
-#include <cstring>
-
-namespace securegcm {
-namespace java_util {
-
-namespace {
-
-// Returns the lower 32-bits of a int64_t |value| as an int32_t.
-int32_t Lower32Bits(int64_t value) {
- const uint32_t lower_bits = static_cast<uint32_t>(value & 0xFFFFFFFF);
- int32_t return_value;
- std::memcpy(&return_value, &lower_bits, sizeof(uint32_t));
- return return_value;
-}
-
-} // namespace
-
-int32_t JavaMultiply(int32_t lhs, int32_t rhs) {
- // Multiplication guaranteed to fit in int64_t, range from [2^63, 2^63 - 1].
- // Minimum value is (-2^31)^2 = 2^62.
- const int64_t result = static_cast<int64_t>(lhs) * static_cast<int64_t>(rhs);
- return Lower32Bits(result);
-}
-
-int32_t JavaAdd(int32_t lhs, int32_t rhs) {
- const int64_t result = static_cast<int64_t>(lhs) + static_cast<int64_t>(rhs);
- return Lower32Bits(result);
-}
-
-int32_t JavaHashCode(const securemessage::ByteBuffer& byte_buffer) {
- const string bytes = byte_buffer.String();
- int32_t hash_code = 1;
- for (const int8_t byte : bytes) {
- int32_t int_value = static_cast<int32_t>(byte);
- // Java relies on the overflow/underflow behaviour of arithmetic operations,
- // which is undefined in C++, so we call our own Java-compatible versions of
- // + and * here.
- hash_code = JavaAdd(JavaMultiply(31, hash_code), int_value);
- }
- return hash_code;
-}
-
-} // namespace java_util
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/ukey2_handshake.cc b/src/main/cpp/src/securegcm/ukey2_handshake.cc
deleted file mode 100644
index dc5c131..0000000
--- a/src/main/cpp/src/securegcm/ukey2_handshake.cc
+++ /dev/null
@@ -1,715 +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.
-
-#include "securegcm/ukey2_handshake.h"
-
-#include <sstream>
-
-#include "securegcm/d2d_crypto_ops.h"
-#include "securemessage/public_key_proto_util.h"
-
-namespace securegcm {
-
-using securemessage::ByteBuffer;
-using securemessage::CryptoOps;
-using securemessage::GenericPublicKey;
-using securemessage::PublicKeyProtoUtil;
-
-namespace {
-
-// Salt value used to derive client and server keys for next protocol.
-const char kUkey2HkdfSalt[] = "UKEY2 v1 next";
-
-// Salt value used to derive verification string.
-const char kUkey2VerificationStringSalt[] = "UKEY2 v1 auth";
-
-// Maximum version of the handshake supported by this class.
-const uint32_t kVersion = 1;
-
-// Random nonce is fixed at 32 bytes (as per go/ukey2).
-const uint32_t kNonceLengthInBytes = 32;
-
-// Currently, we only support one next protocol.
-const char kNextProtocol[] = "AES_256_CBC-HMAC_SHA256";
-
-// Creates the appropriate KeyPair for |cipher|.
-std::unique_ptr<CryptoOps::KeyPair> GenerateKeyPair(
- UKey2Handshake::HandshakeCipher cipher) {
- switch (cipher) {
- case UKey2Handshake::HandshakeCipher::P256_SHA512:
- return CryptoOps::GenerateEcP256KeyPair();
- default:
- return nullptr;
- }
-}
-
-// Parses a CryptoOps::PublicKey from a serialized GenericPublicKey.
-std::unique_ptr<securemessage::CryptoOps::PublicKey> ParsePublicKey(
- const string& serialized_generic_public_key) {
- GenericPublicKey generic_public_key;
- if (!generic_public_key.ParseFromString(serialized_generic_public_key)) {
- return nullptr;
- }
- return PublicKeyProtoUtil::ParsePublicKey(generic_public_key);
-}
-
-} // namespace
-
-// static.
-std::unique_ptr<UKey2Handshake> UKey2Handshake::ForInitiator(
- HandshakeCipher cipher) {
- return std::unique_ptr<UKey2Handshake>(
- new UKey2Handshake(InternalState::CLIENT_START, cipher));
-}
-
-// static.
-std::unique_ptr<UKey2Handshake> UKey2Handshake::ForResponder(
- HandshakeCipher cipher) {
- return std::unique_ptr<UKey2Handshake>(
- new UKey2Handshake(InternalState::SERVER_START, cipher));
-}
-
-UKey2Handshake::UKey2Handshake(InternalState state, HandshakeCipher cipher)
- : handshake_state_(state),
- handshake_cipher_(cipher),
- handshake_role_(state == InternalState::CLIENT_START
- ? HandshakeRole::CLIENT
- : HandshakeRole::SERVER),
- our_key_pair_(GenerateKeyPair(cipher)) {}
-
-UKey2Handshake::State UKey2Handshake::GetHandshakeState() const {
- switch (handshake_state_) {
- case InternalState::CLIENT_START:
- case InternalState::CLIENT_WAITING_FOR_SERVER_INIT:
- case InternalState::CLIENT_AFTER_SERVER_INIT:
- case InternalState::SERVER_START:
- case InternalState::SERVER_AFTER_CLIENT_INIT:
- case InternalState::SERVER_WAITING_FOR_CLIENT_FINISHED:
- // Fallthrough intended -- these are all in-progress states.
- return State::kInProgress;
- case InternalState::HANDSHAKE_VERIFICATION_NEEDED:
- return State::kVerificationNeeded;
- case InternalState::HANDSHAKE_VERIFICATION_IN_PROGRESS:
- return State::kVerificationInProgress;
- case InternalState::HANDSHAKE_FINISHED:
- return State::kFinished;
- case InternalState::HANDSHAKE_ALREADY_USED:
- return State::kAlreadyUsed;
- case InternalState::HANDSHAKE_ERROR:
- return State::kError;
- default:
- // Unreachable.
- return State::kError;
- }
-}
-
-const string& UKey2Handshake::GetLastError() const {
- return last_error_;
-}
-
-std::unique_ptr<string> UKey2Handshake::GetNextHandshakeMessage() {
- switch (handshake_state_) {
- case InternalState::CLIENT_START: {
- std::unique_ptr<string> client_init = MakeClientInitUkey2Message();
- if (!client_init) {
- // |last_error_| is already set.
- return nullptr;
- }
-
- wrapped_client_init_ = *client_init;
- handshake_state_ = InternalState::CLIENT_WAITING_FOR_SERVER_INIT;
- return client_init;
- }
-
- case InternalState::SERVER_AFTER_CLIENT_INIT: {
- std::unique_ptr<string> server_init = MakeServerInitUkey2Message();
- if (!server_init) {
- // |last_error_| is already set.
- return nullptr;
- }
-
- wrapped_server_init_ = *server_init;
- handshake_state_ = InternalState::SERVER_WAITING_FOR_CLIENT_FINISHED;
- return server_init;
- }
-
- case InternalState::CLIENT_AFTER_SERVER_INIT: {
- // Make sure we have a message 3 for the chosen cipher.
- if (raw_message3_map_.count(handshake_cipher_) == 0) {
- std::ostringstream stream;
- stream << "Client state is CLIENT_AFTER_SERVER_INIT, and cipher is "
- << static_cast<int>(handshake_cipher_)
- << ", but no corresponding raw "
- << "[Client Finished] message has been generated.";
- SetError(stream.str());
- return nullptr;
- }
- handshake_state_ = InternalState::HANDSHAKE_VERIFICATION_NEEDED;
- return std::unique_ptr<string>(
- new string(raw_message3_map_[handshake_cipher_]));
- }
-
- default: {
- std::ostringstream stream;
- stream << "Cannot get next message in state "
- << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return nullptr;
- }
- }
-}
-
-UKey2Handshake::ParseResult
-UKey2Handshake::ParseHandshakeMessage(const string& handshake_message) {
- switch (handshake_state_) {
- case InternalState::SERVER_START:
- return ParseClientInitUkey2Message(handshake_message);
- case InternalState::CLIENT_WAITING_FOR_SERVER_INIT:
- return ParseServerInitUkey2Message(handshake_message);
- case InternalState::SERVER_WAITING_FOR_CLIENT_FINISHED:
- return ParseClientFinishUkey2Message(handshake_message);
- default:
- std::ostringstream stream;
- stream << "Cannot parse message in state "
- << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return {false, nullptr};
- }
-}
-
-std::unique_ptr<string> UKey2Handshake::GetVerificationString(int byte_length) {
- if (byte_length < 1 || byte_length > 32) {
- SetError("Minimum length is 1 byte, max is 32 bytes.");
- return nullptr;
- }
-
- if (handshake_state_ != InternalState::HANDSHAKE_VERIFICATION_NEEDED) {
- std::ostringstream stream;
- stream << "Unexpected state: " << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return nullptr;
- }
-
- if (!our_key_pair_ || !our_key_pair_->private_key || !their_public_key_) {
- SetError("One of our private key or their public key is null.");
- return nullptr;
- }
-
- switch (handshake_cipher_) {
- case HandshakeCipher::P256_SHA512:
- derived_secret_key_ = CryptoOps::KeyAgreementSha256(
- *(our_key_pair_->private_key), *their_public_key_);
- break;
- default:
- // Unreachable.
- return nullptr;
- }
-
- if (!derived_secret_key_) {
- SetError("Failed to derive shared secret key.");
- return nullptr;
- }
-
- std::unique_ptr<string> auth_string = CryptoOps::Hkdf(
- derived_secret_key_->data().String(),
- string(kUkey2VerificationStringSalt, sizeof(kUkey2VerificationStringSalt)),
- wrapped_client_init_ + wrapped_server_init_);
-
- handshake_state_ = InternalState::HANDSHAKE_VERIFICATION_IN_PROGRESS;
- return auth_string;
-}
-
-bool UKey2Handshake::VerifyHandshake() {
- if (handshake_state_ != InternalState::HANDSHAKE_VERIFICATION_IN_PROGRESS) {
- std::ostringstream stream;
- stream << "Unexpected state: " << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return false;
- }
-
- handshake_state_ = InternalState::HANDSHAKE_FINISHED;
- return true;
-}
-
-std::unique_ptr<D2DConnectionContextV1> UKey2Handshake::ToConnectionContext() {
- if (InternalState::HANDSHAKE_FINISHED != handshake_state_) {
- std::ostringstream stream;
- stream << "ToConnectionContext can only be called when handshake is "
- << "completed, but current state is "
- << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return nullptr;
- }
-
- if (!derived_secret_key_) {
- SetError("Derived key is null.");
- return nullptr;
- }
-
- string info = wrapped_client_init_ + wrapped_server_init_;
- std::unique_ptr<string> master_key_data = CryptoOps::Hkdf(
- derived_secret_key_->data().String(), kUkey2HkdfSalt, info);
-
- if (!master_key_data) {
- SetError("Failed to create master key.");
- return nullptr;
- }
-
- // Derive separate encode keys for both client and server.
- CryptoOps::SecretKey master_key(*master_key_data, CryptoOps::AES_256_KEY);
- std::unique_ptr<CryptoOps::SecretKey> client_key =
- D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "client");
- std::unique_ptr<CryptoOps::SecretKey> server_key =
- D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "server");
- if (!client_key || !server_key) {
- SetError("Failed to derive client or server key.");
- return nullptr;
- }
-
- handshake_state_ = InternalState::HANDSHAKE_ALREADY_USED;
-
- return std::unique_ptr<D2DConnectionContextV1>(new D2DConnectionContextV1(
- handshake_role_ == HandshakeRole::CLIENT ? *client_key : *server_key,
- handshake_role_ == HandshakeRole::CLIENT ? *server_key : *client_key,
- 0 /* initial encode sequence number */,
- 0 /* initial decode sequence number */));
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::ParseClientInitUkey2Message(
- const string& handshake_message) {
- // Deserialize the protobuf.
- Ukey2Message message;
- if (!message.ParseFromString(handshake_message)) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_MESSAGE,
- "Can't parse message 1.");
- }
-
- // Verify that message_type == CLIENT_INIT.
- if (!message.has_message_type() ||
- message.message_type() != Ukey2Message::CLIENT_INIT) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE,
- "Expected, but did not find ClientInit message type.");
- }
-
- // Derserialize message_data as a ClientInit message.
- if (!message.has_message_data()) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Expected message data, but did not find it.");
- }
-
- Ukey2ClientInit client_init;
- if (!client_init.ParseFromString(message.message_data())) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Can't parse message data into ClientInit.");
- }
-
- // Check that version == VERSION.
- if (!client_init.has_version()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ClientInit missing version.");
- }
- if (client_init.version() != kVersion) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ClientInit version mismatch.");
- }
-
- // Check that random is exactly kNonceLengthInBytes.
- if (!client_init.has_random()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_RANDOM,
- "ClientInit missing random.");
- }
- if (client_init.random().length() != kNonceLengthInBytes) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_RANDOM, "ClientInit has incorrect nonce length.");
- }
-
- // Check to see if any of the handshake_cipher in handshake_cipher_commitment
- // are acceptable. Servers should select the first ahdnshake_cipher that it
- // finds acceptable to support clients signalling deprecated but supported
- // HandshakeCiphers. If no handshake_cipher is acceptable (or there are no
- // HandshakeCiphers in the message), the server sends a BAD_HANDSHAKE_CIPHER
- // alert message.
- if (client_init.cipher_commitments_size() == 0) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "ClientInit is missing cipher commitments.");
- }
-
- for (const Ukey2ClientInit::CipherCommitment& commitment :
- client_init.cipher_commitments()) {
- if (!commitment.has_handshake_cipher() || !commitment.has_commitment() ||
- commitment.commitment().empty()) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "ClientInit has improperly formatted cipher commitment.");
- }
-
- // TODO(aczeskis): for now we only support one cipher, eventually support
- // more.
- if (commitment.handshake_cipher() == static_cast<int>(handshake_cipher_)) {
- peer_commitment_ = commitment.commitment();
- }
- }
-
- if (peer_commitment_.empty()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "No acceptable commitments found");
- }
-
- // Checks that next_protocol contains a protocol that the server supports. We
- // currently only support one protocol.
- if (!client_init.has_next_protocol() ||
- client_init.next_protocol() != kNextProtocol) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_NEXT_PROTOCOL,
- "Incorrect next protocol.");
- }
-
- // Store raw message for AUTH_STRING computation.
- wrapped_client_init_ = handshake_message;
- handshake_state_ = InternalState::SERVER_AFTER_CLIENT_INIT;
- return CreateSuccessResult();
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::ParseServerInitUkey2Message(
- const string& handshake_message) {
- // Deserialize the protobuf.
- Ukey2Message message;
- if (!message.ParseFromString(handshake_message)) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_MESSAGE,
- "Can't parse message 2.");
- }
-
- // Verify that message_type == SERVER_INIT.
- if (!message.has_message_type() ||
- message.message_type() != Ukey2Message::SERVER_INIT) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE,
- "Expected, but did not find SERVER_INIT message type.");
- }
-
- // Derserialize message_data as a ServerInit message.
- if (!message.has_message_data()) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Expected message data, but did not find it.");
- }
-
- Ukey2ServerInit server_init;
- if (!server_init.ParseFromString(message.message_data())) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Can't parse message data into ServerInit.");
- }
-
- // Check that version == VERSION.
- if (!server_init.has_version()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ServerInit missing version.");
- }
- if (server_init.version() != kVersion) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ServerInit version mismatch.");
- }
-
- // Check that random is exactly kNonceLengthInBytes.
- if (!server_init.has_random()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_RANDOM,
- "ServerInit missing random.");
- }
- if (server_init.random().length() != kNonceLengthInBytes) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_RANDOM, "ServerInit has incorrect nonce length.");
- }
-
- // Check that the handshake_cipher matches a handshake cipher that was sent in
- // ClientInit::cipher_commitments().
- if (!server_init.has_handshake_cipher()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "No handshake cipher found.");
- }
-
- Ukey2HandshakeCipher cipher = server_init.handshake_cipher();
- HandshakeCipher server_cipher;
- switch (static_cast<HandshakeCipher>(cipher)) {
- case HandshakeCipher::P256_SHA512:
- server_cipher = static_cast<HandshakeCipher>(cipher);
- break;
- default:
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "No acceptable handshake found.");
- }
-
- // Check that public_key parses into a correct public key structure.
- if (!server_init.has_public_key()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_PUBLIC_KEY,
- "No public key found in ServerInit.");
- }
-
- their_public_key_ = ParsePublicKey(server_init.public_key());
- if (!their_public_key_) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_PUBLIC_KEY,
- "Failed to parse public key.");
- }
-
- // Store raw message for AUTH_STRING computation.
- wrapped_server_init_ = handshake_message;
- handshake_state_ = InternalState::CLIENT_AFTER_SERVER_INIT;
- return CreateSuccessResult();
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::ParseClientFinishUkey2Message(
- const string& handshake_message) {
- // Deserialize the protobuf.
- Ukey2Message message;
- if (!message.ParseFromString(handshake_message)) {
- return CreateFailedResultWithoutAlert("Can't parse message 3.");
- }
-
- // Verify that message_type == CLIENT_FINISH.
- if (!message.has_message_type() ||
- message.message_type() != Ukey2Message::CLIENT_FINISH) {
- return CreateFailedResultWithoutAlert(
- "Expected, but did not find CLIENT_FINISH message type.");
- }
-
- // Verify that the hash of the CLientFinished message matches the expected
- // commitment from ClientInit.
- if (!VerifyCommitment(handshake_message)) {
- return CreateFailedResultWithoutAlert(last_error_);
- }
-
- // Deserialize message_data as a ClientFinished message.
- if (!message.has_message_data()) {
- return CreateFailedResultWithoutAlert(
- "Expected message data, but didn't find it.");
- }
-
- Ukey2ClientFinished client_finished;
- if (!client_finished.ParseFromString(message.message_data())) {
- return CreateFailedResultWithoutAlert("Failed to parse ClientFinished.");
- }
-
- // Check that public_key parses into a correct public key structure.
- if (!client_finished.has_public_key()) {
- return CreateFailedResultWithoutAlert(
- "No public key found in ClientFinished.");
- }
-
- their_public_key_ = ParsePublicKey(client_finished.public_key());
- if (!their_public_key_) {
- return CreateFailedResultWithoutAlert("Failed to parse public key.");
- }
-
- handshake_state_ = InternalState::HANDSHAKE_VERIFICATION_NEEDED;
- return CreateSuccessResult();
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::CreateFailedResultWithAlert(
- Ukey2Alert::AlertType alert_type, const string& error_message) {
- if (!Ukey2Alert_AlertType_IsValid(alert_type)) {
- std::ostringstream stream;
- stream << "Unknown alert type: " << static_cast<int>(alert_type);
- SetError(stream.str());
- return {false, nullptr};
- }
-
- Ukey2Alert alert;
- alert.set_type(alert_type);
- if (!error_message.empty()) {
- alert.set_error_message(error_message);
- }
-
- std::unique_ptr<string> alert_message =
- MakeUkey2Message(Ukey2Message::ALERT, alert.SerializeAsString());
-
- SetError(error_message);
- ParseResult result{false, std::move(alert_message)};
- return result;
-}
-
-UKey2Handshake::ParseResult
-UKey2Handshake::CreateFailedResultWithoutAlert(const string& error_message) {
- SetError(error_message);
- return {false, nullptr};
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::CreateSuccessResult() {
- return {true, nullptr};
-}
-
-bool UKey2Handshake::VerifyCommitment(const string& handshake_message) {
- std::unique_ptr<ByteBuffer> actual_client_finish_hash;
- switch (handshake_cipher_) {
- case HandshakeCipher::P256_SHA512:
- actual_client_finish_hash =
- CryptoOps::Sha512(ByteBuffer(handshake_message));
- break;
- default:
- // Unreachable.
- return false;
- }
-
- if (!actual_client_finish_hash) {
- SetError("Failed to hash ClientFinish message.");
- return false;
- }
-
- // Note: Equals() is a time constant comparison operation.
- if (!actual_client_finish_hash->Equals(peer_commitment_)) {
- SetError("Failed to verify commitment.");
- return false;
- }
-
- return true;
-}
-
-std::unique_ptr<Ukey2ClientInit::CipherCommitment>
-UKey2Handshake::GenerateP256Sha512Commitment() {
- // Generate the corresponding ClientFinished message if it's not done yet.
- if (raw_message3_map_.count(HandshakeCipher::P256_SHA512) == 0) {
- if (!our_key_pair_ || !our_key_pair_->public_key) {
- SetError("Invalid public key.");
- return nullptr;
- }
-
- std::unique_ptr<GenericPublicKey> generic_public_key =
- PublicKeyProtoUtil::EncodePublicKey(*(our_key_pair_->public_key));
- if (!generic_public_key) {
- SetError("Failed to encode generic public key.");
- return nullptr;
- }
-
- Ukey2ClientFinished client_finished;
- client_finished.set_public_key(generic_public_key->SerializeAsString());
- std::unique_ptr<string> serialized_ukey2_message = MakeUkey2Message(
- Ukey2Message::CLIENT_FINISH, client_finished.SerializeAsString());
- if (!serialized_ukey2_message) {
- SetError("Failed to serialized Ukey2Message.");
- return nullptr;
- }
-
- raw_message3_map_[HandshakeCipher::P256_SHA512] = *serialized_ukey2_message;
- }
-
- // Create the SHA512 commitment from raw message 3.
- std::unique_ptr<ByteBuffer> commitment = CryptoOps::Sha512(
- ByteBuffer(raw_message3_map_[HandshakeCipher::P256_SHA512]));
- if (!commitment) {
- SetError("Failed to hash message for commitment.");
- return nullptr;
- }
-
- // Wrap the commitment in a proto.
- std::unique_ptr<Ukey2ClientInit::CipherCommitment>
- handshake_cipher_commitment(new Ukey2ClientInit::CipherCommitment());
- handshake_cipher_commitment->set_handshake_cipher(P256_SHA512);
- handshake_cipher_commitment->set_commitment(commitment->String());
-
- return handshake_cipher_commitment;
-}
-
-std::unique_ptr<string> UKey2Handshake::MakeClientInitUkey2Message() {
- std::unique_ptr<ByteBuffer> nonce =
- CryptoOps::SecureRandom(kNonceLengthInBytes);
- if (!nonce) {
- SetError("Failed to generate nonce.");
- return nullptr;
- }
-
- Ukey2ClientInit client_init;
- client_init.set_version(kVersion);
- client_init.set_random(nonce->String());
- client_init.set_next_protocol(kNextProtocol);
-
- // At the moment, we only support one cipher.
- std::unique_ptr<Ukey2ClientInit::CipherCommitment>
- handshake_cipher_commitment = GenerateP256Sha512Commitment();
- if (!handshake_cipher_commitment) {
- // |last_error_| already set.
- return nullptr;
- }
- *(client_init.add_cipher_commitments()) = *handshake_cipher_commitment;
-
- return MakeUkey2Message(Ukey2Message::CLIENT_INIT,
- client_init.SerializeAsString());
-}
-
-std::unique_ptr<string> UKey2Handshake::MakeServerInitUkey2Message() {
- std::unique_ptr<ByteBuffer> nonce =
- CryptoOps::SecureRandom(kNonceLengthInBytes);
- if (!nonce) {
- SetError("Failed to generate nonce.");
- return nullptr;
- }
-
- if (!our_key_pair_ || !our_key_pair_->public_key) {
- SetError("Invalid key pair.");
- return nullptr;
- }
-
- std::unique_ptr<GenericPublicKey> public_key =
- PublicKeyProtoUtil::EncodePublicKey(*(our_key_pair_->public_key));
- if (!public_key) {
- SetError("Failed to encode public key.");
- return nullptr;
- }
-
- Ukey2ServerInit server_init;
- server_init.set_version(kVersion);
- server_init.set_random(nonce->String());
- server_init.set_handshake_cipher(
- static_cast<Ukey2HandshakeCipher>(handshake_cipher_));
- server_init.set_public_key(public_key->SerializeAsString());
-
- return MakeUkey2Message(Ukey2Message::SERVER_INIT,
- server_init.SerializeAsString());
-}
-
-// Generates the serialized representation of a Ukey2Message based on the
-// provided |type| and |data|. On error, returns nullptr and writes error
-// message to |out_error|.
-std::unique_ptr<string> UKey2Handshake::MakeUkey2Message(
- Ukey2Message::Type type, const string& data) {
- Ukey2Message message;
- if (!Ukey2Message::Type_IsValid(type)) {
- std::ostringstream stream;
- stream << "Invalid message type: " << type;
- SetError(stream.str());
- return nullptr;
- }
- message.set_message_type(type);
-
- // Only ALERT messages can have a blank data field.
- if (type != Ukey2Message::ALERT) {
- if (data.length() == 0) {
- SetError("Cannot send empty message data for non-alert messages");
- return nullptr;
- }
- }
- message.set_message_data(data);
-
- std::unique_ptr<string> serialized(new string());
- message.SerializeToString(serialized.get());
- return serialized;
-}
-
-void UKey2Handshake::SetError(const string& error_message) {
- handshake_state_ = InternalState::HANDSHAKE_ERROR;
- last_error_ = error_message;
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/ukey2_shell.cc b/src/main/cpp/src/securegcm/ukey2_shell.cc
deleted file mode 100644
index 99a35a8..0000000
--- a/src/main/cpp/src/securegcm/ukey2_shell.cc
+++ /dev/null
@@ -1,297 +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.
-
-// The ukey2_shell binary is a command-line based wrapper, exercising the
-// UKey2Handshake class. Its main use is to be run in a Java test, testing the
-// compatibility of the Java and C++ implementations.
-//
-// This program can be run in two modes, initiator or responder (default is
-// initiator):
-// ukey2_shell --mode=initiator --verification_string_length=32
-// ukey2_shell --mode=responder --verification_string_length=32
-//
-// In initiator mode, the program performs the initiator handshake, and in
-// responder mode, it performs the responder handshake.
-//
-// After the handshake is done, the program establishes a secure connection and
-// enters a loop in which it processes the following commands:
-// * encrypt <payload>: encrypts the payload and prints it.
-// * decrypt <message>: decrypts the message and prints the payload.
-// * session_unique: prints the session unique value.
-//
-// IO is performed on stdin and stdout. To provide frame control, all frames
-// will have the following simple format:
-// [ length | bytes ]
-// where |length| is a 4 byte big-endian encoded unsigned integer.
-#include <cassert>
-#include <cstdio>
-#include <iostream>
-#include <memory>
-
-#include "securegcm/ukey2_handshake.h"
-#include "absl/container/fixed_array.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/parse.h"
-
-#define LOG(ERROR) std::cerr
-#define CHECK_EQ(a, b) do { if ((a) != (b)) abort(); } while(0)
-
-ABSL_FLAG(
- int, verification_string_length, 32,
- "The length in bytes of the verification string. Must be a value between 1"
- "and 32.");
-ABSL_FLAG(string, mode, "initiator",
- "The mode to run as: one of [initiator, responder]");
-
-namespace securegcm {
-
-namespace {
-
-// Writes |message| to stdout in the frame format.
-void WriteFrame(const string& message) {
- // Write length of |message| in little-endian.
- const uint32_t length = message.length();
- fputc((length >> (3 * 8)) & 0xFF, stdout);
- fputc((length >> (2 * 8)) & 0xFF, stdout);
- fputc((length >> (1 * 8)) & 0xFF, stdout);
- fputc((length >> (0 * 8)) & 0xFF, stdout);
-
- // Write message to stdout.
- CHECK_EQ(message.length(),
- fwrite(message.c_str(), 1, message.length(), stdout));
- CHECK_EQ(0, fflush(stdout));
-}
-
-// Returns a message read from stdin after parsing it from the frame format.
-string ReadFrame() {
- // Read length of the frame from the stream.
- uint8_t length_data[sizeof(uint32_t)];
- CHECK_EQ(sizeof(uint32_t), fread(&length_data, 1, sizeof(uint32_t), stdin));
-
- uint32_t length = 0;
- length |= static_cast<uint32_t>(length_data[0]) << (3 * 8);
- length |= static_cast<uint32_t>(length_data[1]) << (2 * 8);
- length |= static_cast<uint32_t>(length_data[2]) << (1 * 8);
- length |= static_cast<uint32_t>(length_data[3]) << (0 * 8);
-
- // Read |length| bytes from the stream.
- absl::FixedArray<char> buffer(length);
- CHECK_EQ(length, fread(buffer.data(), 1, length, stdin));
-
- return string(buffer.data(), length);
-}
-
-} // namespace
-
-// Handles the runtime of the program in initiator or responder mode.
-class UKey2Shell {
- public:
- explicit UKey2Shell(int verification_string_length);
- ~UKey2Shell();
-
- // Runs the shell, performing the initiator handshake for authentication.
- bool RunAsInitiator();
-
- // Runs the shell, performing the responder handshake for authentication.
- bool RunAsResponder();
-
- private:
- // Writes the next handshake message obtained from |ukey2_handshake_| to
- // stdout.
- // If an error occurs, |tag| is logged.
- bool WriteNextHandshakeMessage(const string& tag);
-
- // Reads the next handshake message from stdin and parses it using
- // |ukey2_handshake_|.
- // If an error occurs, |tag| is logged.
- bool ReadNextHandshakeMessage(const string& tag);
-
- // Writes the verification string to stdout and waits for a confirmation from
- // stdin.
- bool ConfirmVerificationString();
-
- // After authentication is completed, this function runs the loop handing the
- // secure connection.
- bool RunSecureConnectionLoop();
-
- std::unique_ptr<UKey2Handshake> ukey2_handshake_;
- const int verification_string_length_;
-};
-
-UKey2Shell::UKey2Shell(int verification_string_length)
- : verification_string_length_(verification_string_length) {}
-
-UKey2Shell::~UKey2Shell() {}
-
-bool UKey2Shell::WriteNextHandshakeMessage(const string& tag) {
- const std::unique_ptr<string> message =
- ukey2_handshake_->GetNextHandshakeMessage();
- if (!message) {
- LOG(ERROR) << "Failed to create [" << tag
- << "] message: " << ukey2_handshake_->GetLastError();
- return false;
- }
- WriteFrame(*message);
- return true;
-}
-
-bool UKey2Shell::ReadNextHandshakeMessage(const string& tag) {
- const string message = ReadFrame();
- const UKey2Handshake::ParseResult result =
- ukey2_handshake_->ParseHandshakeMessage(message);
- if (!result.success) {
- LOG(ERROR) << "Failed to parse [" << tag
- << "] message: " << ukey2_handshake_->GetLastError();
- if (result.alert_to_send) {
- WriteFrame(*result.alert_to_send);
- }
- return false;
- }
- return true;
-}
-
-bool UKey2Shell::ConfirmVerificationString() {
- const std::unique_ptr<string> auth_string =
- ukey2_handshake_->GetVerificationString(verification_string_length_);
- if (!auth_string) {
- LOG(ERROR) << "Failed to get verification string: "
- << ukey2_handshake_->GetLastError();
- return false;
- }
- WriteFrame(*auth_string);
-
- // Wait for ack message.
- const string message = ReadFrame();
- if (message != "ok") {
- LOG(ERROR) << "Expected string 'ok'";
- return false;
- }
- ukey2_handshake_->VerifyHandshake();
- return true;
-}
-
-bool UKey2Shell::RunSecureConnectionLoop() {
- const std::unique_ptr<D2DConnectionContextV1> connection_context =
- ukey2_handshake_->ToConnectionContext();
- if (!connection_context) {
- LOG(ERROR) << "Failed to create connection context: "
- << ukey2_handshake_->GetLastError();
- return false;
- }
-
- for (;;) {
- // Parse the next expression.
- const string expression = ReadFrame();
- const size_t pos = expression.find(" ");
- if (pos == std::string::npos) {
- LOG(ERROR) << "Invalid command in connection loop.";
- return false;
- }
- const string command = expression.substr(0, pos);
-
- if (command == "encrypt") {
- const string payload = expression.substr(pos + 1, expression.length());
- std::unique_ptr<string> encoded_message =
- connection_context->EncodeMessageToPeer(payload);
- if (!encoded_message) {
- LOG(ERROR) << "Failed to encode payload of size " << payload.length();
- return false;
- }
- WriteFrame(*encoded_message);
- } else if (command == "decrypt") {
- const string message = expression.substr(pos + 1, expression.length());
- std::unique_ptr<string> decoded_payload =
- connection_context->DecodeMessageFromPeer(message);
- if (!decoded_payload) {
- LOG(ERROR) << "Failed to decode message of size " << message.length();
- return false;
- }
- WriteFrame(*decoded_payload);
- } else if (command == "session_unique") {
- std::unique_ptr<string> session_unique =
- connection_context->GetSessionUnique();
- if (!session_unique) {
- LOG(ERROR) << "Failed to get session unique.";
- return false;
- }
- WriteFrame(*session_unique);
- } else {
- LOG(ERROR) << "Unrecognized command: " << command;
- return false;
- }
- }
-}
-
-bool UKey2Shell::RunAsInitiator() {
- ukey2_handshake_ = UKey2Handshake::ForInitiator(
- UKey2Handshake::HandshakeCipher::P256_SHA512);
- if (!ukey2_handshake_) {
- LOG(ERROR) << "Unable to create UKey2Handshake";
- return false;
- }
-
- // Perform handshake.
- if (!WriteNextHandshakeMessage("Initiator Init")) return false;
- if (!ReadNextHandshakeMessage("Responder Init")) return false;
- if (!WriteNextHandshakeMessage("Initiator Finish")) return false;
- if (!ConfirmVerificationString()) return false;
-
- // Create a connection context.
- return RunSecureConnectionLoop();
-}
-
-bool UKey2Shell::RunAsResponder() {
- ukey2_handshake_ = UKey2Handshake::ForResponder(
- UKey2Handshake::HandshakeCipher::P256_SHA512);
- if (!ukey2_handshake_) {
- LOG(ERROR) << "Unable to create UKey2Handshake";
- return false;
- }
-
- // Perform handshake.
- if (!ReadNextHandshakeMessage("Initiator Init")) return false;
- if (!WriteNextHandshakeMessage("Responder Init")) return false;
- if (!ReadNextHandshakeMessage("Initiator Finish")) return false;
- if (!ConfirmVerificationString()) return false;
-
- // Create a connection context.
- return RunSecureConnectionLoop();
-}
-
-} // namespace securegcm
-
-int main(int argc, char** argv) {
- absl::ParseCommandLine(argc, argv);
-
- const int verification_string_length =
- absl::GetFlag(FLAGS_verification_string_length);
- if (verification_string_length < 1 || verification_string_length > 32) {
- LOG(ERROR) << "Invalid flag value, verification_string_length: "
- << verification_string_length;
- return 1;
- }
-
- securegcm::UKey2Shell shell(verification_string_length);
- int exit_code = 0;
- const string mode = absl::GetFlag(FLAGS_mode);
- if (mode == "initiator") {
- exit_code = !shell.RunAsInitiator();
- } else if (mode == "responder") {
- exit_code = !shell.RunAsResponder();
- } else {
- LOG(ERROR) << "Invalid flag value, mode: " << mode;
- exit_code = 1;
- }
- return exit_code;
-}