#include "i2pcommon.h" #include "util/rsbase64.h" #include "util/rsdebug.h" namespace i2p { std::string keyToBase32Addr(const std::string &key) { std::string copy(key); // replace I2P specific chars std::replace(copy.begin(), copy.end(), '~', '/'); // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too // std::replace(copy.begin(), copy.end(), '-', '+'); // decode std::vector bin; RsBase64::decode(copy, bin); // hash std::vector sha256 = RsUtil::BinToSha256(bin); // encode std::string out = Radix32::encode(sha256); // i2p uses lowercase std::transform(out.begin(), out.end(), out.begin(), ::tolower); out.append(".b32.i2p"); return out; } const std::string makeOption(const std::string &lhs, const int8_t &rhs) { return lhs + "=" + std::to_string(rhs); } uint16_t readTwoBytesBE(std::vector::const_iterator &p) { uint16_t val = 0; val += *p++; val <<= 8; val += *p++; return val; } std::string publicKeyFromPrivate(std::string const &priv) { /* * https://geti2p.net/spec/common-structures#destination * https://geti2p.net/spec/common-structures#keysandcert * https://geti2p.net/spec/common-structures#certificate */ if (priv.length() < privKeyMinLenth_b64) { RS_WARN("key to short!"); return std::string(); } // creat a copy to work on, need to convert it to standard base64 auto priv_copy(priv); std::replace(priv_copy.begin(), priv_copy.end(), '~', '/'); // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too // std::replace(copy.begin(), copy.end(), '-', '+'); // get raw data std::vector dataPriv; RsBase64::decode(priv_copy, dataPriv); auto p = dataPriv.cbegin(); RS_DBG("dataPriv.size ", dataPriv.size()); size_t publicKeyLen = 256 + 128; // default length (bytes) uint8_t certType = 0; uint16_t len = 0; uint16_t signingKeyType = 0; uint16_t cryptKeyType = 0; // only used for easy break do { try { // jump to certificate p += publicKeyLen; // try to read type and length certType = *p++; len = readTwoBytesBE(p); // only 0 and 5 are used / valid at this point // check for == 0 if (certType == static_cast::type>(CertType::Null)) { /* * CertType.Null * type null is followed by 0x00 0x00 * so len has to be 0! */ RS_DBG("cert is CertType.Null"); publicKeyLen += 3; // add 0x00 0x00 0x00 if (len != 0) // weird RS_DBG("cert is CertType.Null but len != 0"); break; } // check for != 5 if (certType != static_cast::type>(CertType::Key)) { // unsupported RS_DBG("cert type ", certType, " is unsupported"); return std::string(); } RS_DBG("cert is CertType.Key"); publicKeyLen += 7; // = 1 + 2 + 2 + 2 = 7 bytes /* * "Key certificates were introduced in release 0.9.12. Prior to that release, all PublicKeys were 256-byte ElGamal keys, and all SigningPublicKeys were 128-byte DSA-SHA1 keys." * --> there is space for 256+128 bytes, longer keys are splitted and appended to the certificate * We don't need to bother with the splitting here as only the lenght is important! */ // Signing Public Key // likely 7 signingKeyType = readTwoBytesBE(p); RS_DBG("signing pubkey type ", signingKeyType); if (signingKeyType >= 3 && signingKeyType <= 6) { RS_DBG("signing pubkey type ", certType, " has oversize"); // calculate oversize if (signingKeyType >= signingKeyLengths.size()) { // just in case RS_DBG("signing pubkey type ", certType, " cannot be found in size data!"); return std::string(); } auto values = signingKeyLengths[signingKeyType]; if (values.first <= 128) { // just in case, it's supposed to be larger! RS_DBG("signing pubkey type ", certType, " is oversize but size calculation would underflow!"); return std::string(); } publicKeyLen += values.first - 128; // 128 = default DSA key length = the space that can be used before the key must be splitted } // Crypto Public Key // likely 0 cryptKeyType = readTwoBytesBE(p); RS_DBG("crypto pubkey type ", cryptKeyType); // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed break; } catch (const std::out_of_range &e) { RS_DBG("hit an exception! ", e.what()); return std::string(); } } while(false); std::string pub; auto data2 = std::vector(dataPriv.cbegin(), dataPriv.cbegin() + publicKeyLen); RsBase64::encode(data2.data(), data2.size(), pub, false, false); return pub; } bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey) { if (key.length() < pubKeyMinLenth_b64) { RS_WARN("key to short!"); return false; } // creat a copy to work on, need to convert it to standard base64 auto key_copy(key); std::replace(key_copy.begin(), key_copy.end(), '~', '/'); // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too // std::replace(copy.begin(), copy.end(), '-', '+'); // get raw data std::vector data; RsBase64::decode(key_copy, data); auto p = data.cbegin(); constexpr size_t publicKeyLen = 256 + 128; // default length (bytes) uint8_t certType = 0; uint16_t signingKeyType = 0; uint16_t cryptKeyType = 0; // try to read types try { // jump to certificate p += publicKeyLen; // try to read type and skip length certType = *p++; p += 2; // only 0 and 5 are used / valid at this point // check for == 0 if (certType == static_cast::type>(CertType::Null)) { RS_DBG("cert is CertType.Null"); signingKey = "DSA_SHA1"; cryptoKey = "ElGamal"; return true; } // check for != 5 if (certType != static_cast::type>(CertType::Key)) { // unsupported RS_DBG("cert type ", certType, " is unsupported"); return false; } RS_DBG("cert is CertType.Key"); // Signing Public Key // likely 7 signingKeyType = readTwoBytesBE(p); RS_DBG("signing pubkey type ", signingKeyType); // Crypto Public Key // likely 0 cryptKeyType = readTwoBytesBE(p); RS_DBG("crypto pubkey type ", cryptKeyType); } catch (const std::out_of_range &e) { RS_DBG("hit an exception! ", e.what()); return false; } // now convert to string (this would be easier with c++17) #define HELPER(a, b, c) \ case static_cast::type>(a::c): \ b = #c; \ break; switch (signingKeyType) { HELPER(SigningKeyType, signingKey, DSA_SHA1) HELPER(SigningKeyType, signingKey, ECDSA_SHA256_P256) HELPER(SigningKeyType, signingKey, ECDSA_SHA384_P384) HELPER(SigningKeyType, signingKey, ECDSA_SHA512_P521) HELPER(SigningKeyType, signingKey, RSA_SHA256_2048) HELPER(SigningKeyType, signingKey, RSA_SHA384_3072) HELPER(SigningKeyType, signingKey, RSA_SHA512_4096) HELPER(SigningKeyType, signingKey, EdDSA_SHA512_Ed25519) HELPER(SigningKeyType, signingKey, EdDSA_SHA512_Ed25519ph) HELPER(SigningKeyType, signingKey, RedDSA_SHA512_Ed25519) default: RsWarn("unkown signing key type:", signingKeyType); return false; } switch (cryptKeyType) { HELPER(CryptoKeyType, cryptoKey, ElGamal) HELPER(CryptoKeyType, cryptoKey, P256) HELPER(CryptoKeyType, cryptoKey, P384) HELPER(CryptoKeyType, cryptoKey, P521) HELPER(CryptoKeyType, cryptoKey, X25519) default: RsWarn("unkown crypto key type:", cryptKeyType); return false; } #undef HELPER return true; } } // namespace i2p