2020-05-22 13:18:41 +00:00
/*
2021-08-31 22:51:40 +00:00
* Copyright ( c ) 2013 - 2021 , The PurpleI2P Project
2020-05-22 13:18:41 +00:00
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
2014-12-31 14:14:53 +00:00
# include <string.h>
2014-01-15 01:57:33 +00:00
# include "I2PEndian.h"
2015-11-03 14:15:49 +00:00
# include "Crypto.h"
2013-11-24 23:10:27 +00:00
# include "Log.h"
2019-06-05 19:57:20 +00:00
# include "Tag.h"
2014-01-15 01:57:33 +00:00
# include "Timestamp.h"
2017-04-22 00:04:16 +00:00
# include "NetDb.hpp"
2016-05-25 18:17:34 +00:00
# include "Tunnel.h"
2013-11-24 23:10:27 +00:00
# include "LeaseSet.h"
namespace i2p
{
namespace data
{
2019-01-09 17:47:47 +00:00
LeaseSet : : LeaseSet ( bool storeLeases ) :
2020-03-01 10:25:50 +00:00
m_IsValid ( false ) , m_StoreLeases ( storeLeases ) , m_ExpirationTime ( 0 ) , m_EncryptionKey ( nullptr ) ,
2019-01-14 18:49:27 +00:00
m_Buffer ( nullptr ) , m_BufferLen ( 0 )
2018-12-21 20:00:03 +00:00
{
}
2016-02-08 00:45:06 +00:00
LeaseSet : : LeaseSet ( const uint8_t * buf , size_t len , bool storeLeases ) :
2019-01-14 18:49:27 +00:00
m_IsValid ( true ) , m_StoreLeases ( storeLeases ) , m_ExpirationTime ( 0 ) , m_EncryptionKey ( nullptr )
2013-11-24 23:10:27 +00:00
{
2015-04-08 13:39:02 +00:00
m_Buffer = new uint8_t [ len ] ;
2014-07-29 17:44:54 +00:00
memcpy ( m_Buffer , buf , len ) ;
m_BufferLen = len ;
ReadFromBuffer ( ) ;
}
2018-01-25 15:32:08 +00:00
void LeaseSet : : Update ( const uint8_t * buf , size_t len , bool verifySignature )
2018-01-06 03:48:51 +00:00
{
2015-04-08 13:39:02 +00:00
if ( len > m_BufferLen )
{
auto oldBuffer = m_Buffer ;
m_Buffer = new uint8_t [ len ] ;
delete [ ] oldBuffer ;
2018-01-06 03:48:51 +00:00
}
2014-07-29 17:44:54 +00:00
memcpy ( m_Buffer , buf , len ) ;
m_BufferLen = len ;
2018-01-25 15:32:08 +00:00
ReadFromBuffer ( false , verifySignature ) ;
2014-07-22 00:14:11 +00:00
}
2016-02-08 00:45:06 +00:00
void LeaseSet : : PopulateLeases ( )
{
m_StoreLeases = true ;
ReadFromBuffer ( false ) ;
2018-01-06 03:48:51 +00:00
}
2018-01-25 15:09:34 +00:00
void LeaseSet : : ReadFromBuffer ( bool readIdentity , bool verifySignature )
2018-01-06 03:48:51 +00:00
{
2015-11-03 14:15:49 +00:00
if ( readIdentity | | ! m_Identity )
m_Identity = std : : make_shared < IdentityEx > ( m_Buffer , m_BufferLen ) ;
size_t size = m_Identity - > GetFullLen ( ) ;
2016-02-02 16:55:38 +00:00
if ( size > m_BufferLen )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet: Identity length " , size , " exceeds buffer size " , m_BufferLen ) ;
2016-02-02 16:55:38 +00:00
m_IsValid = false ;
return ;
}
2019-01-14 18:49:27 +00:00
if ( m_StoreLeases )
{
if ( ! m_EncryptionKey ) m_EncryptionKey = new uint8_t [ 256 ] ;
memcpy ( m_EncryptionKey , m_Buffer + size , 256 ) ;
2020-03-01 10:25:50 +00:00
}
2014-08-22 01:57:24 +00:00
size + = 256 ; // encryption key
2015-11-03 14:15:49 +00:00
size + = m_Identity - > GetSigningPublicKeyLen ( ) ; // unused signing key
2021-03-23 00:12:58 +00:00
if ( size + 1 > m_BufferLen )
{
LogPrint ( eLogError , " LeaseSet: " , size , " exceeds buffer size " , m_BufferLen ) ;
m_IsValid = false ;
return ;
2021-11-27 20:30:35 +00:00
}
2014-08-22 01:57:24 +00:00
uint8_t num = m_Buffer [ size ] ;
size + + ; // num
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " LeaseSet: Read num= " , ( int ) num ) ;
2016-02-02 16:55:38 +00:00
if ( ! num | | num > MAX_NUM_LEASES )
2018-01-06 03:48:51 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet: Rncorrect number of leases " , ( int ) num ) ;
2016-02-02 16:55:38 +00:00
m_IsValid = false ;
return ;
2018-01-06 03:48:51 +00:00
}
2021-11-27 20:30:35 +00:00
if ( size + num * LEASE_SIZE > m_BufferLen )
{
2021-03-23 00:12:58 +00:00
LogPrint ( eLogError , " LeaseSet: " , size , " exceeds buffer size " , m_BufferLen ) ;
m_IsValid = false ;
return ;
2021-11-27 20:30:35 +00:00
}
2019-01-09 17:47:47 +00:00
UpdateLeasesBegin ( ) ;
2014-01-26 14:07:31 +00:00
// process leases
2016-02-08 00:45:06 +00:00
m_ExpirationTime = 0 ;
2016-02-10 03:42:01 +00:00
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2014-08-22 01:57:24 +00:00
const uint8_t * leases = m_Buffer + size ;
for ( int i = 0 ; i < num ; i + + )
2013-11-24 23:10:27 +00:00
{
2014-12-31 14:14:53 +00:00
Lease lease ;
2015-03-23 16:55:42 +00:00
lease . tunnelGateway = leases ;
leases + = 32 ; // gateway
lease . tunnelID = bufbe32toh ( leases ) ;
leases + = 4 ; // tunnel ID
2018-01-06 03:48:51 +00:00
lease . endDate = bufbe64toh ( leases ) ;
2015-03-23 16:55:42 +00:00
leases + = 8 ; // end date
2019-01-02 20:40:48 +00:00
UpdateLease ( lease , ts ) ;
2018-01-06 03:48:51 +00:00
}
2016-02-14 23:30:07 +00:00
if ( ! m_ExpirationTime )
2016-02-10 03:42:01 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogWarning , " LeaseSet: All leases are expired. Dropped " ) ;
2016-02-10 03:42:01 +00:00
m_IsValid = false ;
return ;
2018-01-06 03:48:51 +00:00
}
2016-02-14 23:30:07 +00:00
m_ExpirationTime + = LEASE_ENDDATE_THRESHOLD ;
2019-01-09 17:47:47 +00:00
UpdateLeasesEnd ( ) ;
// verify
2021-03-23 00:12:58 +00:00
if ( verifySignature )
2021-11-27 20:30:35 +00:00
{
2021-03-23 00:12:58 +00:00
auto signedSize = leases - m_Buffer ;
if ( signedSize + m_Identity - > GetSignatureLen ( ) > m_BufferLen )
{
LogPrint ( eLogError , " LeaseSet: Signature exceeds buffer size " , m_BufferLen ) ;
m_IsValid = false ;
2021-11-27 20:30:35 +00:00
}
2021-03-23 00:12:58 +00:00
else if ( ! m_Identity - > Verify ( m_Buffer , signedSize , leases ) )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogWarning , " LeaseSet: Verification failed " ) ;
2021-03-23 00:12:58 +00:00
m_IsValid = false ;
}
2019-01-09 17:47:47 +00:00
}
}
void LeaseSet : : UpdateLeasesBegin ( )
{
// reset existing leases
if ( m_StoreLeases )
for ( auto & it : m_Leases )
it - > isUpdated = false ;
else
m_Leases . clear ( ) ;
}
void LeaseSet : : UpdateLeasesEnd ( )
{
2018-01-06 03:48:51 +00:00
// delete old leases
2016-02-09 20:27:23 +00:00
if ( m_StoreLeases )
2018-01-06 03:48:51 +00:00
{
2016-02-09 20:27:23 +00:00
for ( auto it = m_Leases . begin ( ) ; it ! = m_Leases . end ( ) ; )
2018-01-06 03:48:51 +00:00
{
2016-02-09 20:27:23 +00:00
if ( ! ( * it ) - > isUpdated )
{
( * it ) - > endDate = 0 ; // somebody might still hold it
m_Leases . erase ( it + + ) ;
2018-01-06 03:48:51 +00:00
}
2016-02-09 20:27:23 +00:00
else
2016-08-05 18:23:54 +00:00
+ + it ;
2016-02-09 20:27:23 +00:00
}
}
2018-01-06 03:48:51 +00:00
}
2016-02-17 03:57:38 +00:00
2019-01-02 20:40:48 +00:00
void LeaseSet : : UpdateLease ( const Lease & lease , uint64_t ts )
{
if ( ts < lease . endDate + LEASE_ENDDATE_THRESHOLD )
{
if ( lease . endDate > m_ExpirationTime )
m_ExpirationTime = lease . endDate ;
if ( m_StoreLeases )
{
auto ret = m_Leases . insert ( std : : make_shared < Lease > ( lease ) ) ;
if ( ! ret . second ) ( * ret . first ) - > endDate = lease . endDate ; // update existing
( * ret . first ) - > isUpdated = true ;
}
}
else
2020-03-20 14:44:53 +00:00
LogPrint ( eLogWarning , " LeaseSet: Lease is expired already " ) ;
2019-01-02 20:40:48 +00:00
}
2020-04-29 21:11:48 +00:00
uint64_t LeaseSet : : ExtractExpirationTimestamp ( const uint8_t * buf , size_t len ) const
2016-02-17 03:57:38 +00:00
{
if ( ! m_Identity ) return 0 ;
size_t size = m_Identity - > GetFullLen ( ) ;
if ( size > len ) return 0 ;
size + = 256 ; // encryption key
size + = m_Identity - > GetSigningPublicKeyLen ( ) ; // unused signing key
if ( size > len ) return 0 ;
2018-01-26 19:33:06 +00:00
uint8_t num = buf [ size ] ;
2016-02-17 03:57:38 +00:00
size + + ; // num
2016-05-25 18:17:34 +00:00
if ( size + num * LEASE_SIZE > len ) return 0 ;
2016-02-17 03:57:38 +00:00
uint64_t timestamp = 0 ;
for ( int i = 0 ; i < num ; i + + )
{
size + = 36 ; // gateway (32) + tunnelId(4)
2018-01-06 03:48:51 +00:00
auto endDate = bufbe64toh ( buf + size ) ;
2016-02-17 03:57:38 +00:00
size + = 8 ; // end date
if ( ! timestamp | | endDate < timestamp )
timestamp = endDate ;
2018-01-06 03:48:51 +00:00
}
2016-02-17 03:57:38 +00:00
return timestamp ;
2018-01-06 03:48:51 +00:00
}
2016-02-17 03:57:38 +00:00
bool LeaseSet : : IsNewer ( const uint8_t * buf , size_t len ) const
{
2020-05-28 17:46:02 +00:00
return ExtractExpirationTimestamp ( buf , len ) > ExtractExpirationTimestamp ( m_Buffer , m_BufferLen ) ;
2018-01-06 03:48:51 +00:00
}
2016-07-22 13:56:17 +00:00
2016-09-03 15:46:47 +00:00
bool LeaseSet : : ExpiresSoon ( const uint64_t dlt , const uint64_t fudge ) const
2016-07-22 13:56:17 +00:00
{
auto now = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2016-09-03 15:46:47 +00:00
if ( fudge ) now + = rand ( ) % fudge ;
2016-07-22 13:56:17 +00:00
if ( now > = m_ExpirationTime ) return true ;
return m_ExpirationTime - now < = dlt ;
}
2016-08-27 17:17:34 +00:00
2020-03-01 10:25:50 +00:00
const std : : vector < std : : shared_ptr < const Lease > > LeaseSet : : GetNonExpiredLeases ( bool withThreshold ) const
{
return GetNonExpiredLeasesExcluding ( [ ] ( const Lease & l ) - > bool { return false ; } , withThreshold ) ;
}
2018-01-06 03:48:51 +00:00
2016-08-27 17:17:34 +00:00
const std : : vector < std : : shared_ptr < const Lease > > LeaseSet : : GetNonExpiredLeasesExcluding ( LeaseInspectFunc exclude , bool withThreshold ) const
2014-01-15 01:57:33 +00:00
{
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2016-02-11 03:51:08 +00:00
std : : vector < std : : shared_ptr < const Lease > > leases ;
2016-08-05 18:23:54 +00:00
for ( const auto & it : m_Leases )
2015-03-26 14:30:29 +00:00
{
2016-02-09 15:46:27 +00:00
auto endDate = it - > endDate ;
2016-02-14 23:30:07 +00:00
if ( withThreshold )
endDate + = LEASE_ENDDATE_THRESHOLD ;
else
endDate - = LEASE_ENDDATE_THRESHOLD ;
2016-08-27 17:17:34 +00:00
if ( ts < endDate & & ! exclude ( * it ) )
2014-03-23 13:25:16 +00:00
leases . push_back ( it ) ;
2018-01-06 03:48:51 +00:00
}
return leases ;
}
2014-01-15 01:57:33 +00:00
bool LeaseSet : : HasExpiredLeases ( ) const
2018-01-06 04:01:44 +00:00
{
2014-01-15 01:57:33 +00:00
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2016-08-05 18:23:54 +00:00
for ( const auto & it : m_Leases )
2016-02-09 15:46:27 +00:00
if ( ts > = it - > endDate ) return true ;
2014-01-15 01:57:33 +00:00
return false ;
2018-01-06 04:01:44 +00:00
}
2014-01-15 01:57:33 +00:00
2016-02-08 00:45:06 +00:00
bool LeaseSet : : IsExpired ( ) const
2014-01-15 01:57:33 +00:00
{
2016-06-30 21:21:18 +00:00
if ( m_StoreLeases & & IsEmpty ( ) ) return true ;
2014-01-15 01:57:33 +00:00
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2016-02-08 00:45:06 +00:00
return ts > m_ExpirationTime ;
2016-05-25 18:17:34 +00:00
}
2021-08-31 22:51:40 +00:00
void LeaseSet : : Encrypt ( const uint8_t * data , uint8_t * encrypted ) const
2017-11-07 20:05:22 +00:00
{
2019-01-14 18:49:27 +00:00
if ( ! m_EncryptionKey ) return ;
2017-11-07 20:05:22 +00:00
auto encryptor = m_Identity - > CreateEncryptor ( m_EncryptionKey ) ;
if ( encryptor )
2021-09-03 17:30:01 +00:00
encryptor - > Encrypt ( data , encrypted ) ;
2017-11-07 20:05:22 +00:00
}
2018-12-21 20:00:03 +00:00
void LeaseSet : : SetBuffer ( const uint8_t * buf , size_t len )
{
if ( m_Buffer ) delete [ ] m_Buffer ;
m_Buffer = new uint8_t [ len ] ;
m_BufferLen = len ;
memcpy ( m_Buffer , buf , len ) ;
}
2020-03-20 21:43:37 +00:00
void LeaseSet : : SetBufferLen ( size_t len )
{
if ( len < = m_BufferLen ) m_BufferLen = len ;
else
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Actual buffer size " , len , " exceeds full buffer size " , m_BufferLen ) ;
2020-03-01 10:25:50 +00:00
}
2020-02-12 16:09:20 +00:00
LeaseSet2 : : LeaseSet2 ( uint8_t storeType , const uint8_t * buf , size_t len , bool storeLeases , CryptoKeyType preferredCrypto ) :
LeaseSet ( storeLeases ) , m_StoreType ( storeType ) , m_EncryptionType ( preferredCrypto )
2020-03-01 10:25:50 +00:00
{
SetBuffer ( buf , len ) ;
2019-01-08 16:26:50 +00:00
if ( storeType = = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 )
2019-06-06 16:33:33 +00:00
ReadFromBufferEncrypted ( buf , len , nullptr , nullptr ) ;
2019-01-08 16:26:50 +00:00
else
ReadFromBuffer ( buf , len ) ;
2018-12-21 20:00:03 +00:00
}
2020-03-01 10:25:50 +00:00
LeaseSet2 : : LeaseSet2 ( const uint8_t * buf , size_t len , std : : shared_ptr < const BlindedPublicKey > key ,
2020-02-12 16:09:20 +00:00
const uint8_t * secret , CryptoKeyType preferredCrypto ) :
LeaseSet ( true ) , m_StoreType ( NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 ) , m_EncryptionType ( preferredCrypto )
2019-02-27 20:52:47 +00:00
{
2019-06-06 16:33:33 +00:00
ReadFromBufferEncrypted ( buf , len , key , secret ) ;
2019-02-27 20:52:47 +00:00
}
2019-01-17 00:00:17 +00:00
void LeaseSet2 : : Update ( const uint8_t * buf , size_t len , bool verifySignature )
2020-03-01 10:25:50 +00:00
{
2019-01-17 00:00:17 +00:00
SetBuffer ( buf , len ) ;
2019-02-01 22:41:12 +00:00
if ( GetStoreType ( ) ! = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 )
2020-03-01 10:25:50 +00:00
ReadFromBuffer ( buf , len , false , verifySignature ) ;
2019-02-01 17:55:13 +00:00
// TODO: implement encrypted
2019-01-17 00:00:17 +00:00
}
2020-04-29 21:11:48 +00:00
bool LeaseSet2 : : IsNewer ( const uint8_t * buf , size_t len ) const
{
uint64_t expiration ;
return ExtractPublishedTimestamp ( buf , len , expiration ) > m_PublishedTimestamp ;
}
2020-03-01 10:25:50 +00:00
2019-02-01 17:55:13 +00:00
void LeaseSet2 : : ReadFromBuffer ( const uint8_t * buf , size_t len , bool readIdentity , bool verifySignature )
2018-12-21 20:00:03 +00:00
{
2019-01-02 19:19:10 +00:00
// standard LS2 header
2019-02-01 17:55:13 +00:00
std : : shared_ptr < const IdentityEx > identity ;
if ( readIdentity )
2020-03-01 10:25:50 +00:00
{
2019-02-01 17:55:13 +00:00
identity = std : : make_shared < IdentityEx > ( buf , len ) ;
SetIdentity ( identity ) ;
}
else
identity = GetIdentity ( ) ;
2018-12-21 20:00:03 +00:00
size_t offset = identity - > GetFullLen ( ) ;
2019-01-08 16:26:50 +00:00
if ( offset + 8 > = len ) return ;
2019-02-26 21:20:24 +00:00
m_PublishedTimestamp = bufbe32toh ( buf + offset ) ; offset + = 4 ; // published timestamp (seconds)
2018-12-21 20:00:03 +00:00
uint16_t expires = bufbe16toh ( buf + offset ) ; offset + = 2 ; // expires (seconds)
2019-02-26 21:20:24 +00:00
SetExpirationTime ( ( m_PublishedTimestamp + expires ) * 1000LL ) ; // in milliseconds
2018-12-31 19:23:48 +00:00
uint16_t flags = bufbe16toh ( buf + offset ) ; offset + = 2 ; // flags
2019-02-12 19:56:39 +00:00
if ( flags & LEASESET2_FLAG_OFFLINE_KEYS )
2019-01-01 22:00:37 +00:00
{
2019-01-15 23:56:26 +00:00
// transient key
2019-02-06 18:36:03 +00:00
m_TransientVerifier = ProcessOfflineSignature ( identity , buf , len , offset ) ;
if ( ! m_TransientVerifier )
2020-03-01 10:25:50 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Offline signature failed " ) ;
2019-01-15 23:56:26 +00:00
return ;
2019-02-06 18:36:03 +00:00
}
2019-01-01 22:00:37 +00:00
}
2019-07-16 15:48:30 +00:00
if ( flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET ) m_IsPublic = false ;
2019-08-07 19:43:03 +00:00
if ( flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED )
{
m_IsPublishedEncrypted = true ;
m_IsPublic = true ;
2020-03-01 10:25:50 +00:00
}
2019-01-02 19:19:10 +00:00
// type specific part
size_t s = 0 ;
switch ( m_StoreType )
{
case NETDB_STORE_TYPE_STANDARD_LEASESET2 :
s = ReadStandardLS2TypeSpecificPart ( buf + offset , len - offset ) ;
break ;
case NETDB_STORE_TYPE_META_LEASESET2 :
s = ReadMetaLS2TypeSpecificPart ( buf + offset , len - offset ) ;
break ;
default :
LogPrint ( eLogWarning , " LeaseSet2: Unexpected store type " , ( int ) m_StoreType ) ;
}
if ( ! s ) return ;
offset + = s ;
2019-02-06 18:36:03 +00:00
if ( verifySignature | | m_TransientVerifier )
2020-03-01 10:25:50 +00:00
{
2019-02-01 17:55:13 +00:00
// verify signature
2019-02-06 18:36:03 +00:00
bool verified = m_TransientVerifier ? VerifySignature ( m_TransientVerifier , buf , len , offset ) :
2020-03-01 10:25:50 +00:00
VerifySignature ( identity , buf , len , offset ) ;
SetIsValid ( verified ) ;
2019-02-01 17:55:13 +00:00
}
2020-03-20 21:43:37 +00:00
offset + = m_TransientVerifier ? m_TransientVerifier - > GetSignatureLen ( ) : identity - > GetSignatureLen ( ) ;
SetBufferLen ( offset ) ;
2019-01-08 16:26:50 +00:00
}
template < typename Verifier >
bool LeaseSet2 : : VerifySignature ( Verifier & verifier , const uint8_t * buf , size_t len , size_t signatureOffset )
{
if ( signatureOffset + verifier - > GetSignatureLen ( ) > len ) return false ;
2019-01-10 15:57:57 +00:00
// we assume buf inside DatabaseStore message, so buf[-1] is valid memory
2020-03-01 10:25:50 +00:00
// change it for signature verification, and restore back
2019-01-10 15:57:57 +00:00
uint8_t c = buf [ - 1 ] ;
const_cast < uint8_t * > ( buf ) [ - 1 ] = m_StoreType ;
2020-03-01 10:25:50 +00:00
bool verified = verifier - > Verify ( buf - 1 , signatureOffset + 1 , buf + signatureOffset ) ;
2019-01-10 15:57:57 +00:00
const_cast < uint8_t * > ( buf ) [ - 1 ] = c ;
2019-01-02 19:19:10 +00:00
if ( ! verified )
2021-11-27 19:53:53 +00:00
LogPrint ( eLogWarning , " LeaseSet2: Verification failed " ) ;
2019-01-08 16:26:50 +00:00
return verified ;
2019-01-02 19:19:10 +00:00
}
size_t LeaseSet2 : : ReadStandardLS2TypeSpecificPart ( const uint8_t * buf , size_t len )
{
size_t offset = 0 ;
2018-12-26 20:27:32 +00:00
// properties
2020-03-01 10:25:50 +00:00
uint16_t propertiesLen = bufbe16toh ( buf + offset ) ; offset + = 2 ;
2018-12-26 20:27:32 +00:00
offset + = propertiesLen ; // skip for now. TODO: implement properties
2019-01-02 19:19:10 +00:00
if ( offset + 1 > = len ) return 0 ;
2018-12-26 20:27:32 +00:00
// key sections
2020-02-12 16:09:20 +00:00
CryptoKeyType preferredKeyType = m_EncryptionType ;
bool preferredKeyFound = false ;
2019-01-01 22:00:37 +00:00
int numKeySections = buf [ offset ] ; offset + + ;
for ( int i = 0 ; i < numKeySections ; i + + )
2018-12-26 20:27:32 +00:00
{
2019-01-09 17:47:47 +00:00
uint16_t keyType = bufbe16toh ( buf + offset ) ; offset + = 2 ; // encryption key type
2019-01-02 19:19:10 +00:00
if ( offset + 2 > = len ) return 0 ;
2020-03-01 10:25:50 +00:00
uint16_t encryptionKeyLen = bufbe16toh ( buf + offset ) ; offset + = 2 ;
2019-01-09 17:47:47 +00:00
if ( offset + encryptionKeyLen > = len ) return 0 ;
2020-02-12 16:09:20 +00:00
if ( IsStoreLeases ( ) & & ! preferredKeyFound ) // create encryptor with leases only
2019-01-09 17:47:47 +00:00
{
2020-02-12 16:09:20 +00:00
// we pick first valid key if preferred not found
2019-01-09 17:47:47 +00:00
auto encryptor = i2p : : data : : IdentityEx : : CreateEncryptor ( keyType , buf + offset ) ;
2020-02-12 16:09:20 +00:00
if ( encryptor & & ( ! m_Encryptor | | keyType = = preferredKeyType ) )
2019-01-30 19:10:40 +00:00
{
m_Encryptor = encryptor ; // TODO: atomic
2019-12-19 20:59:15 +00:00
m_EncryptionType = keyType ;
2020-02-12 16:09:20 +00:00
if ( keyType = = preferredKeyType ) preferredKeyFound = true ;
2019-01-30 19:10:40 +00:00
}
2019-01-09 17:47:47 +00:00
}
2020-03-01 10:25:50 +00:00
offset + = encryptionKeyLen ;
}
2018-12-26 20:27:32 +00:00
// leases
2020-03-01 10:25:50 +00:00
if ( offset + 1 > = len ) return 0 ;
2018-12-26 20:27:32 +00:00
int numLeases = buf [ offset ] ; offset + + ;
2019-01-02 20:40:48 +00:00
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2019-01-09 17:47:47 +00:00
if ( IsStoreLeases ( ) )
2019-01-02 19:19:10 +00:00
{
2019-01-09 17:47:47 +00:00
UpdateLeasesBegin ( ) ;
for ( int i = 0 ; i < numLeases ; i + + )
{
2019-01-10 16:52:34 +00:00
if ( offset + LEASE2_SIZE > len ) return 0 ;
2019-01-09 17:47:47 +00:00
Lease lease ;
lease . tunnelGateway = buf + offset ; offset + = 32 ; // gateway
lease . tunnelID = bufbe32toh ( buf + offset ) ; offset + = 4 ; // tunnel ID
lease . endDate = bufbe32toh ( buf + offset ) * 1000LL ; offset + = 4 ; // end date
UpdateLease ( lease , ts ) ;
}
UpdateLeasesEnd ( ) ;
2019-01-02 19:19:10 +00:00
}
2019-01-09 17:47:47 +00:00
else
2019-01-10 16:52:34 +00:00
offset + = numLeases * LEASE2_SIZE ; // 40 bytes per lease
2019-01-02 19:19:10 +00:00
return offset ;
}
size_t LeaseSet2 : : ReadMetaLS2TypeSpecificPart ( const uint8_t * buf , size_t len )
{
size_t offset = 0 ;
// properties
2020-03-01 10:25:50 +00:00
uint16_t propertiesLen = bufbe16toh ( buf + offset ) ; offset + = 2 ;
2019-01-02 19:19:10 +00:00
offset + = propertiesLen ; // skip for now. TODO: implement properties
2020-03-01 10:25:50 +00:00
// entries
2019-01-02 19:19:10 +00:00
if ( offset + 1 > = len ) return 0 ;
int numEntries = buf [ offset ] ; offset + + ;
for ( int i = 0 ; i < numEntries ; i + + )
{
if ( offset + 40 > = len ) return 0 ;
offset + = 32 ; // hash
offset + = 3 ; // flags
2020-03-01 10:25:50 +00:00
offset + = 1 ; // cost
2019-01-02 19:19:10 +00:00
offset + = 4 ; // expires
}
// revocations
if ( offset + 1 > = len ) return 0 ;
2020-03-01 10:25:50 +00:00
int numRevocations = buf [ offset ] ; offset + + ;
2019-01-02 19:19:10 +00:00
for ( int i = 0 ; i < numRevocations ; i + + )
{
if ( offset + 32 > len ) return 0 ;
offset + = 32 ; // hash
}
return offset ;
2018-12-21 20:00:03 +00:00
}
2019-01-08 16:26:50 +00:00
2019-06-06 16:33:33 +00:00
void LeaseSet2 : : ReadFromBufferEncrypted ( const uint8_t * buf , size_t len , std : : shared_ptr < const BlindedPublicKey > key , const uint8_t * secret )
2019-01-08 16:26:50 +00:00
{
size_t offset = 0 ;
// blinded key
2019-01-14 18:49:27 +00:00
if ( len < 2 ) return ;
2019-03-05 17:41:01 +00:00
const uint8_t * stA1 = buf + offset ; // stA1 = blinded signature type, 2 bytes big endian
uint16_t blindedKeyType = bufbe16toh ( stA1 ) ; offset + = 2 ;
2019-01-08 16:26:50 +00:00
std : : unique_ptr < i2p : : crypto : : Verifier > blindedVerifier ( i2p : : data : : IdentityEx : : CreateVerifier ( blindedKeyType ) ) ;
if ( ! blindedVerifier ) return ;
2020-03-01 10:25:50 +00:00
auto blindedKeyLen = blindedVerifier - > GetPublicKeyLen ( ) ;
2019-01-08 16:26:50 +00:00
if ( offset + blindedKeyLen > = len ) return ;
2019-02-27 20:52:47 +00:00
const uint8_t * blindedPublicKey = buf + offset ;
blindedVerifier - > SetPublicKey ( blindedPublicKey ) ; offset + = blindedKeyLen ;
2019-01-08 16:26:50 +00:00
// expiration
if ( offset + 8 > = len ) return ;
2019-02-27 20:52:47 +00:00
const uint8_t * publishedTimestamp = buf + offset ;
m_PublishedTimestamp = bufbe32toh ( publishedTimestamp ) ; offset + = 4 ; // published timestamp (seconds)
2019-01-08 16:26:50 +00:00
uint16_t expires = bufbe16toh ( buf + offset ) ; offset + = 2 ; // expires (seconds)
2019-02-26 21:20:24 +00:00
SetExpirationTime ( ( m_PublishedTimestamp + expires ) * 1000LL ) ; // in milliseconds
2019-01-08 16:26:50 +00:00
uint16_t flags = bufbe16toh ( buf + offset ) ; offset + = 2 ; // flags
2019-02-12 19:56:39 +00:00
if ( flags & LEASESET2_FLAG_OFFLINE_KEYS )
2019-01-08 16:26:50 +00:00
{
2019-01-15 23:56:26 +00:00
// transient key
2019-02-06 18:36:03 +00:00
m_TransientVerifier = ProcessOfflineSignature ( blindedVerifier , buf , len , offset ) ;
2020-03-01 10:25:50 +00:00
if ( ! m_TransientVerifier )
2019-01-15 23:56:26 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Offline signature failed " ) ;
2019-01-15 23:56:26 +00:00
return ;
2019-02-06 18:36:03 +00:00
}
2019-01-08 16:26:50 +00:00
}
// outer ciphertext
if ( offset + 2 > len ) return ;
2019-02-27 20:52:47 +00:00
uint16_t lenOuterCiphertext = bufbe16toh ( buf + offset ) ; offset + = 2 ;
2020-03-01 10:25:50 +00:00
const uint8_t * outerCiphertext = buf + offset ;
offset + = lenOuterCiphertext ;
2019-01-08 16:26:50 +00:00
// verify signature
2019-02-06 18:36:03 +00:00
bool verified = m_TransientVerifier ? VerifySignature ( m_TransientVerifier , buf , len , offset ) :
2020-03-01 10:25:50 +00:00
VerifySignature ( blindedVerifier , buf , len , offset ) ;
2019-02-27 20:52:47 +00:00
SetIsValid ( verified ) ;
// handle ciphertext
2019-03-22 19:32:13 +00:00
if ( verified & & key & & lenOuterCiphertext > = 32 )
2019-02-27 20:52:47 +00:00
{
2020-03-01 10:25:50 +00:00
SetIsValid ( false ) ; // we must verify it again in Layer 2
2019-06-03 16:51:57 +00:00
if ( blindedKeyType = = key - > GetBlindedSigType ( ) )
2019-03-07 16:55:47 +00:00
{
// verify blinding
char date [ 9 ] ;
2019-04-12 18:05:07 +00:00
i2p : : util : : GetDateString ( m_PublishedTimestamp , date ) ;
2019-05-30 17:57:43 +00:00
std : : vector < uint8_t > blinded ( blindedKeyLen ) ;
key - > GetBlindedKey ( date , blinded . data ( ) ) ;
if ( memcmp ( blindedPublicKey , blinded . data ( ) , blindedKeyLen ) )
2019-03-07 16:55:47 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Blinded public key doesn't match " ) ;
2019-03-07 16:55:47 +00:00
return ;
2020-03-01 10:25:50 +00:00
}
}
2019-06-03 16:51:57 +00:00
else
{
2019-06-07 15:59:48 +00:00
LogPrint ( eLogError , " LeaseSet2: Unexpected blinded key type " , blindedKeyType , " instead " , key - > GetBlindedSigType ( ) ) ;
2019-06-03 16:51:57 +00:00
return ;
2020-03-01 10:25:50 +00:00
}
2019-03-05 17:41:01 +00:00
// outer key
2019-02-27 20:52:47 +00:00
// outerInput = subcredential || publishedTimestamp
2019-04-02 20:32:18 +00:00
uint8_t subcredential [ 36 ] ;
key - > GetSubcredential ( blindedPublicKey , blindedKeyLen , subcredential ) ;
2019-02-27 20:52:47 +00:00
memcpy ( subcredential + 32 , publishedTimestamp , 4 ) ;
2019-02-28 18:31:51 +00:00
// outerSalt = outerCiphertext[0:32]
2019-02-27 20:52:47 +00:00
// keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
2019-03-05 17:41:01 +00:00
uint8_t keys [ 64 ] ; // 44 bytes actual data
2019-03-15 16:25:20 +00:00
i2p : : crypto : : HKDF ( outerCiphertext , subcredential , 36 , " ELS2_L1K " , keys ) ;
2019-02-28 18:31:51 +00:00
// decrypt Layer 1
// outerKey = keys[0:31]
// outerIV = keys[32:43]
2019-03-04 20:47:35 +00:00
size_t lenOuterPlaintext = lenOuterCiphertext - 32 ;
std : : vector < uint8_t > outerPlainText ( lenOuterPlaintext ) ;
i2p : : crypto : : ChaCha20 ( outerCiphertext + 32 , lenOuterPlaintext , keys , keys + 32 , outerPlainText . data ( ) ) ;
// inner key
2019-06-05 19:57:20 +00:00
// innerInput = authCookie || subcredential || publishedTimestamp
2019-03-04 20:47:35 +00:00
// innerSalt = innerCiphertext[0:32]
// keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44)
2019-06-05 19:57:20 +00:00
uint8_t innerInput [ 68 ] ;
2020-03-01 10:25:50 +00:00
size_t authDataLen = ExtractClientAuthData ( outerPlainText . data ( ) , lenOuterPlaintext , secret , subcredential , innerInput ) ;
2019-06-05 19:57:20 +00:00
if ( authDataLen > 0 )
{
2020-03-01 10:25:50 +00:00
memcpy ( innerInput + 32 , subcredential , 36 ) ;
2019-06-07 15:59:48 +00:00
i2p : : crypto : : HKDF ( outerPlainText . data ( ) + 1 + authDataLen , innerInput , 68 , " ELS2_L2K " , keys ) ;
2019-06-05 19:57:20 +00:00
}
else
// no authData presented, innerInput = subcredential || publishedTimestamp
// skip 1 byte flags
i2p : : crypto : : HKDF ( outerPlainText . data ( ) + 1 , subcredential , 36 , " ELS2_L2K " , keys ) ; // no authCookie
2019-03-04 20:47:35 +00:00
// decrypt Layer 2
// innerKey = keys[0:31]
// innerIV = keys[32:43]
2019-06-05 19:57:20 +00:00
size_t lenInnerPlaintext = lenOuterPlaintext - 32 - 1 - authDataLen ;
2019-03-04 20:47:35 +00:00
std : : vector < uint8_t > innerPlainText ( lenInnerPlaintext ) ;
2019-06-05 19:57:20 +00:00
i2p : : crypto : : ChaCha20 ( outerPlainText . data ( ) + 32 + 1 + authDataLen , lenInnerPlaintext , keys , keys + 32 , innerPlainText . data ( ) ) ;
2019-03-04 20:47:35 +00:00
if ( innerPlainText [ 0 ] = = NETDB_STORE_TYPE_STANDARD_LEASESET2 | | innerPlainText [ 0 ] = = NETDB_STORE_TYPE_META_LEASESET2 )
{
// override store type and buffer
2020-03-01 10:25:50 +00:00
m_StoreType = innerPlainText [ 0 ] ;
2019-03-04 20:47:35 +00:00
SetBuffer ( innerPlainText . data ( ) + 1 , lenInnerPlaintext - 1 ) ;
// parse and verify Layer 2
ReadFromBuffer ( innerPlainText . data ( ) + 1 , lenInnerPlaintext - 1 ) ;
}
else
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Unexpected LeaseSet type " , ( int ) innerPlainText [ 0 ] , " inside encrypted LeaseSet " ) ;
2020-03-01 10:25:50 +00:00
}
2020-03-20 21:43:37 +00:00
else
{
// we set actual length of encrypted buffer
offset + = m_TransientVerifier ? m_TransientVerifier - > GetSignatureLen ( ) : blindedVerifier - > GetSignatureLen ( ) ;
SetBufferLen ( offset ) ;
2020-03-01 10:25:50 +00:00
}
2019-02-27 20:52:47 +00:00
}
2019-06-11 14:40:53 +00:00
// helper for ExtractClientAuthData
static inline bool GetAuthCookie ( const uint8_t * authClients , int numClients , const uint8_t * okm , uint8_t * authCookie )
{
// try to find clientCookie_i for clientID_i = okm[44:51]
for ( int i = 0 ; i < numClients ; i + + )
{
if ( ! memcmp ( okm + 44 , authClients + i * 40 , 8 ) ) // clientID_i
{
// clientKey_i = okm[0:31]
// clientIV_i = okm[32:43]
2020-03-01 10:25:50 +00:00
i2p : : crypto : : ChaCha20 ( authClients + i * 40 + 8 , 32 , okm , okm + 32 , authCookie ) ; // clientCookie_i
2019-06-11 14:40:53 +00:00
return true ;
2020-03-01 10:25:50 +00:00
}
2019-06-11 14:40:53 +00:00
}
2020-03-01 10:25:50 +00:00
return false ;
}
2019-06-11 14:40:53 +00:00
2019-06-05 19:57:20 +00:00
size_t LeaseSet2 : : ExtractClientAuthData ( const uint8_t * buf , size_t len , const uint8_t * secret , const uint8_t * subcredential , uint8_t * authCookie ) const
{
size_t offset = 0 ;
uint8_t flag = buf [ offset ] ; offset + + ; // flag
if ( flag & 0x01 ) // client auth
{
2019-06-11 14:40:53 +00:00
if ( ! ( flag & 0x0E ) ) // DH, bit 1-3 all zeroes
{
const uint8_t * ephemeralPublicKey = buf + offset ; offset + = 32 ; // ephemeralPublicKey
uint16_t numClients = bufbe16toh ( buf + offset ) ; offset + = 2 ; // clients
const uint8_t * authClients = buf + offset ; offset + = numClients * 40 ; // authClients
2020-03-01 10:25:50 +00:00
if ( offset > len )
2019-06-11 14:40:53 +00:00
{
LogPrint ( eLogError , " LeaseSet2: Too many clients " , numClients , " in DH auth data " ) ;
return 0 ;
}
// calculate authCookie
if ( secret )
{
i2p : : crypto : : X25519Keys ck ( secret , nullptr ) ; // derive cpk_i from csk_i
uint8_t authInput [ 100 ] ;
ck . Agree ( ephemeralPublicKey , authInput ) ; // sharedSecret is first 32 bytes of authInput
memcpy ( authInput + 32 , ck . GetPublicKey ( ) , 32 ) ; // cpk_i
2019-06-11 19:09:10 +00:00
memcpy ( authInput + 64 , subcredential , 36 ) ;
2019-06-11 14:40:53 +00:00
uint8_t okm [ 64 ] ; // 52 actual data
2020-03-01 10:25:50 +00:00
i2p : : crypto : : HKDF ( ephemeralPublicKey , authInput , 100 , " ELS2_XCA " , okm ) ;
2019-06-11 14:40:53 +00:00
if ( ! GetAuthCookie ( authClients , numClients , okm , authCookie ) )
2020-03-01 10:25:50 +00:00
LogPrint ( eLogError , " LeaseSet2: Client cookie DH not found " ) ;
2019-06-11 14:40:53 +00:00
}
else
2020-03-01 10:25:50 +00:00
LogPrint ( eLogError , " LeaseSet2: Can't calculate authCookie: csk_i is not provided " ) ;
2019-06-11 14:40:53 +00:00
}
else if ( flag & 0x02 ) // PSK, bit 1 is set to 1
2019-06-05 19:57:20 +00:00
{
2019-06-06 17:58:31 +00:00
const uint8_t * authSalt = buf + offset ; offset + = 32 ; // authSalt
2019-06-05 19:57:20 +00:00
uint16_t numClients = bufbe16toh ( buf + offset ) ; offset + = 2 ; // clients
2019-06-06 16:33:33 +00:00
const uint8_t * authClients = buf + offset ; offset + = numClients * 40 ; // authClients
2020-03-01 10:25:50 +00:00
if ( offset > len )
2019-06-07 15:59:48 +00:00
{
2019-06-11 14:40:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Too many clients " , numClients , " in PSK auth data " ) ;
2019-06-07 15:59:48 +00:00
return 0 ;
}
2019-06-05 19:57:20 +00:00
// calculate authCookie
if ( secret )
{
uint8_t authInput [ 68 ] ;
memcpy ( authInput , secret , 32 ) ;
2019-06-07 15:59:48 +00:00
memcpy ( authInput + 32 , subcredential , 36 ) ;
2019-06-05 19:57:20 +00:00
uint8_t okm [ 64 ] ; // 52 actual data
2020-03-01 10:25:50 +00:00
i2p : : crypto : : HKDF ( authSalt , authInput , 68 , " ELS2PSKA " , okm ) ;
2019-06-11 14:40:53 +00:00
if ( ! GetAuthCookie ( authClients , numClients , okm , authCookie ) )
LogPrint ( eLogError , " LeaseSet2: Client cookie PSK not found " ) ;
2019-06-05 19:57:20 +00:00
}
else
LogPrint ( eLogError , " LeaseSet2: Can't calculate authCookie: psk_i is not provided " ) ;
}
else
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Unknown client auth type " , ( int ) flag ) ;
2019-06-05 19:57:20 +00:00
}
return offset - 1 ;
}
2021-08-31 22:51:40 +00:00
void LeaseSet2 : : Encrypt ( const uint8_t * data , uint8_t * encrypted ) const
2019-01-09 17:47:47 +00:00
{
auto encryptor = m_Encryptor ; // TODO: atomic
if ( encryptor )
2021-09-03 17:30:01 +00:00
encryptor - > Encrypt ( data , encrypted ) ;
2019-01-09 17:47:47 +00:00
}
2017-11-07 20:05:22 +00:00
2020-04-29 21:11:48 +00:00
uint64_t LeaseSet2 : : ExtractExpirationTimestamp ( const uint8_t * buf , size_t len ) const
{
uint64_t expiration = 0 ;
ExtractPublishedTimestamp ( buf , len , expiration ) ;
return expiration ;
}
uint64_t LeaseSet2 : : ExtractPublishedTimestamp ( const uint8_t * buf , size_t len , uint64_t & expiration ) const
2019-01-14 18:49:27 +00:00
{
2020-03-01 10:25:50 +00:00
if ( len < 8 ) return 0 ;
2019-01-14 18:49:27 +00:00
if ( m_StoreType = = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 )
{
// encrypted LS2
size_t offset = 0 ;
uint16_t blindedKeyType = bufbe16toh ( buf + offset ) ; offset + = 2 ;
std : : unique_ptr < i2p : : crypto : : Verifier > blindedVerifier ( i2p : : data : : IdentityEx : : CreateVerifier ( blindedKeyType ) ) ;
if ( ! blindedVerifier ) return 0 ;
2020-03-01 10:25:50 +00:00
auto blindedKeyLen = blindedVerifier - > GetPublicKeyLen ( ) ;
2019-01-14 18:49:27 +00:00
if ( offset + blindedKeyLen + 6 > = len ) return 0 ;
offset + = blindedKeyLen ;
2020-03-01 10:25:50 +00:00
uint32_t timestamp = bufbe32toh ( buf + offset ) ; offset + = 4 ;
uint16_t expires = bufbe16toh ( buf + offset ) ; offset + = 2 ;
2020-04-29 21:11:48 +00:00
expiration = ( timestamp + expires ) * 1000LL ;
return timestamp ;
2019-01-14 18:49:27 +00:00
}
else
{
auto identity = GetIdentity ( ) ;
if ( ! identity ) return 0 ;
size_t offset = identity - > GetFullLen ( ) ;
if ( offset + 6 > = len ) return 0 ;
2020-03-01 10:25:50 +00:00
uint32_t timestamp = bufbe32toh ( buf + offset ) ; offset + = 4 ;
uint16_t expires = bufbe16toh ( buf + offset ) ; offset + = 2 ;
2020-04-29 21:11:48 +00:00
expiration = ( timestamp + expires ) * 1000LL ;
return timestamp ;
2019-01-14 18:49:27 +00:00
}
2020-03-01 10:25:50 +00:00
}
2016-05-25 18:17:34 +00:00
LocalLeaseSet : : LocalLeaseSet ( std : : shared_ptr < const IdentityEx > identity , const uint8_t * encryptionPublicKey , std : : vector < std : : shared_ptr < i2p : : tunnel : : InboundTunnel > > tunnels ) :
2016-05-25 19:10:28 +00:00
m_ExpirationTime ( 0 ) , m_Identity ( identity )
2016-05-25 18:17:34 +00:00
{
int num = tunnels . size ( ) ;
if ( num > MAX_NUM_LEASES ) num = MAX_NUM_LEASES ;
// identity
2016-05-25 21:41:24 +00:00
auto signingKeyLen = m_Identity - > GetSigningPublicKeyLen ( ) ;
2018-01-06 03:48:51 +00:00
m_BufferLen = m_Identity - > GetFullLen ( ) + 256 + signingKeyLen + 1 + num * LEASE_SIZE + m_Identity - > GetSignatureLen ( ) ;
m_Buffer = new uint8_t [ m_BufferLen ] ;
2016-05-25 18:17:34 +00:00
auto offset = m_Identity - > ToBuffer ( m_Buffer , m_BufferLen ) ;
memcpy ( m_Buffer + offset , encryptionPublicKey , 256 ) ;
offset + = 256 ;
memset ( m_Buffer + offset , 0 , signingKeyLen ) ;
offset + = signingKeyLen ;
// num leases
2018-01-06 03:48:51 +00:00
m_Buffer [ offset ] = num ;
2016-05-25 18:17:34 +00:00
offset + + ;
// leases
2016-05-29 20:35:57 +00:00
m_Leases = m_Buffer + offset ;
2016-05-25 18:17:34 +00:00
auto currentTime = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
for ( int i = 0 ; i < num ; i + + )
{
memcpy ( m_Buffer + offset , tunnels [ i ] - > GetNextIdentHash ( ) , 32 ) ;
offset + = 32 ; // gateway id
htobe32buf ( m_Buffer + offset , tunnels [ i ] - > GetNextTunnelID ( ) ) ;
offset + = 4 ; // tunnel id
uint64_t ts = tunnels [ i ] - > GetCreationTime ( ) + i2p : : tunnel : : TUNNEL_EXPIRATION_TIMEOUT - i2p : : tunnel : : TUNNEL_EXPIRATION_THRESHOLD ; // 1 minute before expiration
ts * = 1000 ; // in milliseconds
2016-05-25 19:10:28 +00:00
if ( ts > m_ExpirationTime ) m_ExpirationTime = ts ;
2016-05-25 18:17:34 +00:00
// make sure leaseset is newer than previous, but adding some time to expiration date
ts + = ( currentTime - tunnels [ i ] - > GetCreationTime ( ) * 1000LL ) * 2 / i2p : : tunnel : : TUNNEL_EXPIRATION_TIMEOUT ; // up to 2 secs
htobe64buf ( m_Buffer + offset , ts ) ;
offset + = 8 ; // end date
}
// we don't sign it yet. must be signed later on
2016-05-25 19:10:28 +00:00
}
2016-05-25 18:17:34 +00:00
2016-05-30 16:56:42 +00:00
LocalLeaseSet : : LocalLeaseSet ( std : : shared_ptr < const IdentityEx > identity , const uint8_t * buf , size_t len ) :
m_ExpirationTime ( 0 ) , m_Identity ( identity )
{
2019-01-09 19:51:47 +00:00
if ( buf )
{
m_BufferLen = len ;
m_Buffer = new uint8_t [ m_BufferLen ] ;
memcpy ( m_Buffer , buf , len ) ;
}
else
{
m_Buffer = nullptr ;
m_BufferLen = 0 ;
}
2016-05-30 16:56:42 +00:00
}
2016-05-25 19:10:28 +00:00
bool LocalLeaseSet : : IsExpired ( ) const
2016-05-25 18:17:34 +00:00
{
2016-05-25 19:10:28 +00:00
auto ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
return ts > m_ExpirationTime ;
2018-01-06 03:48:51 +00:00
}
2018-01-24 15:16:51 +00:00
bool LeaseSetBufferValidate ( const uint8_t * ptr , size_t sz , uint64_t & expires )
{
IdentityEx ident ( ptr , sz ) ;
size_t size = ident . GetFullLen ( ) ;
if ( size > sz )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet: Identity length " , size , " exceeds buffer size " , sz ) ;
2018-01-24 15:16:51 +00:00
return false ;
}
// encryption key
size + = 256 ;
// signing key (unused)
size + = ident . GetSigningPublicKeyLen ( ) ;
uint8_t numLeases = ptr [ size ] ;
+ + size ;
if ( ! numLeases | | numLeases > MAX_NUM_LEASES )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet: Incorrect number of leases " , ( int ) numLeases ) ;
2018-01-24 15:16:51 +00:00
return false ;
}
const uint8_t * leases = ptr + size ;
expires = 0 ;
/** find lease with the max expiration timestamp */
for ( int i = 0 ; i < numLeases ; i + + )
{
leases + = 36 ; // gateway + tunnel ID
uint64_t endDate = bufbe64toh ( leases ) ;
leases + = 8 ; // end date
if ( endDate > expires )
expires = endDate ;
}
return ident . Verify ( ptr , leases - ptr , leases ) ;
}
2019-01-09 19:51:47 +00:00
2020-03-01 10:25:50 +00:00
LocalLeaseSet2 : : LocalLeaseSet2 ( uint8_t storeType , const i2p : : data : : PrivateKeys & keys ,
2021-04-01 17:37:21 +00:00
const KeySections & encryptionKeys , const std : : vector < std : : shared_ptr < i2p : : tunnel : : InboundTunnel > > & tunnels ,
2019-08-07 19:43:03 +00:00
bool isPublic , bool isPublishedEncrypted ) :
2019-02-12 19:56:39 +00:00
LocalLeaseSet ( keys . GetPublic ( ) , nullptr , 0 )
2019-01-09 19:51:47 +00:00
{
2019-02-12 19:56:39 +00:00
auto identity = keys . GetPublic ( ) ;
2020-03-01 10:25:50 +00:00
// assume standard LS2
2019-01-10 16:52:34 +00:00
int num = tunnels . size ( ) ;
if ( num > MAX_NUM_LEASES ) num = MAX_NUM_LEASES ;
2020-03-18 22:03:03 +00:00
size_t keySectionsLen = 0 ;
for ( const auto & it : encryptionKeys )
2020-03-01 10:25:50 +00:00
keySectionsLen + = 2 /*key type*/ + 2 /*key len*/ + it . keyLen /*key*/ ;
2019-01-10 16:52:34 +00:00
m_BufferLen = identity - > GetFullLen ( ) + 4 /*published*/ + 2 /*expires*/ + 2 /*flag*/ + 2 /*properties len*/ +
2020-03-18 22:03:03 +00:00
1 /*num keys*/ + keySectionsLen + 1 /*num leases*/ + num * LEASE2_SIZE + keys . GetSignatureLen ( ) ;
2019-02-12 19:56:39 +00:00
uint16_t flags = 0 ;
2020-03-01 10:25:50 +00:00
if ( keys . IsOfflineSignature ( ) )
2019-02-12 19:56:39 +00:00
{
flags | = LEASESET2_FLAG_OFFLINE_KEYS ;
2020-03-01 10:25:50 +00:00
m_BufferLen + = keys . GetOfflineSignature ( ) . size ( ) ;
2019-02-12 19:56:39 +00:00
}
2020-03-01 10:25:50 +00:00
if ( isPublishedEncrypted )
2019-08-07 19:43:03 +00:00
{
flags | = LEASESET2_FLAG_PUBLISHED_ENCRYPTED ;
2020-03-01 10:25:50 +00:00
isPublic = true ;
2019-08-07 19:43:03 +00:00
}
2019-07-16 15:48:30 +00:00
if ( ! isPublic ) flags | = LEASESET2_FLAG_UNPUBLISHED_LEASESET ;
2019-02-12 19:56:39 +00:00
2019-01-10 16:52:34 +00:00
m_Buffer = new uint8_t [ m_BufferLen + 1 ] ;
2020-03-01 10:25:50 +00:00
m_Buffer [ 0 ] = storeType ;
2019-01-10 16:52:34 +00:00
// LS2 header
auto offset = identity - > ToBuffer ( m_Buffer + 1 , m_BufferLen ) + 1 ;
auto timestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
htobe32buf ( m_Buffer + offset , timestamp ) ; offset + = 4 ; // published timestamp (seconds)
uint8_t * expiresBuf = m_Buffer + offset ; offset + = 2 ; // expires, fill later
2019-02-15 02:22:49 +00:00
htobe16buf ( m_Buffer + offset , flags ) ; offset + = 2 ; // flags
2019-02-12 19:56:39 +00:00
if ( keys . IsOfflineSignature ( ) )
{
// offline signature
const auto & offlineSignature = keys . GetOfflineSignature ( ) ;
memcpy ( m_Buffer + offset , offlineSignature . data ( ) , offlineSignature . size ( ) ) ;
offset + = offlineSignature . size ( ) ;
}
2019-01-10 16:52:34 +00:00
htobe16buf ( m_Buffer + offset , 0 ) ; offset + = 2 ; // properties len
2020-03-01 10:25:50 +00:00
// keys
2020-03-18 22:03:03 +00:00
m_Buffer [ offset ] = encryptionKeys . size ( ) ; offset + + ; // 1 key
for ( const auto & it : encryptionKeys )
2020-03-01 10:25:50 +00:00
{
htobe16buf ( m_Buffer + offset , it . keyType ) ; offset + = 2 ; // key type
htobe16buf ( m_Buffer + offset , it . keyLen ) ; offset + = 2 ; // key len
2020-03-18 22:03:03 +00:00
memcpy ( m_Buffer + offset , it . encryptionPublicKey , it . keyLen ) ; offset + = it . keyLen ; // key
2020-03-01 10:25:50 +00:00
}
2019-01-10 16:52:34 +00:00
// leases
uint32_t expirationTime = 0 ; // in seconds
m_Buffer [ offset ] = num ; offset + + ; // num leases
for ( int i = 0 ; i < num ; i + + )
{
memcpy ( m_Buffer + offset , tunnels [ i ] - > GetNextIdentHash ( ) , 32 ) ;
offset + = 32 ; // gateway id
htobe32buf ( m_Buffer + offset , tunnels [ i ] - > GetNextTunnelID ( ) ) ;
offset + = 4 ; // tunnel id
auto ts = tunnels [ i ] - > GetCreationTime ( ) + i2p : : tunnel : : TUNNEL_EXPIRATION_TIMEOUT - i2p : : tunnel : : TUNNEL_EXPIRATION_THRESHOLD ; // in seconds, 1 minute before expiration
if ( ts > expirationTime ) expirationTime = ts ;
htobe32buf ( m_Buffer + offset , ts ) ;
offset + = 4 ; // end date
2020-03-01 10:25:50 +00:00
}
2019-01-10 16:52:34 +00:00
// update expiration
2021-04-01 14:29:03 +00:00
if ( expirationTime )
2021-11-27 20:30:35 +00:00
{
2021-04-01 14:29:03 +00:00
SetExpirationTime ( expirationTime * 1000LL ) ;
auto expires = ( int ) expirationTime - timestamp ;
htobe16buf ( expiresBuf , expires > 0 ? expires : 0 ) ;
2021-11-27 20:30:35 +00:00
}
2021-04-01 14:29:03 +00:00
else
{
// no tunnels or withdraw
SetExpirationTime ( timestamp * 1000LL ) ;
memset ( expiresBuf , 0 , 2 ) ; // expires immeditely
2021-11-27 20:30:35 +00:00
}
2019-02-12 19:56:39 +00:00
// sign
keys . Sign ( m_Buffer , offset , m_Buffer + offset ) ; // LS + leading store type
2019-01-09 19:51:47 +00:00
}
2019-01-29 16:30:31 +00:00
LocalLeaseSet2 : : LocalLeaseSet2 ( uint8_t storeType , std : : shared_ptr < const IdentityEx > identity , const uint8_t * buf , size_t len ) :
LocalLeaseSet ( identity , nullptr , 0 )
{
m_BufferLen = len ;
m_Buffer = new uint8_t [ m_BufferLen + 1 ] ;
memcpy ( m_Buffer + 1 , buf , len ) ;
m_Buffer [ 0 ] = storeType ;
}
2019-04-05 20:03:58 +00:00
2020-03-01 10:25:50 +00:00
LocalEncryptedLeaseSet2 : : LocalEncryptedLeaseSet2 ( std : : shared_ptr < const LocalLeaseSet2 > ls , const i2p : : data : : PrivateKeys & keys ,
2019-07-16 20:31:17 +00:00
int authType , std : : shared_ptr < std : : vector < AuthPublicKey > > authKeys ) :
2019-04-10 16:04:19 +00:00
LocalLeaseSet2 ( ls - > GetIdentity ( ) ) , m_InnerLeaseSet ( ls )
2019-04-05 20:03:58 +00:00
{
2019-08-26 11:35:11 +00:00
size_t lenInnerPlaintext = ls - > GetBufferLen ( ) + 1 , lenOuterPlaintext = lenInnerPlaintext + 32 + 1 ;
2019-07-12 19:37:32 +00:00
uint8_t layer1Flags = 0 ;
if ( authKeys )
{
2019-07-16 20:31:17 +00:00
if ( authType = = ENCRYPTED_LEASESET_AUTH_TYPE_DH ) layer1Flags | = 0x01 ; // DH, authentication scheme 0, auth bit 1
else if ( authType = = ENCRYPTED_LEASESET_AUTH_TYPE_PSK ) layer1Flags | = 0x03 ; // PSK, authentication scheme 1, auth bit 1
2020-03-01 10:25:50 +00:00
if ( layer1Flags )
2019-08-26 11:35:11 +00:00
lenOuterPlaintext + = 32 + 2 + authKeys - > size ( ) * 40 ; // auth data len
2020-03-01 10:25:50 +00:00
}
2019-08-26 11:35:11 +00:00
size_t lenOuterCiphertext = lenOuterPlaintext + 32 ;
2020-03-01 10:25:50 +00:00
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 ] ;
2019-04-05 20:03:58 +00:00
m_Buffer [ 0 ] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 ;
BlindedPublicKey blindedKey ( ls - > GetIdentity ( ) ) ;
2020-03-01 10:25:50 +00:00
auto timestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2019-04-05 20:03:58 +00:00
char date [ 9 ] ;
2019-04-12 18:05:07 +00:00
i2p : : util : : GetDateString ( timestamp , date ) ;
2019-05-22 20:15:11 +00:00
uint8_t blindedPriv [ 64 ] , blindedPub [ 128 ] ; // 64 and 128 max
size_t publicKeyLen = blindedKey . BlindPrivateKey ( keys . GetSigningPrivateKey ( ) , date , blindedPriv , blindedPub ) ;
2019-07-12 19:37:32 +00:00
std : : unique_ptr < i2p : : crypto : : Signer > blindedSigner ( i2p : : data : : PrivateKeys : : CreateSigner ( blindedKey . GetBlindedSigType ( ) , blindedPriv ) ) ;
2021-09-29 16:38:38 +00:00
if ( ! blindedSigner )
{
LogPrint ( eLogError , " LeaseSet2: Can't create blinded signer for signature type " , blindedKey . GetSigType ( ) ) ;
return ;
2021-11-27 20:30:35 +00:00
}
2019-04-05 20:03:58 +00:00
auto offset = 1 ;
2019-07-12 19:37:32 +00:00
htobe16buf ( m_Buffer + offset , blindedKey . GetBlindedSigType ( ) ) ; offset + = 2 ; // Blinded Public Key Sig Type
2019-05-22 20:15:11 +00:00
memcpy ( m_Buffer + offset , blindedPub , publicKeyLen ) ; offset + = publicKeyLen ; // Blinded Public Key
2019-04-05 20:03:58 +00:00
htobe32buf ( m_Buffer + offset , timestamp ) ; offset + = 4 ; // published timestamp (seconds)
2019-04-12 15:13:46 +00:00
auto nextMidnight = ( timestamp / 86400LL + 1 ) * 86400LL ; // 86400 = 24*3600 seconds
2020-03-01 10:25:50 +00:00
auto expirationTime = ls - > GetExpirationTime ( ) / 1000LL ;
2019-04-12 15:13:46 +00:00
if ( expirationTime > nextMidnight ) expirationTime = nextMidnight ;
SetExpirationTime ( expirationTime * 1000LL ) ;
htobe16buf ( m_Buffer + offset , expirationTime > timestamp ? expirationTime - timestamp : 0 ) ; offset + = 2 ; // expires
2019-04-05 20:03:58 +00:00
uint16_t flags = 0 ;
2020-03-01 10:25:50 +00:00
htobe16buf ( m_Buffer + offset , flags ) ; offset + = 2 ; // flags
2019-04-08 17:27:21 +00:00
htobe16buf ( m_Buffer + offset , lenOuterCiphertext ) ; offset + = 2 ; // lenOuterCiphertext
// outerChipherText
2020-03-01 10:25:50 +00:00
// Layer 1
2019-04-08 17:27:21 +00:00
uint8_t subcredential [ 36 ] ;
blindedKey . GetSubcredential ( blindedPub , 32 , subcredential ) ;
htobe32buf ( subcredential + 32 , timestamp ) ; // outerInput = subcredential || publishedTimestamp
// keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
uint8_t keys1 [ 64 ] ; // 44 bytes actual data
2020-03-01 10:25:50 +00:00
RAND_bytes ( m_Buffer + offset , 32 ) ; // outerSalt = CSRNG(32)
2019-04-08 17:27:21 +00:00
i2p : : crypto : : HKDF ( m_Buffer + offset , subcredential , 36 , " ELS2_L1K " , keys1 ) ;
offset + = 32 ; // outerSalt
2020-03-01 10:25:50 +00:00
uint8_t * outerPlainText = m_Buffer + offset ;
m_Buffer [ offset ] = layer1Flags ; offset + + ; // layer 1 flags
2019-07-12 19:37:32 +00:00
// auth data
uint8_t innerInput [ 68 ] ; // authCookie || subcredential || publishedTimestamp
if ( layer1Flags )
{
RAND_bytes ( innerInput , 32 ) ; // authCookie
2019-08-25 18:54:43 +00:00
CreateClientAuthData ( subcredential , authType , authKeys , innerInput , m_Buffer + offset ) ;
2019-08-26 00:51:15 +00:00
offset + = 32 + 2 + authKeys - > size ( ) * 40 ; // auth clients
2020-03-01 10:25:50 +00:00
}
2019-04-08 17:27:21 +00:00
// Layer 2
// keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44)
uint8_t keys2 [ 64 ] ; // 44 bytes actual data
2020-03-01 10:25:50 +00:00
RAND_bytes ( m_Buffer + offset , 32 ) ; // innerSalt = CSRNG(32)
2019-07-12 19:37:32 +00:00
if ( layer1Flags )
{
memcpy ( innerInput + 32 , subcredential , 36 ) ; // + subcredential || publishedTimestamp
2020-03-01 10:25:50 +00:00
i2p : : crypto : : HKDF ( m_Buffer + offset , innerInput , 68 , " ELS2_L2K " , keys2 ) ;
2019-07-12 19:37:32 +00:00
}
else
i2p : : crypto : : HKDF ( m_Buffer + offset , subcredential , 36 , " ELS2_L2K " , keys2 ) ; // no authCookie
2020-03-01 10:25:50 +00:00
offset + = 32 ; // innerSalt
m_Buffer [ offset ] = ls - > GetStoreType ( ) ;
2019-04-09 14:34:05 +00:00
memcpy ( m_Buffer + offset + 1 , ls - > GetBuffer ( ) , ls - > GetBufferLen ( ) ) ;
2019-04-08 17:27:21 +00:00
i2p : : crypto : : ChaCha20 ( m_Buffer + offset , lenInnerPlaintext , keys2 , keys2 + 32 , m_Buffer + offset ) ; // encrypt Layer 2
offset + = lenInnerPlaintext ;
i2p : : crypto : : ChaCha20 ( outerPlainText , lenOuterPlaintext , keys1 , keys1 + 32 , outerPlainText ) ; // encrypt Layer 1
// signature
blindedSigner - > Sign ( m_Buffer , offset , m_Buffer + offset ) ;
2019-04-09 19:36:10 +00:00
// store hash
2020-03-01 10:25:50 +00:00
m_StoreHash = blindedKey . GetStoreHash ( date ) ;
2019-04-05 20:03:58 +00:00
}
2019-04-11 18:06:53 +00:00
LocalEncryptedLeaseSet2 : : LocalEncryptedLeaseSet2 ( std : : shared_ptr < const IdentityEx > identity , const uint8_t * buf , size_t len ) :
2020-03-01 10:25:50 +00:00
LocalLeaseSet2 ( NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 , identity , buf , len )
2019-04-11 18:06:53 +00:00
{
2020-03-01 10:25:50 +00:00
// fill inner LeaseSet2
auto blindedKey = std : : make_shared < BlindedPublicKey > ( identity ) ;
2019-04-11 18:06:53 +00:00
i2p : : data : : LeaseSet2 ls ( buf , len , blindedKey ) ; // inner layer
if ( ls . IsValid ( ) )
{
m_InnerLeaseSet = std : : make_shared < LocalLeaseSet2 > ( ls . GetStoreType ( ) , identity , ls . GetBuffer ( ) , ls . GetBufferLen ( ) ) ;
m_StoreHash = blindedKey - > GetStoreHash ( ) ;
}
else
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " LeaseSet2: Couldn't extract inner layer " ) ;
2019-04-11 18:06:53 +00:00
}
2019-07-12 19:37:32 +00:00
2020-03-01 10:25:50 +00:00
void LocalEncryptedLeaseSet2 : : CreateClientAuthData ( const uint8_t * subcredential , int authType , std : : shared_ptr < std : : vector < AuthPublicKey > > authKeys , const uint8_t * authCookie , uint8_t * authData ) const
2019-07-12 19:37:32 +00:00
{
2019-07-16 20:31:17 +00:00
if ( authType = = ENCRYPTED_LEASESET_AUTH_TYPE_DH )
2019-07-12 19:37:32 +00:00
{
i2p : : crypto : : X25519Keys ek ;
ek . GenerateKeys ( ) ; // esk and epk
2019-08-26 00:51:15 +00:00
memcpy ( authData , ek . GetPublicKey ( ) , 32 ) ; authData + = 32 ; // epk
htobe16buf ( authData , authKeys - > size ( ) ) ; authData + = 2 ; // num clients
2019-07-12 19:37:32 +00:00
uint8_t authInput [ 100 ] ; // sharedSecret || cpk_i || subcredential || publishedTimestamp
memcpy ( authInput + 64 , subcredential , 36 ) ;
for ( auto & it : * authKeys )
{
ek . Agree ( it , authInput ) ; // sharedSecret = DH(esk, cpk_i)
memcpy ( authInput + 32 , it , 32 ) ;
uint8_t okm [ 64 ] ; // 52 actual data
2020-03-01 10:25:50 +00:00
i2p : : crypto : : HKDF ( ek . GetPublicKey ( ) , authInput , 100 , " ELS2_XCA " , okm ) ;
2019-08-26 00:51:15 +00:00
memcpy ( authData , okm + 44 , 8 ) ; authData + = 8 ; // clientID_i
2020-03-01 10:25:50 +00:00
i2p : : crypto : : ChaCha20 ( authCookie , 32 , okm , okm + 32 , authData ) ; authData + = 32 ; // clientCookie_i
2019-07-12 19:37:32 +00:00
}
}
else // assume PSK
{
uint8_t authSalt [ 32 ] ;
RAND_bytes ( authSalt , 32 ) ;
2019-08-26 00:51:15 +00:00
memcpy ( authData , authSalt , 32 ) ; authData + = 32 ; // authSalt
htobe16buf ( authData , authKeys - > size ( ) ) ; authData + = 2 ; // num clients
2019-07-12 19:37:32 +00:00
uint8_t authInput [ 68 ] ; // authInput = psk_i || subcredential || publishedTimestamp
memcpy ( authInput + 32 , subcredential , 36 ) ;
for ( auto & it : * authKeys )
{
memcpy ( authInput , it , 32 ) ;
uint8_t okm [ 64 ] ; // 52 actual data
2020-03-01 10:25:50 +00:00
i2p : : crypto : : HKDF ( authSalt , authInput , 68 , " ELS2PSKA " , okm ) ;
2019-08-26 00:51:15 +00:00
memcpy ( authData , okm + 44 , 8 ) ; authData + = 8 ; // clientID_i
2020-03-01 10:25:50 +00:00
i2p : : crypto : : ChaCha20 ( authCookie , 32 , okm , okm + 32 , authData ) ; authData + = 32 ; // clientCookie_i
}
2019-07-12 19:37:32 +00:00
}
}
2018-01-06 03:48:51 +00:00
}
}