mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2026-10-08 11:48:31 +00:00
b33 offline keys for an encrypted LeaseSet
This commit is contained in:
+72
-1
@@ -11,6 +11,7 @@
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "Blinding.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -495,6 +496,70 @@ namespace data
|
||||
return l;
|
||||
}
|
||||
|
||||
size_t B33OfflineKeys::FromBuffer (const uint8_t * buf, size_t len, const IdentHash& ident)
|
||||
{
|
||||
m_Buf.clear ();
|
||||
if (len < B33_OFFLINE_KEYS_HEADER_LENGTH || buf[0] != B33_OFFLINE_KEYS_VERSION ||
|
||||
memcmp (buf + 1, ident, IdentHash::len))
|
||||
{
|
||||
LogPrint (eLogWarning, "Identity: ", len, " bytes behind the keys are not b33 offline keys of this destination");
|
||||
return 0;
|
||||
}
|
||||
m_Buf.assign (buf, buf + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t B33OfflineKeys::ToBuffer (uint8_t * buf, size_t len) const
|
||||
{
|
||||
if (m_Buf.size () > len) return 0;
|
||||
memcpy (buf, m_Buf.data (), m_Buf.size ());
|
||||
return m_Buf.size ();
|
||||
}
|
||||
|
||||
OfflinePrivateKeys::OfflinePrivateKeys (const PrivateKeys& keys, const char * date):
|
||||
PrivateKeys (keys)
|
||||
{
|
||||
const uint8_t * buf = GetB33OfflineKeys ().GetBuffer ();
|
||||
size_t len = GetB33OfflineKeys ().GetLen ();
|
||||
if (!len) return;
|
||||
BlindedPublicKey blindedKey (GetPublic ());
|
||||
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (IdentityEx::CreateVerifier (blindedKey.GetBlindedSigType ()));
|
||||
uint8_t blindedPub[i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH]; // 32 max
|
||||
if (!blindedVerifier || !blindedKey.GetBlindedKey (date, blindedPub)) return;
|
||||
blindedVerifier->SetPublicKey (blindedPub);
|
||||
uint16_t numKeys = bufbe16toh (buf + B33_OFFLINE_KEYS_HEADER_LENGTH - 2);
|
||||
size_t offset = B33_OFFLINE_KEYS_HEADER_LENGTH;
|
||||
for (uint16_t i = 0; i < numKeys && offset + OFFLINE_SIGNATURE_HEADER_LENGTH < len; i++)
|
||||
{
|
||||
const uint8_t * key = buf + offset;
|
||||
SigningKeyType transientSigType = bufbe16toh (key + 4);
|
||||
std::unique_ptr<i2p::crypto::Verifier> transientVerifier (IdentityEx::CreateVerifier (transientSigType));
|
||||
if (!transientVerifier) break;
|
||||
size_t signedLen = OFFLINE_SIGNATURE_HEADER_LENGTH + transientVerifier->GetPublicKeyLen ();
|
||||
size_t offlineSignatureLen = signedLen + blindedVerifier->GetSignatureLen ();
|
||||
if (offset + offlineSignatureLen + transientVerifier->GetPrivateKeyLen () > len) break;
|
||||
char keyDate[9];
|
||||
// a key is for the day its signature expires at the end of
|
||||
i2p::util::GetDateString (bufbe32toh (key) - SECONDS_PER_DAY, keyDate);
|
||||
if (!strncmp (keyDate, date, 8))
|
||||
{
|
||||
if (!blindedVerifier->Verify (key, signedLen, key + signedLen))
|
||||
{
|
||||
LogPrint (eLogError, "Identity: b33 offline key for ", date, " is not signed by the blinded key of that day");
|
||||
return;
|
||||
}
|
||||
m_Signer.reset (CreateSigner (transientSigType, key + offlineSignatureLen));
|
||||
if (!m_Signer) return;
|
||||
m_SignatureLen = transientVerifier->GetSignatureLen ();
|
||||
m_OfflineSignature.assign (key, key + offlineSignatureLen);
|
||||
m_TransientPrivateKey = key + offlineSignatureLen;
|
||||
return;
|
||||
}
|
||||
offset += offlineSignatureLen + transientVerifier->GetPrivateKeyLen ();
|
||||
}
|
||||
LogPrint (eLogError, "Identity: No b33 offline key for ", date);
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
||||
{
|
||||
m_Public = std::make_shared<IdentityEx>(Identity (keys));
|
||||
@@ -517,6 +582,7 @@ namespace data
|
||||
m_OfflineSignature = other.m_OfflineSignature;
|
||||
m_TransientSignatureLen = other.m_TransientSignatureLen;
|
||||
m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen;
|
||||
m_B33OfflineKeys = other.m_B33OfflineKeys;
|
||||
m_SigningPrivateKey = other.m_SigningPrivateKey;
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
@@ -528,7 +594,7 @@ namespace data
|
||||
size_t ret = m_Public->GetFullLen () + GetPrivateKeyLen () + m_Public->GetSigningPrivateKeyLen ();
|
||||
if (IsOfflineSignature ())
|
||||
ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen;
|
||||
return ret;
|
||||
return ret + m_B33OfflineKeys.GetLen ();
|
||||
}
|
||||
|
||||
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
|
||||
@@ -597,6 +663,8 @@ namespace data
|
||||
}
|
||||
else
|
||||
CreateSigner (m_Public->GetSigningKeyType ());
|
||||
if (ret < len)
|
||||
ret += m_B33OfflineKeys.FromBuffer (buf + ret, len - ret, m_Public->GetIdentHash ());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -628,6 +696,8 @@ namespace data
|
||||
memcpy (buf + ret, m_SigningPrivateKey.data (), m_TransientSigningPrivateKeyLen);
|
||||
ret += m_TransientSigningPrivateKeyLen;
|
||||
}
|
||||
if (m_B33OfflineKeys.GetLen () && !m_B33OfflineKeys.ToBuffer (buf + ret, len - ret)) return 0;
|
||||
ret += m_B33OfflineKeys.GetLen ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -659,6 +729,7 @@ namespace data
|
||||
m_OfflineSignature = other.m_OfflineSignature;
|
||||
m_TransientSignatureLen = other.m_TransientSignatureLen;
|
||||
m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen;
|
||||
m_B33OfflineKeys = other.m_B33OfflineKeys;
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "Base.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Signature.h"
|
||||
#include "Tag.h"
|
||||
|
||||
@@ -153,6 +154,28 @@ namespace data
|
||||
|
||||
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
|
||||
|
||||
const size_t OFFLINE_SIGNATURE_HEADER_LENGTH = 4 + 2; // expires, transient signature type
|
||||
const uint8_t B33_OFFLINE_KEYS_VERSION = 1;
|
||||
const size_t B33_OFFLINE_KEYS_HEADER_LENGTH = 1 + IdentHash::len + 2; // version, ident hash, number of keys
|
||||
const uint64_t SECONDS_PER_DAY = 24*60*60;
|
||||
|
||||
// version || ident hash || number of keys, then an offline signature per day. Appended to the keys
|
||||
// file, where a router without b33 offline keys does not look for it
|
||||
class B33OfflineKeys
|
||||
{
|
||||
public:
|
||||
|
||||
size_t GetLen () const { return m_Buf.size (); };
|
||||
const uint8_t * GetBuffer () const { return m_Buf.data (); };
|
||||
bool operator== (const B33OfflineKeys& other) const { return m_Buf == other.m_Buf; };
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len, const IdentHash& ident);
|
||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<uint8_t> m_Buf;
|
||||
};
|
||||
|
||||
class PrivateKeys // for eepsites
|
||||
{
|
||||
public:
|
||||
@@ -191,6 +214,7 @@ namespace data
|
||||
// offline keys
|
||||
PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const;
|
||||
const std::vector<uint8_t>& GetOfflineSignature () const { return m_OfflineSignature; };
|
||||
const B33OfflineKeys& GetB33OfflineKeys () const { return m_B33OfflineKeys; };
|
||||
void UpdateOfflineSignature (const PrivateKeys& other); // refresh transient material, keep identity
|
||||
|
||||
private:
|
||||
@@ -208,6 +232,29 @@ namespace data
|
||||
std::vector<uint8_t> m_OfflineSignature; // non zero length, if applicable
|
||||
size_t m_TransientSignatureLen = 0;
|
||||
size_t m_TransientSigningPrivateKeyLen = 0;
|
||||
B33OfflineKeys m_B33OfflineKeys;
|
||||
};
|
||||
|
||||
// the destination's signing key is offline: a transient per day out of the b33 offline keys,
|
||||
// each authorized by the blinded key of its own day
|
||||
class OfflinePrivateKeys: public PrivateKeys
|
||||
{
|
||||
public:
|
||||
|
||||
OfflinePrivateKeys (const PrivateKeys& keys, const char * date); // date is 8 chars "YYYYMMDD"
|
||||
|
||||
bool IsOfflineSignature () const { return m_TransientPrivateKey != nullptr; }; // false if that day can't be signed
|
||||
const uint8_t * GetSigningPrivateKey () const { return m_TransientPrivateKey ? m_TransientPrivateKey : PrivateKeys::GetSigningPrivateKey (); };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Signer->Sign (buf, len, signature); };
|
||||
size_t GetSignatureLen () const { return m_SignatureLen; };
|
||||
const std::vector<uint8_t>& GetOfflineSignature () const { return m_OfflineSignature; };
|
||||
|
||||
private:
|
||||
|
||||
size_t m_SignatureLen = 0;
|
||||
const uint8_t * m_TransientPrivateKey = nullptr; // points into the b33 offline keys
|
||||
std::vector<uint8_t> m_OfflineSignature;
|
||||
std::unique_ptr<i2p::crypto::Signer> m_Signer;
|
||||
};
|
||||
|
||||
// destination for delivery instructions
|
||||
|
||||
+29
-9
@@ -993,21 +993,33 @@ namespace data
|
||||
}
|
||||
size_t lenOuterCiphertext = lenOuterPlaintext + 32;
|
||||
|
||||
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
|
||||
m_Buffer = new uint8_t[m_BufferLen + 1];
|
||||
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
|
||||
BlindedPublicKey blindedKey (ls->GetIdentity ());
|
||||
auto timestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
char date[9];
|
||||
i2p::util::GetDateString (timestamp, date);
|
||||
OfflinePrivateKeys offlineKeys (keys, date);
|
||||
uint8_t blindedPriv[i2p::crypto::EDDSA25519_PRIVATE_KEY_LENGTH], blindedPub[i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH]; // 32 and 32 max
|
||||
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
|
||||
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
|
||||
if (!blindedSigner)
|
||||
std::unique_ptr<i2p::crypto::Signer> blindedSigner;
|
||||
size_t publicKeyLen = 0;
|
||||
if (offlineKeys.IsOfflineSignature ())
|
||||
publicKeyLen = blindedKey.GetBlindedKey (date, blindedPub); // the transient is authorized by it, not blinded from it
|
||||
else if (!keys.IsOfflineSignature ())
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet2: Can't create blinded signer for signature type ", blindedKey.GetSigType ());
|
||||
publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
|
||||
blindedSigner.reset (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
|
||||
memset (blindedPriv, 0, sizeof (blindedPriv)); // it would give the destination's key away
|
||||
}
|
||||
if (!publicKeyLen || (!blindedSigner && !offlineKeys.IsOfflineSignature ()))
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet2: Can't sign an encrypted LeaseSet for ", date);
|
||||
return;
|
||||
}
|
||||
const auto& offlineSignature = offlineKeys.GetOfflineSignature ();
|
||||
m_BufferLen = 2/*blinded sig type*/ + publicKeyLen + 4/*published*/ + 2/*expires*/ + 2/*flags*/ +
|
||||
offlineSignature.size () + 2/*lenOuterCiphertext*/ + lenOuterCiphertext +
|
||||
(blindedSigner ? 64/*signature*/ : offlineKeys.GetSignatureLen ());
|
||||
m_Buffer = new uint8_t[m_BufferLen + 1];
|
||||
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
|
||||
auto offset = 1;
|
||||
htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
|
||||
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key
|
||||
@@ -1017,8 +1029,13 @@ namespace data
|
||||
if (expirationTime > nextMidnight) expirationTime = nextMidnight;
|
||||
SetExpirationTime (expirationTime*1000LL);
|
||||
htobe16buf (m_Buffer + offset, expirationTime > timestamp ? expirationTime - timestamp : 0); offset += 2; // expires
|
||||
uint16_t flags = 0;
|
||||
uint16_t flags = offlineKeys.IsOfflineSignature () ? LEASESET2_FLAG_OFFLINE_KEYS : 0;
|
||||
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
|
||||
if (flags)
|
||||
{
|
||||
memcpy (m_Buffer + offset, offlineSignature.data (), offlineSignature.size ());
|
||||
offset += offlineSignature.size ();
|
||||
}
|
||||
htobe16buf (m_Buffer + offset, lenOuterCiphertext); offset += 2; // lenOuterCiphertext
|
||||
// outerChipherText
|
||||
// Layer 1
|
||||
@@ -1058,7 +1075,10 @@ namespace data
|
||||
offset += lenInnerPlaintext;
|
||||
i2p::crypto::ChaCha20 (outerPlainText, lenOuterPlaintext, keys1, keys1 + 32, outerPlainText); // encrypt Layer 1
|
||||
// signature
|
||||
blindedSigner->Sign (m_Buffer, offset, m_Buffer + offset);
|
||||
if (blindedSigner)
|
||||
blindedSigner->Sign (m_Buffer, offset, m_Buffer + offset);
|
||||
else
|
||||
offlineKeys.Sign (m_Buffer, offset, m_Buffer + offset);
|
||||
// store hash
|
||||
m_StoreHash = blindedKey.GetStoreHash (date);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user