123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- /**
- * famtool - a tool for creating and verifying router families
- */
- #include <cassert>
- #include <iostream>
- #include <fstream>
- #include <unistd.h>
- #include "Crypto.h"
- #include "RouterInfo.h"
- #include "Base.h"
- #include <openssl/x509.h>
- #include <openssl/pem.h>
- #include <openssl/evp.h>
- #include <openssl/ec.h>
- #include <openssl/ssl.h>
- using namespace i2p::crypto;
- using namespace i2p::data;
- static void usage(const std::string & name)
- {
- std::cout << "usage: " << name << " [-h] [-v] [-g -n family -c family.crt -k family.pem] [-s -n family -k family.pem -i router.keys -f router.info] [-V -c family.crt -f router.info]" << std::endl;
- }
- static void printhelp(const std::string & name)
- {
- usage(name);
- std::cout << std::endl;
- std::cout << "generate a new family signing key for family called ``i2pfam''" << std::endl;
- std::cout << name << " -g -n i2pfam -c myfam.crt -k myfam.pem" << std::endl << std::endl;
- std::cout << "sign a router info with family signing key" << std::endl;
- std::cout << name << " -s -n i2pfam -k myfam.pem -i router.keys -f router.info" << std::endl << std::endl;
- std::cout << "verify signed router.info" << std::endl;
- std::cout << name << " -V -n i2pfam -c myfam.pem -f router.info" << std::endl << std::endl;
- }
- static std::shared_ptr<Verifier> LoadCertificate (const std::string& filename)
- {
- std::shared_ptr<Verifier> verifier;
- SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
- int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
- if (ret)
- {
- SSL * ssl = SSL_new (ctx);
- X509 * cert = SSL_get_certificate (ssl);
- if (cert)
- {
- // extract issuer name
- char name[100];
- X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
- char * cn = strstr (name, "CN=");
- if (cn)
- {
- cn += 3;
- char * family = strstr (cn, ".family");
- if (family) family[0] = 0;
- }
- auto pkey = X509_get_pubkey (cert);
- EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
- if (ecKey)
- {
- auto group = EC_KEY_get0_group (ecKey);
- if (group)
- {
- int curve = EC_GROUP_get_curve_name (group);
- if (curve == NID_X9_62_prime256v1)
- {
- uint8_t signingKey[64];
- BIGNUM * x = BN_new(), * y = BN_new();
- EC_POINT_get_affine_coordinates_GFp (group,
- EC_KEY_get0_public_key (ecKey), x, y, NULL);
- bn2buf (x, signingKey, 32);
- bn2buf (y, signingKey + 32, 32);
- BN_free (x); BN_free (y);
- verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
- verifier->SetPublicKey (signingKey);
- }
- }
- EC_KEY_free (ecKey);
- }
- EVP_PKEY_free (pkey);
- }
- SSL_free (ssl);
- }
- SSL_CTX_free (ctx);
- return verifier;
- }
- static bool CreateFamilySignature (const std::string& family, const IdentHash& ident, const std::string & filename, std::string & sig)
- {
- SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
- int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
- if (ret)
- {
- SSL * ssl = SSL_new (ctx);
- EVP_PKEY * pkey = SSL_get_privatekey (ssl);
- EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
- if (ecKey)
- {
- auto group = EC_KEY_get0_group (ecKey);
- if (group)
- {
- int curve = EC_GROUP_get_curve_name (group);
- if (curve == NID_X9_62_prime256v1)
- {
- uint8_t signingPrivateKey[32], buf[50], signature[64];
- bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32);
- ECDSAP256Signer signer (signingPrivateKey);
- size_t len = family.length ();
- memcpy (buf, family.c_str (), len);
- memcpy (buf + len, (const uint8_t *)ident, 32);
- len += 32;
- signer.Sign (buf, len, signature);
- len = Base64EncodingBufferSize (64);
- char * b64 = new char[len+1];
- len = ByteStreamToBase64 (signature, 64, b64, len);
- b64[len] = 0;
- sig = b64;
- delete[] b64;
- }
- else
- return false;
- }
- }
- SSL_free (ssl);
- }
- else
- return false;
- SSL_CTX_free (ctx);
- return true;
- }
- int main(int argc, char * argv[])
- {
- if (argc == 1) {
- usage(argv[0]);
- return -1;
- }
- int opt;
- bool verbose = false;
- bool help = false;
- bool gen = false;
- bool sign = false;
- bool verify = false;
- std::string fam;
- std::string privkey;
- std::string certfile;
- std::string infile;
- std::string infofile;
- std::string outfile;
- while((opt = getopt(argc, argv, "vVhgsn:i:c:k:o:f:")) != -1) {
- switch(opt) {
- case 'v':
- verbose = true;
- break;
- case 'h':
- help = true;
- break;
- case 'g':
- gen = true;
- break;
- case 'n':
- fam = std::string(argv[optind-1]);
- if (fam.size() + 32 > 50) {
- std::cout << "family name too long" << std::endl;
- return 1;
- }
- break;
- case 'f':
- infofile = std::string(argv[optind-1]);
- break;
- case 'i':
- infile = std::string(argv[optind-1]);
- break;
- case 'o':
- outfile = std::string(argv[optind-1]);
- case 'c':
- certfile = std::string(argv[optind-1]);
- break;
- case 'k':
- privkey = std::string(argv[optind-1]);
- break;
- case 'V':
- verify = true;
- break;
- case 's':
- sign = true;
- break;
- default:
- usage(argv[0]);
- return -1;
- }
- }
- if(help) {
- printhelp(argv[0]);
- return 0;
- }
- InitCrypto(false, true, true, false);
- if(!fam.size()) {
- // no family name
- std::cout << "no family name specified" << std::endl;
- return 1;
- }
- // generate family key code
- if(gen) {
- if(!privkey.size()) privkey = fam + ".key";
- if(!certfile.size()) certfile = fam + ".crt";
- std::string cn = fam + ".family.i2p.net";
- FILE * privf = fopen(privkey.c_str(), "w");
- if(!privf) {
- fprintf(stderr, "cannot open %s: %s\n", privkey.c_str(), strerror(errno));
- return 1;
- }
- FILE * certf = fopen(certfile.c_str(), "w");
- if(!certf) {
- fprintf(stderr, "cannot open %s: %s\n", certfile.c_str(), strerror(errno));
- return 1;
- }
- // openssl fagmastery starts here
- EC_KEY * k_priv = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- assert(k_priv);
- EC_KEY_set_asn1_flag(k_priv, OPENSSL_EC_NAMED_CURVE);
- EC_KEY_generate_key(k_priv);
- if(verbose) std::cout << "generated key" << std::endl;
- // TODO: password protection
- PEM_write_ECPrivateKey(privf, k_priv, nullptr, nullptr, 0, nullptr, nullptr);
- fclose(privf);
- if(verbose) std::cout << "wrote private key" << std::endl;
- EVP_PKEY * ev_k = EVP_PKEY_new();
- assert(ev_k);
- assert(EVP_PKEY_assign_EC_KEY(ev_k, k_priv) == 1);
- X509 * x = X509_new();
- assert(x);
- X509_set_version(x, 2);
- ASN1_INTEGER_set(X509_get_serialNumber(x), 0);
- X509_gmtime_adj(X509_get_notBefore(x),0);
- // TODO: make expiration date configurable
- X509_gmtime_adj(X509_get_notAfter(x),(long)60*60*24*365*10);
- X509_set_pubkey(x, ev_k);
- X509_NAME * name = X509_get_subject_name(x);
- X509_NAME_add_entry_by_txt(name,"C", MBSTRING_ASC, (unsigned char *) "XX", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"ST", MBSTRING_ASC, (unsigned char *) "XX", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"L", MBSTRING_ASC, (unsigned char *) "XX", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"O", MBSTRING_ASC, (unsigned char *) "I2P Anonymous Network", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"OU", MBSTRING_ASC, (unsigned char *) "family", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char *) cn.c_str(), -1, -1, 0);
- X509_set_issuer_name(x,name);
- if(verbose) std::cout << "signing cert" << std::endl;
- assert(X509_sign(x, ev_k, EVP_sha256()));
- if(verbose) std::cout << "writing private key" << std::endl;
- PEM_write_X509(certf, x);
- fclose(certf);
- EVP_PKEY_free(ev_k);
- X509_free(x);
- std::cout << "family " << fam << " made" << std::endl;
- }
- if (sign) {
- // sign
- if (!infile.size()) {
- // no router info specified
- std::cerr << "no router keys file specified" << std::endl;
- return 1;
- }
- if (!privkey.size()) {
- // no private key specified
- std::cerr << "no private key specified" << std::endl;
- return 1;
- }
- {
- std::ifstream i;
- i.open(infofile);
- if(!i.is_open()) {
- std::cout << "cannot open " << infofile << std::endl;
- return 1;
- }
- }
- if (verbose) std::cout << "load " << infofile << std::endl;
- PrivateKeys keys;
- {
- std::ifstream fi(infile, std::ifstream::in | std::ifstream::binary);
- if(!fi.is_open()) {
- std::cout << "cannot open " << infile << std::endl;
- return 1;
- }
- fi.seekg (0, std::ios::end);
- size_t len = fi.tellg();
- fi.seekg (0, std::ios::beg);
- uint8_t * k = new uint8_t[len];
- fi.read((char*)k, len);
- if(!keys.FromBuffer(k, len)) {
- std::cout << "invalid key file " << infile << std::endl;
- return 1;
- }
- delete [] k;
- }
- LocalRouterInfo ri(infofile);
- auto ident = ri.GetIdentHash();
- if (verbose) std::cout << "add " << ident.ToBase64() << " to " << fam << std::endl;
- std::string sig;
- if(CreateFamilySignature(fam, ident, privkey, sig)) {
- ri.SetProperty(ROUTER_INFO_PROPERTY_FAMILY, fam);
- ri.SetProperty(ROUTER_INFO_PROPERTY_FAMILY_SIG, sig);
- if (verbose) std::cout << "signed " << sig << std::endl;
- ri.CreateBuffer(keys);
- if(!ri.SaveToFile(infofile)) {
- std::cout << "failed to save to " << infofile << std::endl;
- }
- } else {
- std::cout << "failed to sign router info" << std::endl;
- }
- std::cout << "signed" << std::endl;
- }
- if(verify) {
- if(!infofile.size()) {
- std::cout << "no router info file specified" << std::endl;
- return 1;
- }
- if(!certfile.size()) {
- std::cout << "no family certificate specified" << std::endl;
- return 1;
- }
- auto v = LoadCertificate(certfile);
- if(!v) {
- std::cout << "invalid certificate" << std::endl;
- return 1;
- }
- {
- std::ifstream i;
- i.open(infofile);
- if(!i.is_open()) {
- std::cout << "cannot open " << infofile << std::endl;
- return 1;
- }
- }
- if (verbose) std::cout << "load " << infofile << std::endl;
- LocalRouterInfo ri(infofile);
- auto sig = ri.GetProperty(ROUTER_INFO_PROPERTY_FAMILY_SIG);
- if (ri.GetProperty(ROUTER_INFO_PROPERTY_FAMILY) != fam) {
- std::cout << infofile << " does not belong to " << fam << std::endl;
- return 1;
- }
- auto ident = ri.GetIdentHash();
- uint8_t buf[50];
- size_t len = fam.length();
- memcpy(buf, fam.c_str(), len);
- memcpy(buf + len, (const uint8_t *) ident, 32);
- len += 32;
- uint8_t sigbuf[64];
- Base64ToByteStream(sig.c_str(), sig.length(), sigbuf, 64);
- if(!v->Verify(buf, len, sigbuf)) {
- std::cout << "invalid signature" << std::endl;
- return 1;
- }
- std::cout << "verified" << std::endl;
- }
- return 0;
- }
|