@ -2,393 +2,390 @@
# include <crypto/crypto.hpp>
# include <path/path.hpp>
# include <lokimq/bt_serialize.h>
# include <oxenmq/bt_serialize.h>
namespace lokimq = oxenmq ;
namespace llarp
namespace llarp : : service
{
namespace service
util : : StatusObject
EncryptedIntroSet : : ExtractStatus ( ) const
{
util : : StatusObject
EncryptedIntroSet : : ExtractStatus ( ) const
{
const auto sz = introsetPayload . size ( ) ;
return { { " location " , derivedSigningKey . ToString ( ) } ,
{ " signedAt " , to_json ( signedAt ) } ,
{ " size " , sz } } ;
}
const auto sz = introsetPayload . size ( ) ;
return {
{ " location " , derivedSigningKey . ToString ( ) } , { " signedAt " , to_json ( signedAt ) } , { " size " , sz } } ;
}
bool
EncryptedIntroSet : : BEncode ( llarp_buffer_t * buf ) const
{
if ( not bencode_start_dict ( buf ) )
return false ;
if ( not BEncodeWriteDictEntry ( " d " , derivedSigningKey , buf ) )
return false ;
if ( not BEncodeWriteDictEntry ( " n " , nounce , buf ) )
return false ;
if ( not BEncodeWriteDictInt ( " s " , signedAt . count ( ) , buf ) )
return false ;
if ( not bencode_write_bytestring ( buf , " x " , 1 ) )
return false ;
if ( not bencode_write_bytestring ( buf , introsetPayload . data ( ) , introsetPayload . size ( ) ) )
return false ;
if ( not BEncodeWriteDictEntry ( " z " , sig , buf ) )
return false ;
return bencode_end ( buf ) ;
}
bool
EncryptedIntroSet : : BEncode ( llarp_buffer_t * buf ) const
bool
EncryptedIntroSet : : DecodeKey ( const llarp_buffer_t & key , llarp_buffer_t * buf )
{
bool read = false ;
if ( key = = " x " )
{
if ( not bencode_start_dict ( buf ) )
llarp_buffer_t strbuf ;
if ( not bencode_read_string ( buf , & strbuf ) )
return false ;
if ( not BEncodeWriteDictEntry ( " d " , derivedSigningKey , buf ) )
if ( strbuf. sz > MAX_INTROSET_SIZE )
return false ;
if ( not BEncodeWriteDictEntry ( " n " , nounce , buf ) )
return false ;
if ( not BEncodeWriteDictInt ( " s " , signedAt . count ( ) , buf ) )
return false ;
if ( not bencode_write_bytestring ( buf , " x " , 1 ) )
return false ;
if ( not bencode_write_bytestring ( buf , introsetPayload . data ( ) , introsetPayload . size ( ) ) )
return false ;
if ( not BEncodeWriteDictEntry ( " z " , sig , buf ) )
return false ;
return bencode_end ( buf ) ;
introsetPayload . resize ( strbuf . sz ) ;
std : : copy_n ( strbuf . base , strbuf . sz , introsetPayload . data ( ) ) ;
return true ;
}
if ( not BEncodeMaybeReadDictEntry ( " d " , derivedSigningKey , read , key , buf ) )
return false ;
bool
EncryptedIntroSet : : DecodeKey ( const llarp_buffer_t & key , llarp_buffer_t * buf )
{
bool read = false ;
if ( key = = " x " )
{
llarp_buffer_t strbuf ;
if ( not bencode_read_string ( buf , & strbuf ) )
return false ;
if ( strbuf . sz > MAX_INTROSET_SIZE )
return false ;
introsetPayload . resize ( strbuf . sz ) ;
std : : copy_n ( strbuf . base , strbuf . sz , introsetPayload . data ( ) ) ;
return true ;
}
if ( not BEncodeMaybeReadDictEntry ( " d " , derivedSigningKey , read , key , buf ) )
return false ;
if ( not BEncodeMaybeReadDictEntry ( " n " , nounce , read , key , buf ) )
return false ;
if ( not BEncodeMaybeReadDict Entry( " n " , nounce , read , key , buf ) )
return false ;
if ( not BEncodeMaybeReadDictInt ( " s " , signedAt , read , key , buf ) )
return false ;
if ( not BEncodeMaybeReadDictInt ( " s " , signedAt , read , key , buf ) )
return false ;
if ( not BEncodeMaybeReadDictEntry ( " z " , sig , read , key , buf ) )
return false ;
return read ;
}
if ( not BEncodeMaybeReadDictEntry ( " z " , sig , read , key , buf ) )
return false ;
return read ;
}
bool
EncryptedIntroSet : : OtherIsNewer ( const EncryptedIntroSet & other ) const
{
return signedAt < other . signedAt ;
}
bool
EncryptedIntroSet : : OtherIsNewer ( const EncryptedIntroSet & other ) const
{
return signedAt < other . signedAt ;
}
std : : ostream &
EncryptedIntroSet : : print ( std : : ostream & out , int levels , int spaces ) const
{
Printer printer ( out , levels , spaces ) ;
printer . printAttribute ( " d " , derivedSigningKey ) ;
printer . printAttribute ( " n " , nounce ) ;
printer . printAttribute ( " s " , signedAt . count ( ) ) ;
printer . printAttribute ( " x " , " [ " + std : : to_string ( introsetPayload . size ( ) ) + " bytes] " ) ;
printer . printAttribute ( " z " , sig ) ;
return out ;
}
std : : optional < IntroSet >
EncryptedIntroSet : : MaybeDecrypt ( const PubKey & root ) const
{
SharedSecret k ( root ) ;
IntroSet i ;
std : : vector < byte_t > payload = introsetPayload ;
llarp_buffer_t buf ( payload ) ;
CryptoManager : : instance ( ) - > xchacha20 ( buf , k , nounce ) ;
if ( not i . BDecode ( & buf ) )
return { } ;
return i ;
}
bool
EncryptedIntroSet : : IsExpired ( llarp_time_t now ) const
{
return now > = signedAt + path : : default_lifetime ;
}
std : : ostream &
EncryptedIntroSet : : print ( std : : ostream & out , int levels , int spaces ) const
{
Printer printer ( out , levels , spaces ) ;
printer . printAttribute ( " d " , derivedSigningKey ) ;
printer . printAttribute ( " n " , nounce ) ;
printer . printAttribute ( " s " , signedAt . count ( ) ) ;
printer . printAttribute ( " x " , " [ " + std : : to_string ( introsetPayload . size ( ) ) + " bytes] " ) ;
printer . printAttribute ( " z " , sig ) ;
return out ;
}
bool
EncryptedIntroSet : : Sign ( const PrivateKey & k )
{
signedAt = llarp : : time_now_ms ( ) ;
if ( not k . toPublic ( derivedSigningKey ) )
return false ;
sig . Zero ( ) ;
std : : array < byte_t , MAX_INTROSET_SIZE + 128 > tmp ;
llarp_buffer_t buf ( tmp ) ;
if ( not BEncode ( & buf ) )
return false ;
buf . sz = buf . cur - buf . base ;
buf . cur = buf . base ;
if ( not CryptoManager : : instance ( ) - > sign ( sig , k , buf ) )
return false ;
LogDebug ( " signed encrypted introset: " , * this ) ;
return true ;
}
std : : optional < IntroSet >
EncryptedIntroSet : : MaybeDecrypt ( const PubKey & root ) const
{
SharedSecret k ( root ) ;
IntroSet i ;
std : : vector < byte_t > payload = introsetPayload ;
llarp_buffer_t buf ( payload ) ;
CryptoManager : : instance ( ) - > xchacha20 ( buf , k , nounce ) ;
if ( not i . BDecode ( & buf ) )
return { } ;
return i ;
}
bool
EncryptedIntroSet : : Verify ( llarp_time_t now ) const
{
if ( IsExpired ( now ) )
return false ;
std : : array < byte_t , MAX_INTROSET_SIZE + 128 > tmp ;
llarp_buffer_t buf ( tmp ) ;
EncryptedIntroSet copy ( * this ) ;
copy . sig . Zero ( ) ;
if ( not copy . BEncode ( & buf ) )
return false ;
LogDebug ( " verify encrypted introset: " , copy , " sig = " , sig ) ;
buf . sz = buf . cur - buf . base ;
buf . cur = buf . base ;
return CryptoManager : : instance ( ) - > verify ( derivedSigningKey , buf , sig ) ;
}
util : : StatusObject
IntroSet : : ExtractStatus ( ) const
{
util : : StatusObject obj { { " published " , to_json ( T ) } } ;
std : : vector < util : : StatusObject > introsObjs ;
std : : transform (
I . begin ( ) ,
I . end ( ) ,
std : : back_inserter ( introsObjs ) ,
[ ] ( const auto & intro ) - > util : : StatusObject { return intro . ExtractStatus ( ) ; } ) ;
obj [ " intros " ] = introsObjs ;
if ( ! topic . IsZero ( ) )
obj [ " topic " ] = topic . ToString ( ) ;
return obj ;
}
bool
IntroSet : : DecodeKey ( const llarp_buffer_t & key , llarp_buffer_t * buf )
{
bool read = false ;
if ( ! BEncodeMaybeReadDictEntry ( " a " , A , read , key , buf ) )
return false ;
bool
EncryptedIntroSet : : IsExpired ( llarp_time_t now ) const
if ( key = = " i " )
{
return now > = signedAt + path : : default_lifetime ;
return BEncodeReadList ( I , buf ) ;
}
if ( ! BEncodeMaybeReadDictEntry ( " k " , K , read , key , buf ) )
return false ;
bool
EncryptedIntroSet : : Sign ( const PrivateKey & k )
{
signedAt = llarp : : time_now_ms ( ) ;
if ( not k . toPublic ( derivedSigningKey ) )
return false ;
sig . Zero ( ) ;
std : : array < byte_t , MAX_INTROSET_SIZE + 128 > tmp ;
llarp_buffer_t buf ( tmp ) ;
if ( not BEncode ( & buf ) )
return false ;
buf . sz = buf . cur - buf . base ;
buf . cur = buf . base ;
if ( not CryptoManager : : instance ( ) - > sign ( sig , k , buf ) )
return false ;
LogDebug ( " signed encrypted introset: " , * this ) ;
return true ;
}
if ( ! BEncodeMaybeReadDictEntry ( " n " , topic , read , key , buf ) )
return false ;
bool
EncryptedIntroSet : : Verify ( llarp_time_t now ) const
if ( key = = " s " )
{
if ( IsExpired ( now ) )
return false ;
std : : array < byte_t , MAX_INTROSET_SIZE + 128 > tmp ;
llarp_buffer_t buf ( tmp ) ;
EncryptedIntroSet copy ( * this ) ;
copy . sig . Zero ( ) ;
if ( not copy . BEncode ( & buf ) )
byte_t * begin = buf - > cur ;
if ( not bencode_discard ( buf ) )
return false ;
LogDebug ( " verify encrypted introset: " , copy , " sig = " , sig ) ;
buf . sz = buf . cur - buf . base ;
buf . cur = buf . base ;
return CryptoManager : : instance ( ) - > verify ( derivedSigningKey , buf , sig ) ;
}
util : : StatusObject
IntroSet : : ExtractStatus ( ) const
{
util : : StatusObject obj { { " published " , to_json ( T ) } } ;
std : : vector < util : : StatusObject > introsObjs ;
std : : transform (
I . begin ( ) ,
I . end ( ) ,
std : : back_inserter ( introsObjs ) ,
[ ] ( const auto & intro ) - > util : : StatusObject { return intro . ExtractStatus ( ) ; } ) ;
obj [ " intros " ] = introsObjs ;
if ( ! topic . IsZero ( ) )
obj [ " topic " ] = topic . ToString ( ) ;
return obj ;
}
byte_t * end = buf - > cur ;
bool
IntroSet : : DecodeKey ( const llarp_buffer_t & key , llarp_buffer_t * buf )
{
bool read = false ;
if ( ! BEncodeMaybeReadDictEntry ( " a " , A , read , key , buf ) )
return false ;
std : : string_view srvString ( reinterpret_cast < char * > ( begin ) , end - begin ) ;
if ( key = = " i " )
try
{
return BEncodeReadList ( I , buf ) ;
lokimq : : bt_deserialize ( srvString , SRVs ) ;
}
if ( ! BEncodeMaybeReadDictEntry ( " k " , K , read , key , buf ) )
catch ( const lokimq : : bt_deserialize_invalid & err )
{
LogError ( " Error decoding SRV records from IntroSet: " , err . what ( ) ) ;
return false ;
}
read = true ;
}
if ( ! BEncodeMaybeReadDictEntry ( " n " , topic , read , key , buf ) )
return false ;
if ( ! BEncodeMaybeReadDict Int( " t " , T , read , key , buf ) )
return false ;
if ( key = = " s " )
{
byte_t * begin = buf - > cur ;
if ( not bencode_discard ( buf ) )
return false ;
if ( key = = " w " )
{
W . emplace ( ) ;
return bencode_decode_dict ( * W , buf ) ;
}
byte_t * end = buf - > cur ;
if ( ! BEncodeMaybeReadDictInt ( " v " , version , read , key , buf ) )
return false ;
std : : string_view srvString ( reinterpret_cast < char * > ( begin ) , end - begin ) ;
if ( ! BEncodeMaybeReadDictEntry ( " z " , Z , read , key , buf ) )
return false ;
try
{
lokimq : : bt_deserialize ( srvString , SRVs ) ;
}
catch ( const lokimq : : bt_deserialize_invalid & err )
{
LogError ( " Error decoding SRV records from IntroSet: " , err . what ( ) ) ;
return false ;
}
read = true ;
}
if ( read )
return true ;
if ( ! BEncodeMaybeReadDictInt ( " t " , T , read , key , buf ) )
return false ;
return bencode_discard ( buf ) ;
}
if ( key = = " w " )
{
W . emplace ( ) ;
return bencode_decode_dict ( * W , buf ) ;
}
bool
IntroSet : : BEncode ( llarp_buffer_t * buf ) const
{
if ( ! bencode_start_dict ( buf ) )
return false ;
if ( ! BEncodeWriteDictEntry ( " a " , A , buf ) )
return false ;
// start introduction list
if ( ! bencode_write_bytestring ( buf , " i " , 1 ) )
return false ;
if ( ! BEncodeWriteList ( I . begin ( ) , I . end ( ) , buf ) )
return false ;
// end introduction list
if ( ! BEncodeMaybeReadDictInt ( " v " , version , read , key , buf ) )
return false ;
// pq pubkey
if ( ! BEncodeWriteDictEntry ( " k " , K , buf ) )
return false ;
if ( ! BEncodeMaybeReadDictEntry ( " z " , Z , read , key , buf ) )
// topic tag
if ( topic . ToString ( ) . size ( ) )
{
if ( ! BEncodeWriteDictEntry ( " n " , topic , buf ) )
return false ;
if ( read )
return true ;
return bencode_discard ( buf ) ;
}
bool
IntroSet : : BEncode ( llarp_buffer_t * buf ) const
if ( SRVs . size ( ) )
{
if ( ! bencode_start_dict ( buf ) )
return false ;
if ( ! BEncodeWriteDictEntry ( " a " , A , buf ) )
return false ;
// start introduction list
if ( ! bencode_write_bytestring ( buf , " i " , 1 ) )
std : : string serial = lokimq : : bt_serialize ( SRVs ) ;
if ( ! bencode_write_bytestring ( buf , " s " , 1 ) )
return false ;
if ( ! BEncodeWriteList( I . begin ( ) , I . end ( ) , buf ) )
if ( ! buf - > write ( serial . begin ( ) , serial . end ( ) ) )
return false ;
// end introduction list
}
// Timestamp published
if ( ! BEncodeWriteDictInt ( " t " , T . count ( ) , buf ) )
return false ;
// pq pubkey
if ( ! BEncodeWriteDictEntry ( " k " , K , buf ) )
// write version
if ( ! BEncodeWriteDictInt ( " v " , version , buf ) )
return false ;
if ( W )
{
if ( ! BEncodeWriteDictEntry ( " w " , * W , buf ) )
return false ;
}
if ( ! BEncodeWriteDictEntry ( " z " , Z , buf ) )
return false ;
// topic tag
if ( topic . ToString ( ) . size ( ) )
{
if ( ! BEncodeWriteDictEntry ( " n " , topic , buf ) )
return false ;
}
return bencode_end ( buf ) ;
}
if ( SRVs . size ( ) )
{
std : : string serial = lokimq : : bt_serialize ( SRVs ) ;
if ( ! bencode_write_bytestring ( buf , " s " , 1 ) )
return false ;
if ( ! buf - > write ( serial . begin ( ) , serial . end ( ) ) )
return false ;
}
bool
IntroSet : : HasExpiredIntros ( llarp_time_t now ) const
{
for ( const auto & i : I )
if ( now > = i . expiresAt )
return true ;
return false ;
}
// Timestamp published
if ( ! BEncodeWriteDictInt ( " t " , T . count ( ) , buf ) )
return false ;
bool
IntroSet : : IsExpired ( llarp_time_t now ) const
{
return GetNewestIntroExpiration ( ) < now ;
}
// write version
if ( ! BEncodeWriteDictInt ( " v " , version , buf ) )
return false ;
if ( W )
std : : vector < llarp : : dns : : SRVData >
IntroSet : : GetMatchingSRVRecords ( std : : string_view service_proto ) const
{
std : : vector < llarp : : dns : : SRVData > records ;
for ( const auto & tuple : SRVs )
{
if ( std : : get < 0 > ( tuple ) = = service_proto )
{
if ( ! BEncodeWriteDictEntry ( " w " , * W , buf ) )
return false ;
records . push_back ( llarp : : dns : : SRVData : : fromTuple ( tuple ) ) ;
}
if ( ! BEncodeWriteDictEntry ( " z " , Z , buf ) )
return false ;
return bencode_end ( buf ) ;
}
bool
IntroSet : : HasExpiredIntros ( llarp_time_t now ) const
return records ;
}
bool
IntroSet : : Verify ( llarp_time_t now ) const
{
std : : array < byte_t , MAX_INTROSET_SIZE > tmp ;
llarp_buffer_t buf ( tmp ) ;
IntroSet copy ;
copy = * this ;
copy . Z . Zero ( ) ;
if ( ! copy . BEncode ( & buf ) )
{
for ( const auto & i : I )
if ( now > = i . expiresAt )
return true ;
return false ;
}
bool
IntroSet : : IsExpired ( llarp_time_t now ) const
// rewind and resize buffer
buf . sz = buf . cur - buf . base ;
buf . cur = buf . base ;
if ( ! A . Verify ( buf , Z ) )
{
return GetNewestIntroExpiration ( ) < now ;
return false ;
}
std : : vector < llarp : : dns : : SRVData >
IntroSet : : GetMatchingSRVRecords ( std : : string_view service_proto ) const
// validate PoW
if ( W & & ! W - > IsValid ( now ) )
{
std : : vector < llarp : : dns : : SRVData > records ;
for ( const auto & tuple : SRVs )
{
if ( std : : get < 0 > ( tuple ) = = service_proto )
{
records . push_back ( llarp : : dns : : SRVData : : fromTuple ( tuple ) ) ;
}
}
return records ;
return false ;
}
bool
IntroSet : : Verify ( llarp_time_t now ) const
// valid timestamps
// add max clock skew
now + = MAX_INTROSET_TIME_DELTA ;
for ( const auto & intro : I )
{
std : : array < byte_t , MAX_INTROSET_SIZE > tmp ;
llarp_buffer_t buf ( tmp ) ;
IntroSet copy ;
copy = * this ;
copy . Z . Zero ( ) ;
if ( ! copy . BEncode ( & buf ) )
{
return false ;
}
// rewind and resize buffer
buf . sz = buf . cur - buf . base ;
buf . cur = buf . base ;
if ( ! A . Verify ( buf , Z ) )
{
return false ;
}
// validate PoW
if ( W & & ! W - > IsValid ( now ) )
if ( intro . expiresAt > now & & intro . expiresAt - now > path : : default_lifetime )
{
return false ;
}
// valid timestamps
// add max clock skew
now + = MAX_INTROSET_TIME_DELTA ;
for ( const auto & intro : I )
{
if ( intro . expiresAt > now & & intro . expiresAt - now > path : : default_lifetime )
if ( ! W )
{
if ( ! W )
{
LogWarn ( " intro has too high expire time " ) ;
return false ;
}
if ( intro . expiresAt - W - > extendedLifetime > path : : default_lifetime )
{
return false ;
}
LogWarn ( " intro has too high expire time " ) ;
return false ;
}
if ( intro . expiresAt - W - > extendedLifetime > path : : default_lifetime )
{
return false ;
}
}
if ( IsExpired ( now ) )
{
LogWarn ( " introset expired: " , * this ) ;
return false ;
}
return true ;
}
llarp_time_t
IntroSet : : GetNewestIntroExpiration ( ) const
if ( IsExpired ( now ) )
{
llarp_time_t t = 0 s ;
for ( const auto & intro : I )
t = std : : max ( intro . expiresAt , t ) ;
return t ;
LogWarn ( " introset expired: " , * this ) ;
return false ;
}
return true ;
}
std : : ostream &
IntroSet : : print ( std : : ostream & stream , int level , int spaces ) const
{
Printer printer ( stream , level , spaces ) ;
printer . printAttribute ( " A " , A ) ;
printer . printAttribute ( " I " , I ) ;
printer . printAttribute ( " K " , K ) ;
llarp_time_t
IntroSet : : GetNewestIntroExpiration ( ) const
{
llarp_time_t t = 0 s ;
for ( const auto & intro : I )
t = std : : max ( intro . expiresAt , t ) ;
return t ;
}
std : : ostream &
IntroSet : : print ( std : : ostream & stream , int level , int spaces ) const
{
Printer printer ( stream , level , spaces ) ;
printer . printAttribute ( " A " , A ) ;
printer . printAttribute ( " I " , I ) ;
printer . printAttribute ( " K " , K ) ;
std : : string _topic = topic . ToString ( ) ;
std : : string _topic = topic . ToString ( ) ;
if ( ! _topic . empty ( ) )
{
printer . printAttribute ( " topic " , _topic ) ;
}
else
{
printer . printAttribute ( " topic " , topic ) ;
}
printer . printAttribute ( " T " , T . count ( ) ) ;
if ( W )
{
printer . printAttribute ( " W " , * W ) ;
}
else
{
printer . printAttribute ( " W " , " NULL " ) ;
}
printer . printAttribute ( " V " , version ) ;
printer . printAttribute ( " Z " , Z ) ;
if ( ! _topic . empty ( ) )
{
printer . printAttribute ( " topic " , _topic ) ;
}
else
{
printer . printAttribute ( " topic " , topic ) ;
}
return stream ;
printer . printAttribute ( " T " , T . count ( ) ) ;
if ( W )
{
printer . printAttribute ( " W " , * W ) ;
}
} // namespace service
} // namespace llarp
else
{
printer . printAttribute ( " W " , " NULL " ) ;
}
printer . printAttribute ( " V " , version ) ;
printer . printAttribute ( " Z " , Z ) ;
return stream ;
}
} // namespace llarp::service