diff options
Diffstat (limited to 'src/main/cpp/src/securegcm')
-rw-r--r-- | src/main/cpp/src/securegcm/CMakeLists.txt | 47 | ||||
-rw-r--r-- | src/main/cpp/src/securegcm/d2d_connection_context_v1.cc | 228 | ||||
-rw-r--r-- | src/main/cpp/src/securegcm/d2d_crypto_ops.cc | 151 | ||||
-rw-r--r-- | src/main/cpp/src/securegcm/java_util.cc | 60 | ||||
-rw-r--r-- | src/main/cpp/src/securegcm/ukey2_handshake.cc | 715 | ||||
-rw-r--r-- | src/main/cpp/src/securegcm/ukey2_shell.cc | 297 |
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; -} |