Family.cpp
1 /* 2 * Copyright (c) 2013-2025, The PurpleI2P Project 3 * 4 * This file is part of Purple i2pd project and licensed under BSD3 5 * 6 * See full license text in LICENSE file at top of project tree 7 */ 8 9 #include <string.h> 10 #include <openssl/ssl.h> 11 #include "Crypto.h" 12 #include "FS.h" 13 #include "Log.h" 14 #include "Family.h" 15 #include "Config.h" 16 17 namespace i2p 18 { 19 namespace data 20 { 21 Families::Families () 22 { 23 } 24 25 Families::~Families () 26 { 27 for (auto it : m_SigningKeys) 28 if (it.second.first) EVP_PKEY_free (it.second.first); 29 } 30 31 void Families::LoadCertificate (const std::string& filename) 32 { 33 SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); 34 int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); 35 if (ret) 36 { 37 SSL * ssl = SSL_new (ctx); 38 X509 * cert = SSL_get_certificate (ssl); 39 if (cert) 40 { 41 std::shared_ptr<i2p::crypto::Verifier> verifier; 42 // extract issuer name 43 char name[100]; 44 X509_NAME_oneline (X509_get_issuer_name(cert), name, 100); 45 char * cn = strstr (name, "CN="); 46 if (cn) 47 { 48 cn += 3; 49 char * family = strstr (cn, ".family"); 50 if (family) family[0] = 0; 51 auto pkey = X509_get_pubkey (cert); 52 if (pkey) 53 { 54 int curve = 0; 55 #if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 56 char groupName[20]; 57 if (EVP_PKEY_get_group_name(pkey, groupName, sizeof(groupName), NULL) == 1) 58 curve = OBJ_txt2nid (groupName); 59 else 60 curve = -1; 61 #endif 62 if (!curve || curve == NID_X9_62_prime256v1) 63 { 64 if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second) 65 { 66 EVP_PKEY_free (pkey); 67 LogPrint (eLogError, "Family: Duplicated family name ", cn); 68 } 69 } 70 else 71 LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); 72 } 73 } 74 } 75 SSL_free (ssl); 76 } 77 else 78 LogPrint (eLogError, "Family: Can't open certificate file ", filename); 79 SSL_CTX_free (ctx); 80 } 81 82 void Families::LoadCertificates () 83 { 84 std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family"; 85 86 std::vector<std::string> files; 87 int numCertificates = 0; 88 89 if (!i2p::fs::ReadDir(certDir, files)) { 90 LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir); 91 return; 92 } 93 94 for (const std::string & file : files) { 95 if (file.compare(file.size() - 4, 4, ".crt") != 0) { 96 LogPrint(eLogWarning, "Family: ignoring file ", file); 97 continue; 98 } 99 LoadCertificate (file); 100 numCertificates++; 101 } 102 LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded"); 103 } 104 105 bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, 106 std::string_view signature, const char * key) const 107 { 108 uint8_t buf[100], signatureBuf[64]; 109 size_t len = family.length (); 110 if (len + 32 > 100) 111 { 112 LogPrint (eLogError, "Family: ", family, " is too long"); 113 return false; 114 } 115 auto it = m_SigningKeys.find (family); 116 if (it != m_SigningKeys.end () && it->second.first) 117 { 118 memcpy (buf, family.c_str (), len); 119 memcpy (buf + len, (const uint8_t *)ident, 32); 120 len += 32; 121 auto signatureBufLen = Base64ToByteStream (signature, signatureBuf, 64); 122 if (signatureBufLen == 64) 123 { 124 ECDSA_SIG * sig = ECDSA_SIG_new(); 125 ECDSA_SIG_set0 (sig, BN_bin2bn (signatureBuf, 32, NULL), BN_bin2bn (signatureBuf + 32, 32, NULL)); 126 uint8_t sign[72]; 127 uint8_t * s = sign; 128 auto l = i2d_ECDSA_SIG (sig, &s); 129 ECDSA_SIG_free(sig); 130 EVP_MD_CTX * ctx = EVP_MD_CTX_create (); 131 EVP_DigestVerifyInit (ctx, NULL, EVP_sha256(), NULL, it->second.first); 132 auto ret = EVP_DigestVerify (ctx, sign, l, buf, len) == 1; 133 EVP_MD_CTX_destroy (ctx); 134 return ret; 135 } 136 } 137 // TODO: process key 138 return true; 139 } 140 141 FamilyID Families::GetFamilyID (const std::string& family) const 142 { 143 auto it = m_SigningKeys.find (family); 144 if (it != m_SigningKeys.end ()) 145 return it->second.second; 146 return 0; 147 } 148 149 std::string CreateFamilySignature (const std::string& family, const IdentHash& ident) 150 { 151 auto filename = i2p::fs::DataDirPath("family", (family + ".key")); 152 std::string sig; 153 SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); 154 int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); 155 if (ret) 156 { 157 SSL * ssl = SSL_new (ctx); 158 EVP_PKEY * pkey = SSL_get_privatekey (ssl); 159 int curve = 0; 160 #if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 161 char groupName[20]; 162 if (EVP_PKEY_get_group_name(pkey, groupName, sizeof(groupName), NULL) == 1) 163 curve = OBJ_txt2nid (groupName); 164 else 165 curve = -1; 166 #endif 167 if (!curve || curve == NID_X9_62_prime256v1) 168 { 169 uint8_t buf[100], sign[72], signature[64]; 170 size_t len = family.length (); 171 memcpy (buf, family.c_str (), len); 172 memcpy (buf + len, (const uint8_t *)ident, 32); 173 len += 32; 174 175 size_t l = 72; 176 EVP_MD_CTX * mdctx = EVP_MD_CTX_create (); 177 EVP_DigestSignInit (mdctx, NULL, EVP_sha256(), NULL, pkey); 178 EVP_DigestSign (mdctx, sign, &l, buf, len); 179 EVP_MD_CTX_destroy (mdctx); 180 181 const uint8_t * s1 = sign; 182 ECDSA_SIG * sig1 = d2i_ECDSA_SIG (NULL, &s1, l); 183 const BIGNUM * r, * s; 184 ECDSA_SIG_get0 (sig1, &r, &s); 185 i2p::crypto::bn2buf (r, signature, 32); 186 i2p::crypto::bn2buf (s, signature + 32, 32); 187 ECDSA_SIG_free(sig1); 188 sig = ByteStreamToBase64 (signature, 64); 189 } 190 else 191 LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); 192 193 SSL_free (ssl); 194 } 195 else 196 LogPrint (eLogError, "Family: Can't open keys file: ", filename); 197 SSL_CTX_free (ctx); 198 return sig; 199 } 200 } 201 }