From 229385955109b866a23c4ac2aa03d4d11044c39d Mon Sep 17 00:00:00 2001 From: "Enrico Loparco (eloparco)" Date: Thu, 24 Jun 2021 09:15:41 +0200 Subject: [HICN-708] Rebase with master Signed-off-by: Enrico Loparco (eloparco) Change-Id: I2122e1d61dd3b2e039972624ffbdbcb3c5610159 --- libtransport/src/auth/CMakeLists.txt | 22 +++ libtransport/src/auth/identity.cc | 116 ++++++++++++ libtransport/src/auth/signer.cc | 208 ++++++++++++++++++++++ libtransport/src/auth/verifier.cc | 335 +++++++++++++++++++++++++++++++++++ 4 files changed, 681 insertions(+) create mode 100644 libtransport/src/auth/CMakeLists.txt create mode 100644 libtransport/src/auth/identity.cc create mode 100644 libtransport/src/auth/signer.cc create mode 100644 libtransport/src/auth/verifier.cc (limited to 'libtransport/src/auth') diff --git a/libtransport/src/auth/CMakeLists.txt b/libtransport/src/auth/CMakeLists.txt new file mode 100644 index 000000000..0e7b5832b --- /dev/null +++ b/libtransport/src/auth/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc + ${CMAKE_CURRENT_SOURCE_DIR}/identity.cc +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) diff --git a/libtransport/src/auth/identity.cc b/libtransport/src/auth/identity.cc new file mode 100644 index 000000000..bd787b9b6 --- /dev/null +++ b/libtransport/src/auth/identity.cc @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +using namespace std; + +namespace transport { +namespace auth { + +Identity::Identity(const string &keystore_path, const string &keystore_pwd, + CryptoSuite suite, unsigned int signature_len, + unsigned int validity_days, const string &subject_name) + : identity_(nullptr), signer_(nullptr) { + parcSecurity_Init(); + + bool success = parcPkcs12KeyStore_CreateFile( + keystore_path.c_str(), keystore_pwd.c_str(), subject_name.c_str(), + parcCryptoSuite_GetSigningAlgorithm(static_cast(suite)), + signature_len, validity_days); + + parcAssertTrue( + success, + "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d, %d) failed.", + keystore_path.c_str(), keystore_pwd.c_str(), subject_name.c_str(), + static_cast(suite), static_cast(signature_len), validity_days); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_path.c_str(), keystore_pwd.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, + parcCryptoSuite_GetCryptoHash(static_cast(suite))); + + signer_ = make_shared(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(string &keystore_path, string &keystore_pwd, + CryptoHashType hash_type) + : identity_(nullptr), signer_(nullptr) { + parcSecurity_Init(); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_path.c_str(), keystore_pwd.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, static_cast(hash_type)); + + signer_ = make_shared(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(const Identity &other) + : identity_(nullptr), signer_(other.signer_) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); +} + +Identity::Identity(Identity &&other) + : identity_(nullptr), signer_(move(other.signer_)) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); + parcIdentity_Release(&other.identity_); +} + +Identity::~Identity() { + if (identity_) parcIdentity_Release(&identity_); + parcSecurity_Fini(); +} + +shared_ptr Identity::getSigner() const { return signer_; } + +string Identity::getFilename() const { + return string(parcIdentity_GetFileName(identity_)); +} + +string Identity::getPassword() const { + return string(parcIdentity_GetPassWord(identity_)); +} + +Identity Identity::generateIdentity(const string &subject_name) { + string keystore_name = "keystore"; + string keystore_password = "password"; + size_t key_length = 1024; + unsigned int validity_days = 30; + CryptoSuite suite = CryptoSuite::RSA_SHA256; + + return Identity(keystore_name, keystore_password, suite, + (unsigned int)key_length, validity_days, subject_name); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/signer.cc b/libtransport/src/auth/signer.cc new file mode 100644 index 000000000..99c3d099d --- /dev/null +++ b/libtransport/src/auth/signer.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +extern "C" { +#ifndef _WIN32 +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#endif +#include +} + +#include + +#define ALLOW_UNALIGNED_READS 1 + +using namespace std; + +namespace transport { +namespace auth { + +Signer::Signer() : signer_(nullptr), key_id_(nullptr) { parcSecurity_Init(); } + +Signer::Signer(PARCSigner *signer) : Signer() { setSigner(signer); } + +Signer::~Signer() { + if (signer_) parcSigner_Release(&signer_); + if (key_id_) parcKeyId_Release(&key_id_); + parcSecurity_Fini(); +} + +void Signer::signPacket(PacketPtr packet) { + parcAssertNotNull(signer_, "Expected non-null signer"); + + const utils::MemBuf &header_chain = *packet; + core::Packet::Format format = packet->getFormat(); + auto suite = getCryptoSuite(); + size_t signature_len = getSignatureSize(); + + if (!packet->authenticationHeader()) { + throw errors::MalformedAHPacketException(); + } + + packet->setSignatureSize(signature_len); + + // Copy IP+TCP / ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); + packet->resetForHash(); + + // Fill in the HICN_AH header + auto now = chrono::duration_cast( + chrono::system_clock::now().time_since_epoch()) + .count(); + packet->setSignatureTimestamp(now); + packet->setValidationAlgorithm(suite); + + // Set the key ID + KeyId key_id; + key_id.first = static_cast( + parcBuffer_Overlay((PARCBuffer *)parcKeyId_GetKeyId(key_id_), 0)); + packet->setKeyId(key_id); + + // Calculate hash + CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_)); + const utils::MemBuf *current = &header_chain; + + hasher.init(); + + do { + hasher.updateBytes(current->data(), current->length()); + current = current->next(); + } while (current != &header_chain); + + CryptoHash hash = hasher.finalize(); + + // Compute signature + PARCSignature *signature = parcSigner_SignDigestNoAlloc( + signer_, hash.hash_, packet->getSignature(), (uint32_t)signature_len); + PARCBuffer *buffer = parcSignature_GetSignature(signature); + size_t bytes_len = parcBuffer_Remaining(buffer); + + if (bytes_len > signature_len) { + throw errors::MalformedAHPacketException(); + } + + // Put signature in AH header + hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); + + // Release allocated objects + parcSignature_Release(&signature); +} + +void Signer::setSigner(PARCSigner *signer) { + parcAssertNotNull(signer, "Expected non-null signer"); + + if (signer_) parcSigner_Release(&signer_); + if (key_id_) parcKeyId_Release(&key_id_); + + signer_ = parcSigner_Acquire(signer); + key_id_ = parcSigner_CreateKeyId(signer_); +} + +size_t Signer::getSignatureSize() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return parcSigner_GetSignatureSize(signer_); +} + +CryptoSuite Signer::getCryptoSuite() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return static_cast(parcSigner_GetCryptoSuite(signer_)); +} + +CryptoHashType Signer::getCryptoHashType() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return static_cast(parcSigner_GetCryptoHashType(signer_)); +} + +PARCSigner *Signer::getParcSigner() const { return signer_; } + +PARCKeyStore *Signer::getParcKeyStore() const { + parcAssertNotNull(signer_, "Expected non-null signer"); + return parcSigner_GetKeyStore(signer_); +} + +AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, PARCKeyStore *key_store) { + parcAssertNotNull(key_store, "Expected non-null key_store"); + + auto crypto_suite = static_cast(suite); + + switch (suite) { + case CryptoSuite::DSA_SHA256: + case CryptoSuite::RSA_SHA256: + case CryptoSuite::RSA_SHA512: + case CryptoSuite::ECDSA_256K1: + break; + default: + throw errors::RuntimeException( + "Invalid crypto suite for asymmetric signer"); + } + + setSigner( + parcSigner_Create(parcPublicKeySigner_Create(key_store, crypto_suite), + PARCPublicKeySignerAsSigner)); +} + +SymmetricSigner::SymmetricSigner(CryptoSuite suite, PARCKeyStore *key_store) { + parcAssertNotNull(key_store, "Expected non-null key_store"); + + auto crypto_suite = static_cast(suite); + + switch (suite) { + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: + break; + default: + throw errors::RuntimeException( + "Invalid crypto suite for symmetric signer"); + } + + setSigner(parcSigner_Create(parcSymmetricKeySigner_Create( + (PARCSymmetricKeyStore *)key_store, + parcCryptoSuite_GetCryptoHash(crypto_suite)), + PARCSymmetricKeySignerAsSigner)); +} + +SymmetricSigner::SymmetricSigner(CryptoSuite suite, const string &passphrase) { + auto crypto_suite = static_cast(suite); + + switch (suite) { + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: + break; + default: + throw errors::RuntimeException( + "Invalid crypto suite for symmetric signer"); + } + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, passphrase.c_str()); + PARCBuffer *key_buf = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + + PARCSymmetricKeyStore *key_store = parcSymmetricKeyStore_Create(key_buf); + PARCSymmetricKeySigner *key_signer = parcSymmetricKeySigner_Create( + key_store, parcCryptoSuite_GetCryptoHash(crypto_suite)); + + setSigner(parcSigner_Create(key_signer, PARCSymmetricKeySignerAsSigner)); + + parcSymmetricKeySigner_Release(&key_signer); + parcSymmetricKeyStore_Release(&key_store); + parcBuffer_Release(&key_buf); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/verifier.cc b/libtransport/src/auth/verifier.cc new file mode 100644 index 000000000..c6648a763 --- /dev/null +++ b/libtransport/src/auth/verifier.cc @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +extern "C" { +#ifndef _WIN32 +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#endif +#include +} + +#include + +using namespace std; + +namespace transport { +namespace auth { + +const std::vector Verifier::DEFAULT_FAILED_POLICIES = { + VerificationPolicy::DROP, + VerificationPolicy::ABORT, +}; + +Verifier::Verifier() + : hasher_(nullptr), + verifier_(nullptr), + verification_failed_cb_(interface::VOID_HANDLER), + failed_policies_(DEFAULT_FAILED_POLICIES) { + parcSecurity_Init(); + PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create(); + verifier_ = + parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier); + parcInMemoryVerifier_Release(&in_memory_verifier); +} + +Verifier::~Verifier() { + if (hasher_) parcCryptoHasher_Release(&hasher_); + if (verifier_) parcVerifier_Release(&verifier_); + parcSecurity_Fini(); +} + +bool Verifier::verifyPacket(PacketPtr packet) { + bool valid_packet = false; + core::Packet::Format format = packet->getFormat(); + + if (!packet->authenticationHeader()) { + throw errors::MalformedAHPacketException(); + } + + // Get crypto suite and hash type + auto suite = static_cast(packet->getValidationAlgorithm()); + PARCCryptoHashType hash_type = parcCryptoSuite_GetCryptoHash(suite); + + // Copy IP+TCP / ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); + + // Fetch packet signature + uint8_t *packet_signature = packet->getSignature(); + size_t signature_len = Verifier::getSignatureSize(packet); + vector signature_raw(packet_signature, + packet_signature + signature_len); + + // Create a signature buffer from the raw packet signature + PARCBuffer *bits = + parcBuffer_Wrap(signature_raw.data(), signature_len, 0, signature_len); + parcBuffer_Rewind(bits); + + // If the signature algo is ECDSA, the signature might be shorter than the + // signature field + PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite); + if (algo == PARCSigningAlgorithm_ECDSA) { + while (parcBuffer_HasRemaining(bits) && parcBuffer_GetUint8(bits) == 0) + ; + parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1); + } + + if (!parcBuffer_HasRemaining(bits)) { + parcBuffer_Release(&bits); + return false; + } + + // Create a signature object from the signature buffer + PARCSignature *signature = parcSignature_Create( + parcCryptoSuite_GetSigningAlgorithm(suite), hash_type, bits); + + // Fetch the key to verify the signature + KeyId key_buffer = packet->getKeyId(); + PARCBuffer *buffer = parcBuffer_Wrap(key_buffer.first, key_buffer.second, 0, + key_buffer.second); + PARCKeyId *key_id = parcKeyId_Create(buffer); + + // Reset fields that are not used to compute signature + packet->resetForHash(); + + // Compute the packet hash + if (!hasher_) + setHasher(parcVerifier_GetCryptoHasher(verifier_, key_id, hash_type)); + CryptoHash local_hash = computeHash(packet); + + // Compare the packet signature to the locally computed one + valid_packet = parcVerifier_VerifyDigestSignature( + verifier_, key_id, local_hash.hash_, suite, signature); + + // Restore the fields that were reset + hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); + + // Release allocated objects + parcBuffer_Release(&buffer); + parcKeyId_Release(&key_id); + parcSignature_Release(&signature); + parcBuffer_Release(&bits); + + return valid_packet; +} + +vector Verifier::verifyPackets( + const vector &packets) { + vector policies(packets.size(), VerificationPolicy::DROP); + + for (unsigned int i = 0; i < packets.size(); ++i) { + if (verifyPacket(packets[i])) { + policies[i] = VerificationPolicy::ACCEPT; + } + + callVerificationFailedCallback(packets[i], policies[i]); + } + + return policies; +} + +vector Verifier::verifyPackets( + const vector &packets, + const unordered_map &suffix_map) { + vector policies(packets.size(), + VerificationPolicy::UNKNOWN); + + for (unsigned int i = 0; i < packets.size(); ++i) { + uint32_t suffix = packets[i]->getName().getSuffix(); + auto manifest_hash = suffix_map.find(suffix); + + if (manifest_hash != suffix_map.end()) { + CryptoHashType hash_type = manifest_hash->second.first; + CryptoHash packet_hash = packets[i]->computeDigest(hash_type); + + if (!CryptoHash::compareBinaryDigest( + packet_hash.getDigest().data(), + manifest_hash->second.second.data(), hash_type)) { + policies[i] = VerificationPolicy::ABORT; + } else { + policies[i] = VerificationPolicy::ACCEPT; + } + } + + callVerificationFailedCallback(packets[i], policies[i]); + } + + return policies; +} + +void Verifier::addKey(PARCKey *key) { parcVerifier_AddKey(verifier_, key); } + +void Verifier::setHasher(PARCCryptoHasher *hasher) { + parcAssertNotNull(hasher, "Expected non-null hasher"); + if (hasher_) parcCryptoHasher_Release(&hasher_); + hasher_ = parcCryptoHasher_Acquire(hasher); +} + +void Verifier::setVerificationFailedCallback( + VerificationFailedCallback verfication_failed_cb, + const vector &failed_policies) { + verification_failed_cb_ = verfication_failed_cb; + failed_policies_ = failed_policies; +} + +void Verifier::getVerificationFailedCallback( + VerificationFailedCallback **verfication_failed_cb) { + *verfication_failed_cb = &verification_failed_cb_; +} + +size_t Verifier::getSignatureSize(const PacketPtr packet) { + return packet->getSignatureSize(); +} + +CryptoHash Verifier::computeHash(PacketPtr packet) { + parcAssertNotNull(hasher_, "Expected non-null hasher"); + + CryptoHasher crypto_hasher(hasher_); + const utils::MemBuf &header_chain = *packet; + const utils::MemBuf *current = &header_chain; + + crypto_hasher.init(); + + do { + crypto_hasher.updateBytes(current->data(), current->length()); + current = current->next(); + } while (current != &header_chain); + + return crypto_hasher.finalize(); +} + +void Verifier::callVerificationFailedCallback(PacketPtr packet, + VerificationPolicy &policy) { + if (verification_failed_cb_ == interface::VOID_HANDLER) { + return; + } + + if (find(failed_policies_.begin(), failed_policies_.end(), policy) != + failed_policies_.end()) { + policy = verification_failed_cb_( + static_cast(*packet), + make_error_code( + protocol::protocol_error::signature_verification_failed)); + } +} + +bool VoidVerifier::verifyPacket(PacketPtr packet) { return true; } + +vector VoidVerifier::verifyPackets( + const vector &packets) { + return vector(packets.size(), VerificationPolicy::ACCEPT); +} + +vector VoidVerifier::verifyPackets( + const vector &packets, + const unordered_map &suffix_map) { + return vector(packets.size(), VerificationPolicy::ACCEPT); +} + +AsymmetricVerifier::AsymmetricVerifier(PARCKey *pub_key) { addKey(pub_key); } + +AsymmetricVerifier::AsymmetricVerifier(const string &cert_path) { + setCertificate(cert_path); +} + +void AsymmetricVerifier::setCertificate(const string &cert_path) { + PARCCertificateFactory *factory = parcCertificateFactory_Create( + PARCCertificateType_X509, PARCContainerEncoding_PEM); + + struct stat buffer; + if (stat(cert_path.c_str(), &buffer) != 0) { + throw errors::RuntimeException("Certificate does not exist"); + } + + PARCCertificate *certificate = + parcCertificateFactory_CreateCertificateFromFile(factory, + cert_path.c_str(), NULL); + PARCKey *key = parcCertificate_GetPublicKey(certificate); + + addKey(key); + + parcKey_Release(&key); + parcCertificateFactory_Release(&factory); +} + +SymmetricVerifier::SymmetricVerifier(const string &passphrase) + : passphrase_(nullptr), signer_(nullptr) { + setPassphrase(passphrase); +} + +SymmetricVerifier::~SymmetricVerifier() { + if (passphrase_) parcBuffer_Release(&passphrase_); + if (signer_) parcSigner_Release(&signer_); +} + +void SymmetricVerifier::setPassphrase(const string &passphrase) { + if (passphrase_) parcBuffer_Release(&passphrase_); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer, passphrase.c_str()); + passphrase_ = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); +} + +void SymmetricVerifier::setSigner(const PARCCryptoSuite &suite) { + parcAssertNotNull(passphrase_, "Expected non-null passphrase"); + + if (signer_) parcSigner_Release(&signer_); + + PARCSymmetricKeyStore *key_store = parcSymmetricKeyStore_Create(passphrase_); + PARCSymmetricKeySigner *key_signer = parcSymmetricKeySigner_Create( + key_store, parcCryptoSuite_GetCryptoHash(suite)); + signer_ = parcSigner_Create(key_signer, PARCSymmetricKeySignerAsSigner); + + PARCKeyId *key_id = parcSigner_CreateKeyId(signer_); + PARCKey *key = parcKey_CreateFromSymmetricKey( + key_id, parcSigner_GetSigningAlgorithm(signer_), passphrase_); + + addKey(key); + setHasher(parcSigner_GetCryptoHasher(signer_)); + + parcSymmetricKeyStore_Release(&key_store); + parcSymmetricKeySigner_Release(&key_signer); + parcKeyId_Release(&key_id); + parcKey_Release(&key); +} + +vector SymmetricVerifier::verifyPackets( + const vector &packets) { + vector policies(packets.size(), VerificationPolicy::DROP); + + for (unsigned int i = 0; i < packets.size(); ++i) { + auto suite = + static_cast(packets[i]->getValidationAlgorithm()); + + if (!signer_ || suite != parcSigner_GetCryptoSuite(signer_)) { + setSigner(suite); + } + + if (verifyPacket(packets[i])) { + policies[i] = VerificationPolicy::ACCEPT; + } + + callVerificationFailedCallback(packets[i], policies[i]); + } + + return policies; +} + +} // namespace auth +} // namespace transport -- cgit 1.2.3-korg