/ libi2pd / Family.cpp
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  }