common/crypto/signature_verifier.cpp (213 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 "common/crypto/signature_verifier.h" #include <cryptopp/aes.h> #include <cryptopp/cmac.h> #include <cryptopp/dsa.h> #include <cryptopp/eccrypto.h> #include <cryptopp/filters.h> #include <cryptopp/hex.h> #include <cryptopp/osrng.h> #include <cryptopp/pssr.h> #include <cryptopp/rsa.h> #include <cryptopp/sha.h> #include <cryptopp/whrlpool.h> #include <glog/logging.h> #include "common/crypto/signature_utils.h" namespace resdb { namespace { // ================== for verify ==================================== bool ED25519verifyString(const std::string& message, const std::string& public_key, const std::string& signature) { CryptoPP::byte byteKey[CryptoPP::ed25519PrivateKey::PUBLIC_KEYLENGTH]; if (public_key.size() != CryptoPP::ed25519PrivateKey::PUBLIC_KEYLENGTH) { LOG(ERROR) << "public key len invalid:" << public_key.size(); return false; } memcpy(byteKey, public_key.c_str(), public_key.size()); auto verifier = CryptoPP::ed25519::Verifier(byteKey); bool valid = true; CryptoPP::StringSource( signature + message, true, new CryptoPP::SignatureVerificationFilter( verifier, new CryptoPP::ArraySink((CryptoPP::byte*)&valid, sizeof(valid)))); if (!valid) { LOG(ERROR) << "signature invalid. signature len:" << signature.size() << " message len:" << message.size(); } return valid; } bool CmacVerifyString(const std::string& message, const std::string& public_key, const std::string& signature) { bool res = false; // KEY TRANSFORMATION // https://stackoverflow.com/questions/26145776/string-to-secbyteblock-conversion CryptoPP::SecByteBlock privKey((const unsigned char*)(public_key.data()), public_key.size()); CryptoPP::CMAC<CryptoPP::AES> cmac(privKey.data(), privKey.size()); const int flags = CryptoPP::HashVerificationFilter::HASH_AT_END | CryptoPP::HashVerificationFilter::PUT_RESULT; // MESSAGE VERIFICATION CryptoPP::StringSource ss3( message + signature, true, new CryptoPP::HashVerificationFilter( cmac, new CryptoPP::ArraySink((CryptoPP::byte*)&res, sizeof(res)), flags)); // StringSource return res; } // ================== for sign ==================================== std::string ED25519signString(const std::string& message, CryptoPP::ed25519::Signer* signer) { CryptoPP::AutoSeededRandomPool prng; std::string signature; CryptoPP::StringSource( message, true, new CryptoPP::SignerFilter(CryptoPP::NullRNG(), *signer, new CryptoPP::StringSink(signature))); return signature; } // CMAC-AES Signature generator // Return a token, mac, to verify the a message. std::string CmacSignString(const std::string& private_key, const std::string& message) { std::string mac = ""; // KEY TRANSFORMATION. // https://stackoverflow.com/questions/26145776/string-to-secbyteblock-conversion CryptoPP::SecByteBlock mac_private_key( (const unsigned char*)(private_key.data()), private_key.size()); CryptoPP::CMAC<CryptoPP::AES> cmac(mac_private_key.data(), mac_private_key.size()); CryptoPP::StringSource ss1( message, true, new CryptoPP::HashFilter(cmac, new CryptoPP::StringSink(mac))); return mac; } } // namespace SignatureVerifier::SignatureVerifier(const KeyInfo& private_key, const CertificateInfo& certificate_info) { private_key_ = private_key; if (private_key_.key().empty()) { LOG(ERROR) << "private invalid"; } node_id_ = certificate_info.node_id(); admin_public_key_ = certificate_info.admin_public_key(); AddPublicKey(certificate_info.public_key(), false); if (private_key_.hash_type() == SignatureInfo::ED25519) { CryptoPP::byte byteKey[CryptoPP::ed25519PrivateKey::SECRET_KEYLENGTH]; if (private_key_.key().size() != CryptoPP::ed25519PrivateKey::SECRET_KEYLENGTH) { return; } memcpy(byteKey, private_key_.key().c_str(), private_key_.key().size()); signer_ = std::make_unique<CryptoPP::ed25519::Signer>(byteKey); } } bool SignatureVerifier::AddPublicKey(const CertificateKey& public_key, bool need_verify) { std::unique_lock<std::shared_mutex> lk(mutex_); if (need_verify && !VerifyKey(public_key.public_key_info(), public_key.certificate())) { return false; } // LOG(ERROR) << "add public key from:" // << public_key.public_key_info().node_id(); keys_[public_key.public_key_info().node_id()] = public_key; return true; } absl::StatusOr<KeyInfo> SignatureVerifier::GetPublicKey(int64_t node_id) const { std::shared_lock<std::shared_mutex> lk(mutex_); auto it = keys_.find(node_id); if (it == keys_.end()) { return absl::InvalidArgumentError("Public key not exist."); } return it->second.public_key_info().key(); } std::vector<CertificateKey> SignatureVerifier::GetAllPublicKeys() const { std::shared_lock<std::shared_mutex> lk(mutex_); std::vector<CertificateKey> keys; for (auto key : keys_) { keys.push_back(key.second); } return keys; } size_t SignatureVerifier::GetPublicKeysSize() const { std::shared_lock<std::shared_mutex> lk(mutex_); return keys_.size(); } // Funtion to calculate hash of a string. std::string SignatureVerifier::CalculateHash(const std::string& str) { CryptoPP::byte const* pData = (CryptoPP::byte*)str.data(); unsigned int nDataLen = str.size(); CryptoPP::byte aDigest[CryptoPP::SHA256::DIGESTSIZE]; CryptoPP::SHA256().CalculateDigest(aDigest, pData, nDataLen); return std::string((char*)aDigest, CryptoPP::SHA256::DIGESTSIZE); } absl::StatusOr<SignatureInfo> SignatureVerifier::SignMessage( const std::string& message) { SignatureInfo info; info.set_node_id(node_id_); switch (private_key_.hash_type()) { case SignatureInfo::RSA: info.set_signature(utils::RsaSignString(private_key_.key(), message)); break; case SignatureInfo::ED25519: info.set_signature(ED25519signString(message, signer_.get())); break; case SignatureInfo::CMAC_AES: info.set_signature(CmacSignString(private_key_.key(), message)); break; case SignatureInfo::ECDSA: info.set_signature(utils::ECDSASignString(private_key_.key(), message)); break; default: break; } return info; } bool SignatureVerifier::VerifyMessage(const google::protobuf::Message& message, const SignatureInfo& sign) { std::string str; if (!message.SerializeToString(&str)) { return false; } return VerifyMessage(str, sign); } bool SignatureVerifier::VerifyMessage(const std::string& message, const SignatureInfo& info) { if (info.signature().empty()) { LOG(ERROR) << " signature is empty"; return false; } auto public_key = GetPublicKey(info.node_id()); if (!public_key.ok()) { LOG(ERROR) << "key not found:" << info.node_id(); return false; } return VerifyMessage(message, *public_key, info.signature()); } absl::StatusOr<SignatureInfo> SignatureVerifier::SignCertificateKeyInfo( const CertificateKeyInfo& info) { std::string str; if (!info.SerializeToString(&str)) { return absl::InvalidArgumentError("Serialize fail"); } return SignatureVerifier::SignMessage(str); } bool SignatureVerifier::VerifyKey(const CertificateKeyInfo& info, const SignatureInfo& sign) { std::string str; if (!info.SerializeToString(&str)) { return false; } return VerifyMessage(str, admin_public_key_, sign.signature()); } bool SignatureVerifier::VerifyMessage(const std::string& message, const KeyInfo& public_key, const std::string& signature) { if (public_key.key().empty() || signature.empty()) { LOG(ERROR) << "public is empty, size(" << public_key.key().size() << ") or signature is empty, size(" << signature.size() << ")"; return false; } // LOG(ERROR) << "public key hash type:" << public_key.hash_type() // << " key size:" << public_key.key().size() // << " sig size:" << signature.size() // << " msg size:" << message.size(); switch (public_key.hash_type()) { case SignatureInfo::RSA: return utils::RsaVerifyString(message, public_key.key(), signature); case SignatureInfo::ED25519: return ED25519verifyString(message, public_key.key(), signature); case SignatureInfo::CMAC_AES: return CmacVerifyString(message, public_key.key(), signature); case SignatureInfo::ECDSA: return utils::ECDSAVerifyString(message, public_key.key(), signature); default: return true; } } } // namespace resdb