diff options
Diffstat (limited to 'libtransport/src/security')
-rw-r--r-- | libtransport/src/security/CMakeLists.txt | 22 | ||||
-rw-r--r-- | libtransport/src/security/identity.cc | 115 | ||||
-rw-r--r-- | libtransport/src/security/signer.cc | 187 | ||||
-rw-r--r-- | libtransport/src/security/verifier.cc | 241 |
4 files changed, 565 insertions, 0 deletions
diff --git a/libtransport/src/security/CMakeLists.txt b/libtransport/src/security/CMakeLists.txt new file mode 100644 index 000000000..0e7b5832b --- /dev/null +++ b/libtransport/src/security/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/security/identity.cc b/libtransport/src/security/identity.cc new file mode 100644 index 000000000..55713245e --- /dev/null +++ b/libtransport/src/security/identity.cc @@ -0,0 +1,115 @@ +/* + * 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 <hicn/transport/security/identity.h> + +extern "C" { +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Security.h> +} + +namespace utils { + +Identity::Identity(const std::string &keystore_name, + const std::string &keystore_password, CryptoSuite suite, + unsigned int key_length, unsigned int validity_days, + const std::string &subject_name) { + parcSecurity_Init(); + + bool success = parcPkcs12KeyStore_CreateFile( + keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(), + parcCryptoSuite_GetSigningAlgorithm(static_cast<PARCCryptoSuite>(suite)), + key_length, validity_days); + + parcAssertTrue( + success, + "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d) failed.", + keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(), + static_cast<int>(key_length), validity_days); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_name.c_str(), keystore_password.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, + parcCryptoSuite_GetCryptoHash(static_cast<PARCCryptoSuite>(suite))); + + signer_ = std::make_shared<Signer>(signer, suite); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(const Identity &other) + : signer_(other.signer_), hash_algorithm_(other.hash_algorithm_) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); +} + +Identity Identity::generateIdentity(const std::string &subject_name) { + std::string keystore_name = "keystore"; + std::string keystore_password = "password"; + std::size_t key_length = 1024; + unsigned int validity_days = 30; + CryptoSuite suite = CryptoSuite::RSA_SHA256; + + return utils::Identity(keystore_name, keystore_password, suite, + (unsigned int)key_length, validity_days, subject_name); +} + +Identity::Identity(std::string &file_name, std::string &password, + transport::core::HashAlgorithm hash_algorithm) + : hash_algorithm_(hash_algorithm) { + parcSecurity_Init(); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(file_name.c_str(), password.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, static_cast<PARCCryptoHashType>(hash_algorithm)); + + signer_ = std::make_shared<Signer>( + signer, CryptoSuite(parcSigner_GetCryptoSuite(signer))); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::~Identity() { + parcIdentity_Release(&identity_); + parcSecurity_Fini(); +} + +std::string Identity::getFileName() { + return std::string(parcIdentity_GetFileName(identity_)); +} + +std::string Identity::getPassword() { + return std::string(parcIdentity_GetPassWord(identity_)); +} + +std::shared_ptr<Signer> Identity::getSigner() { return signer_; } + +size_t Identity::getSignatureLength() const { + return signer_->getSignatureLength(); +} + +} // namespace utils diff --git a/libtransport/src/security/signer.cc b/libtransport/src/security/signer.cc new file mode 100644 index 000000000..314c3ea82 --- /dev/null +++ b/libtransport/src/security/signer.cc @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * 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 <hicn/transport/errors/malformed_ahpacket_exception.h> +#include <hicn/transport/security/key_id.h> +#include <hicn/transport/security/signer.h> +#include <hicn/transport/utils/membuf.h> + +extern "C" { +#ifndef _WIN32 +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#endif +#include <hicn/hicn.h> +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Security.h> +#include <parc/security/parc_SymmetricKeySigner.h> +} + +#include <chrono> + +#define ALLOW_UNALIGNED_READS 1 + +namespace utils { + +uint8_t Signer::zeros[200] = {0}; + +/*One signer_ per Private Key*/ +Signer::Signer(PARCKeyStore *keyStore, CryptoSuite suite) { + parcSecurity_Init(); + + switch (suite) { + case CryptoSuite::DSA_SHA256: + case CryptoSuite::RSA_SHA256: + case CryptoSuite::RSA_SHA512: + case CryptoSuite::ECDSA_256K1: { + this->signer_ = + parcSigner_Create(parcPublicKeySigner_Create( + keyStore, static_cast<PARCCryptoSuite>(suite)), + PARCPublicKeySignerAsSigner); + break; + } + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: { + this->signer_ = + parcSigner_Create(parcSymmetricKeySigner_Create( + (PARCSymmetricKeyStore *)keyStore, + parcCryptoSuite_GetCryptoHash( + static_cast<PARCCryptoSuite>(suite))), + PARCSymmetricKeySignerAsSigner); + break; + } + default: { return; } + } + + suite_ = suite; + key_id_ = parcSigner_CreateKeyId(this->signer_); + signature_length_ = parcSigner_GetSignatureSize(this->signer_); +} + +Signer::Signer(const std::string &passphrase, CryptoSuite suite) { + parcSecurity_Init(); + + switch (suite) { + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: { + composer_ = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer_, passphrase.c_str()); + key_buffer_ = parcBufferComposer_ProduceBuffer(composer_); + symmetricKeyStore_ = parcSymmetricKeyStore_Create(key_buffer_); + this->signer_ = parcSigner_Create( + parcSymmetricKeySigner_Create( + symmetricKeyStore_, parcCryptoSuite_GetCryptoHash( + static_cast<PARCCryptoSuite>(suite))), + PARCSymmetricKeySignerAsSigner); + break; + } + default: { return; } + } + + suite_ = suite; + key_id_ = parcSigner_CreateKeyId(this->signer_); + signature_length_ = parcSigner_GetSignatureSize(this->signer_); +} + +Signer::Signer(const PARCSigner *signer, CryptoSuite suite) + : signer_(parcSigner_Acquire(signer)), + key_id_(parcSigner_CreateKeyId(this->signer_)), + suite_(suite), + signature_length_(parcSigner_GetSignatureSize(this->signer_)) { + parcSecurity_Init(); +} + +Signer::Signer(const PARCSigner *signer) + : Signer(signer, CryptoSuite::UNKNOWN) {} + +Signer::~Signer() { + if (signature_) parcSignature_Release(&signature_); + if (symmetricKeyStore_) parcSymmetricKeyStore_Release(&symmetricKeyStore_); + if (key_buffer_) parcBuffer_Release(&key_buffer_); + if (composer_) parcBufferComposer_Release(&composer_); + if (signer_) parcSigner_Release(&signer_); + if (key_id_) parcKeyId_Release(&key_id_); + parcSecurity_Fini(); +} + +void Signer::sign(Packet &packet) { + // header chain points to the IP + TCP hicn header + AH Header + MemBuf *header_chain = packet.header_head_; + MemBuf *payload_chain = packet.payload_head_; + uint8_t *hicn_packet = (uint8_t *)header_chain->writableData(); + Packet::Format format = packet.getFormat(); + + if (!(format & HFO_AH)) { + throw errors::MalformedAHPacketException(); + } + + packet.setSignatureSize(signature_length_); + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format, (const hicn_header_t *)packet.packet_start_, + &header_copy, false); + + std::size_t header_len = Packet::getHeaderSizeFromFormat(format); + + packet.resetForHash(); + + /* Fill the hicn_ah header */ + using namespace std::chrono; + auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()) + .count(); + packet.setSignatureTimestamp(now); + packet.setValidationAlgorithm(suite_); + + KeyId key_id; + key_id.first = (uint8_t *)parcBuffer_Overlay( + (PARCBuffer *)parcKeyId_GetKeyId(this->key_id_), 0); + packet.setKeyId(key_id); + + // Calculate hash + CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_)); + hasher.init(); + hasher.updateBytes(hicn_packet, header_len + signature_length_); + + for (MemBuf *current = payload_chain; current != header_chain; + current = current->next()) { + hasher.updateBytes(current->data(), current->length()); + } + + CryptoHash hash = hasher.finalize(); + + signature_ = parcSigner_SignDigestNoAlloc(this->signer_, hash.hash_, + packet.getSignature(), + (uint32_t)signature_length_); + PARCBuffer *buffer = parcSignature_GetSignature(signature_); + + size_t bytes_len = parcBuffer_Remaining(buffer); + + if (bytes_len > signature_length_) { + throw errors::MalformedAHPacketException(); + } + + hicn_packet_copy_header(format, &header_copy, + (hicn_header_t *)packet.packet_start_, false); +} + +size_t Signer::getSignatureLength() { return signature_length_; } + +PARCKeyStore *Signer::getKeyStore() { + return parcSigner_GetKeyStore(this->signer_); +} + +} // namespace utils diff --git a/libtransport/src/security/verifier.cc b/libtransport/src/security/verifier.cc new file mode 100644 index 000000000..19796f718 --- /dev/null +++ b/libtransport/src/security/verifier.cc @@ -0,0 +1,241 @@ +/* + * 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 <hicn/transport/core/packet.h> +#include <hicn/transport/errors/malformed_ahpacket_exception.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/security/key_id.h> +#include <hicn/transport/security/verifier.h> +#include <hicn/transport/utils/log.h> + +extern "C" { +#ifndef _WIN32 +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#endif +#include <hicn/hicn.h> +} + +#include <sys/stat.h> + +namespace utils { + +TRANSPORT_ALWAYS_INLINE bool file_exists(const std::string &name) { + struct stat buffer; + return (stat(name.c_str(), &buffer) == 0); +} + +uint8_t Verifier::zeros[200] = {0}; + +Verifier::Verifier() { + parcSecurity_Init(); + PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create(); + this->verifier_ = + parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier); +} + +Verifier::~Verifier() { + if (key_) parcKey_Release(&key_); + if (keyId_) parcKeyId_Release(&keyId_); + if (signer_) parcSigner_Release(&signer_); + if (symmetricKeyStore_) parcSymmetricKeyStore_Release(&symmetricKeyStore_); + if (key_buffer_) parcBuffer_Release(&key_buffer_); + if (composer_) parcBufferComposer_Release(&composer_); + if (certificate_) parcCertificate_Release(&certificate_); + if (factory_) parcCertificateFactory_Release(&factory_); + if (verifier_) parcVerifier_Release(&verifier_); + parcSecurity_Fini(); +} + +/* + * TODO: Unsupported in libparc + */ +bool Verifier::hasKey(PARCKeyId *keyId) { return false; } + +/* + * TODO: signal errors without trap. + */ +bool Verifier::addKey(PARCKey *key) { + parcVerifier_AddKey(this->verifier_, key); + return true; +} + +PARCKeyId *Verifier::addKeyFromPassphrase(const std::string &passphrase, + CryptoSuite suite) { + composer_ = parcBufferComposer_Create(); + parcBufferComposer_PutString(composer_, passphrase.c_str()); + key_buffer_ = parcBufferComposer_ProduceBuffer(composer_); + + symmetricKeyStore_ = parcSymmetricKeyStore_Create(key_buffer_); + signer_ = parcSigner_Create( + parcSymmetricKeySigner_Create( + symmetricKeyStore_, + parcCryptoSuite_GetCryptoHash(static_cast<PARCCryptoSuite>(suite))), + PARCSymmetricKeySignerAsSigner); + keyId_ = parcSigner_CreateKeyId(signer_); + key_ = parcKey_CreateFromSymmetricKey( + keyId_, parcSigner_GetSigningAlgorithm(signer_), key_buffer_); + + addKey(key_); + return keyId_; +} + +PARCKeyId *Verifier::addKeyFromCertificate(const std::string &file_name) { + factory_ = parcCertificateFactory_Create(PARCCertificateType_X509, + PARCContainerEncoding_PEM); + parcAssertNotNull(factory_, "Expected non-NULL factory"); + + if (!file_exists(file_name)) { + TRANSPORT_LOGW("Warning! The certificate %s file does not exist", + file_name.c_str()); + return nullptr; + } + + certificate_ = parcCertificateFactory_CreateCertificateFromFile( + factory_, (char *)file_name.c_str(), NULL); + PARCBuffer *derEncodedVersion = + parcCertificate_GetDEREncodedPublicKey(certificate_); + PARCCryptoHash *keyDigest = parcCertificate_GetPublicKeyDigest(certificate_); + keyId_ = parcKeyId_Create(parcCryptoHash_GetDigest(keyDigest)); + key_ = parcKey_CreateFromDerEncodedPublicKey(keyId_, PARCSigningAlgorithm_RSA, + derEncodedVersion); + + addKey(key_); + return keyId_; +} + +int Verifier::verify(const Packet &packet) { + // to initialize packet.payload_head_ + const_cast<Packet *>(&packet)->separateHeaderPayload(); + bool valid = false; + + // initialize packet.payload_head_ + const_cast<Packet *>(&packet)->separateHeaderPayload(); + // header chain points to the IP + TCP hicn header + // utils::MemBuf *header_chain = packet.header_head_; + // utils::MemBuf *payload_chain = packet.payload_head_; + // uint8_t *hicn_packet = header_chain->writableData(); + Packet::Format format = packet.getFormat(); + + if (!(packet.format_ & HFO_AH)) { + throw errors::MalformedAHPacketException(); + } + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format, (const hicn_header_t *)packet.packet_start_, + &header_copy, false); + + PARCCryptoSuite suite = + static_cast<PARCCryptoSuite>(packet.getValidationAlgorithm()); + PARCCryptoHashType hashtype = parcCryptoSuite_GetCryptoHash(suite); + KeyId _key_id = packet.getKeyId(); + PARCBuffer *buffer = + parcBuffer_Wrap(_key_id.first, _key_id.second, 0, _key_id.second); + PARCKeyId *key_id = parcKeyId_Create(buffer); + parcBuffer_Release(&buffer); + + int ah_payload_len = (int)packet.getSignatureSize(); + uint8_t *_signature = packet.getSignature(); + uint8_t *signature = new uint8_t[ah_payload_len]; + // TODO Remove signature copy at this point, by not setting to zero + // the validation payload. + std::memcpy(signature, _signature, ah_payload_len); + + std::shared_ptr<CryptoHasher> hasher; + switch (CryptoSuite(suite)) { + case CryptoSuite::DSA_SHA256: + case CryptoSuite::RSA_SHA256: + case CryptoSuite::RSA_SHA512: + case CryptoSuite::ECDSA_256K1: { + hasher = std::make_shared<CryptoHasher>( + parcVerifier_GetCryptoHasher(verifier_, key_id, hashtype)); + break; + } + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::HMAC_SHA512: { + if (!signer_) return false; + hasher = + std::make_shared<CryptoHasher>(parcSigner_GetCryptoHasher(signer_)); + break; + } + default: { return false; } + } + CryptoHash hash_computed_locally = getPacketHash(packet, hasher); + + PARCBuffer *bits = + parcBuffer_Wrap(signature, ah_payload_len, 0, ah_payload_len); + parcBuffer_Rewind(bits); + + /* IF the signature algo is ECDSA, the signature might be shorter than the + * signature field */ + PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite); + while (algo == PARCSigningAlgorithm_ECDSA && parcBuffer_HasRemaining(bits) && + parcBuffer_GetUint8(bits) == 0) + ; + + if (algo == PARCSigningAlgorithm_ECDSA) { + parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1); + } + + if (!parcBuffer_HasRemaining(bits)) { + parcKeyId_Release(&key_id); + parcBuffer_Release(&bits); + return valid; + } + + PARCSignature *signatureToVerify = parcSignature_Create( + parcCryptoSuite_GetSigningAlgorithm(suite), hashtype, bits); + + if (algo == PARCSigningAlgorithm_RSA) { + parcBuffer_SetPosition(bits, 0); + } + + valid = parcVerifier_VerifyDigestSignature( + verifier_, key_id, hash_computed_locally.hash_, suite, signatureToVerify); + + /* Restore the resetted fields */ + hicn_packet_copy_header(format, &header_copy, + (hicn_header_t *)packet.packet_start_, false); + + delete[] signature; + parcKeyId_Release(&key_id); + parcBuffer_Release(&bits); + parcSignature_Release(&signatureToVerify); + + return valid; +} + +CryptoHash Verifier::getPacketHash(const Packet &packet, + std::shared_ptr<CryptoHasher> hasher) { + MemBuf *header_chain = packet.header_head_; + MemBuf *payload_chain = packet.payload_head_; + Packet::Format format = packet.getFormat(); + int ah_payload_len = (int)packet.getSignatureSize(); + uint8_t *hicn_packet = header_chain->writableData(); + std::size_t header_len = Packet::getHeaderSizeFromFormat(format); + + // Reset fields that should not appear in the signature + const_cast<Packet &>(packet).resetForHash(); + hasher->init().updateBytes(hicn_packet, header_len + ah_payload_len); + + for (MemBuf *current = payload_chain; current != header_chain; + current = current->next()) { + hasher->updateBytes(current->data(), current->length()); + } + + return hasher->finalize(); +} + +} // namespace utils |