improved checking of short invite / pgp key in friend server. Added a key parsing method in PGPKeyManagement

This commit is contained in:
csoler 2021-11-10 23:36:23 +01:00
parent 0cf889d556
commit 5e50f23423
4 changed files with 108 additions and 21 deletions

View File

@ -54,7 +54,12 @@ namespace librs
return Sha1CheckSum(h);
}
template<>
HashStream& operator<<(HashStream& u,const std::pair<unsigned char *,uint32_t>& p)
{
EVP_DigestUpdate(u.mdctx,p.first,p.second) ;
return u;
}
template<>
HashStream& operator<<(HashStream& u,const std::string& s)
{

View File

@ -21,6 +21,7 @@
*******************************************************************************/
#include <stdint.h>
#include <util/radix64.h>
#include <crypto/hashstream.h>
#include "pgpkeyutil.h"
#include <iostream>
@ -181,6 +182,59 @@ uint32_t PGPKeyManagement::compute24bitsCRC(unsigned char *octets, size_t len)
return crc & 0xFFFFFFL;
}
bool PGPKeyManagement::parsePGPPublicKey(const unsigned char *keydata, size_t keylen, PGPKeyInfo& info)
{
#ifdef DEBUG_PGPUTIL
std::cerr << "Total size: " << keylen << std::endl;
#endif
unsigned char *data = (unsigned char*)keydata;
uint8_t packet_tag;
uint32_t packet_length ;
PGPKeyParser::read_packetHeader(data,packet_tag,packet_length) ;
#ifdef DEBUG_PGPUTIL
std::cerr << "Packet tag : " << (int)packet_tag << ", length=" << packet_length << std::endl;
#endif
if(packet_tag != PGPKeyParser::PGP_PACKET_TAG_PUBLIC_KEY)
{
std::cerr << "(EE) Parsing error in PGP public key. Expected a public key tag (6). Found " << (int)packet_tag << " instead." << std::endl;
return false;
}
librs::crypto::HashStream H(librs::crypto::HashStream::SHA1);
H << (uint8_t)0x99; // RFC_4880
std::cerr << "Packet length = " << packet_length << std::endl;
H << (uint8_t)(packet_length >> 8);
H << (uint8_t)(packet_length);
H << std::make_pair(data,packet_length) ;
auto hash = H.hash();
memcpy(info.fingerprint, hash.toByteArray(),hash.SIZE_IN_BYTES);
data += packet_length;
// Read user ID.
PGPKeyParser::read_packetHeader(data,packet_tag,packet_length) ;
if(packet_tag != PGPKeyParser::PGP_PACKET_TAG_USER_ID)
{
std::cerr << "(EE) Parsing error in PGP public key. Expected a user ID key tag (13). Found " << (int)packet_tag << " instead." << std::endl;
return false;
}
info.user_id.clear();
for(uint32_t i=0;i<packet_length;++i)
info.user_id += (char)(data[i]);
return true ;
}
bool PGPKeyManagement::parseSignature(const unsigned char *signature, size_t sign_len, PGPSignatureInfo& info)
{
unsigned char *data = (unsigned char *)signature ;

View File

@ -81,6 +81,16 @@ public:
uint8_t hash_algorithm ;
};
class PGPKeyInfo
{
public:
PGPKeyInfo() {}
std::string user_id;
unsigned char fingerprint[20];
};
// This class handles GPG keys. For now we only clean them from signatures, but
// in the future, we might cache them to avoid unnecessary calls to gpgme.
//
@ -107,6 +117,8 @@ class PGPKeyManagement
static uint32_t compute24bitsCRC(unsigned char *data,size_t len) ;
static bool parseSignature(const unsigned char *signature, size_t sign_len, PGPSignatureInfo& info) ;
static bool parsePGPPublicKey(const unsigned char *keydata, size_t keylen, PGPKeyInfo& info);
};
// This class handles the parsing of PGP packet headers under various (old and new) formats.

View File

@ -4,6 +4,7 @@
#include "util/rsbase64.h"
#include "util/radix64.h"
#include "pgp/pgpkeyutil.h"
#include "pgp/rscertificate.h"
#include "friendserver.h"
@ -34,10 +35,10 @@ void FriendServer::threadTick()
switch(fsitem->PacketSubType())
{
case RS_PKT_SUBTYPE_FS_CLIENT_REMOVE: handleClientRemove(dynamic_cast<RsFriendServerClientRemoveItem*>(fsitem));
break;
case RS_PKT_SUBTYPE_FS_CLIENT_PUBLISH: handleClientPublish(dynamic_cast<RsFriendServerClientPublishItem*>(fsitem));
break;
case RS_PKT_SUBTYPE_FS_CLIENT_REMOVE: handleClientRemove(dynamic_cast<RsFriendServerClientRemoveItem*>(fsitem));
break;
default: ;
}
delete item;
@ -169,23 +170,27 @@ std::map<std::string, bool> FriendServer::computeListOfFriendInvites(uint32_t nb
std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64)
{
// 1 - Check that the incoming data is sound.
RsDbg() << " Checking item data...";
std::string error_string;
RsPgpId pgp_id ;
std::vector<uint8_t> key_binary_data ;
// key_binary_data = Radix64::decode(pgp_public_key_b64);
if(RsBase64::decode(pgp_public_key_b64,key_binary_data))
throw std::runtime_error(" Cannot decode client pgp public key: \"" + pgp_public_key_b64 + "\". Wrong format??");
RsDbg() << " Public key radix is fine." ;
RsDbg() << " Parsing public key:" ;
if(!mPgpHandler->LoadCertificateFromBinaryData(key_binary_data.data(),key_binary_data.size(), pgp_id, error_string))
throw std::runtime_error("Cannot load client's pgp public key into keyring: " + error_string) ;
PGPKeyInfo received_key_info;
RsDbg() << " Public key added to keyring.";
if(!PGPKeyManagement::parsePGPPublicKey(key_binary_data.data(),key_binary_data.size(),received_key_info))
throw std::runtime_error("Cannot parse received pgp public key.") ;
RsDbg() << " Issuer : \"" << received_key_info.user_id << "\"" ;
RsDbg() << " Fingerprint: " << RsPgpFingerprint::fromBufferUnsafe(received_key_info.fingerprint) ;
RsDbg() << " Parsing short invite:" ;
RsPeerDetails shortInviteDetails;
uint32_t errorCode = 0;
@ -193,19 +198,30 @@ std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(con
if(short_invite_b64.empty() || !RsCertificate::decodeRadix64ShortInvite(short_invite_b64, shortInviteDetails,errorCode ))
throw std::runtime_error("Could not parse short certificate. Error = " + RsUtil::NumberToString(errorCode));
RsDbg() << " Short invite is fine. PGP fingerprint: " << shortInviteDetails.fpr ;
RsDbg() << " Fingerprint: " << shortInviteDetails.fpr ;
RsDbg() << " Peer ID: " << shortInviteDetails.id ;
if(shortInviteDetails.fpr != RsPgpFingerprint::fromBufferUnsafe(received_key_info.fingerprint))
throw std::runtime_error("Fingerpring from short invite and public key are different! Very unexpected! Message will be ignored.");
// 3 - if the key is not already here, add it to keyring.
{
RsPgpFingerprint fpr_test;
if(!mPgpHandler->getKeyFingerprint(pgp_id,fpr_test))
throw std::runtime_error("Cannot get fingerprint from keyring for client public key. Something's really wrong.") ;
if(mPgpHandler->isPgpPubKeyAvailable(RsPgpId::fromBufferUnsafe(received_key_info.fingerprint+12)))
RsDbg() << " PGP Key is already into keyring.";
else
{
RsPgpId pgp_id;
if(!mPgpHandler->LoadCertificateFromBinaryData(key_binary_data.data(),key_binary_data.size(), pgp_id, error_string))
throw std::runtime_error("Cannot load client's pgp public key into keyring: " + error_string) ;
if(fpr_test != shortInviteDetails.fpr)
throw std::runtime_error("Cannot get fingerprint from keyring for client public key. Something's really wrong.") ;
RsDbg() << " Short invite PGP fingerprint matches the public key fingerprint.";
RsDbg() << " Public key added to keyring.";
RsDbg() << " Sync-ing the PGP keyring on disk";
mPgpHandler->syncDatabase();
}
}
// Check the item's data signature. Is that needed? Not sure, since the data is sent PGP-encrypted, so only the owner
// of the secret PGP key can actually use it.
@ -219,7 +235,7 @@ std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(con
pi.short_certificate = short_invite_b64;
pi.last_connection_TS = time(nullptr);
pi.pgp_fingerprint = fpr_test;
pi.pgp_fingerprint = shortInviteDetails.fpr;
while(pi.last_nonce == 0) // reuse the same identifier (so it's not really a nonce, but it's kept secret whatsoever).
pi.last_nonce = RsRandom::random_u64();