@ -1,12 +1,12 @@
/ / / The locator of the extension datekeys . capsule of a . dkk and the envelope
/ / / it points to ( spec § 44.1 ) , as package locator of datekeys - go at the draft
/ / / v0. 12 , with the same checks in the same order and the same texts:
/ / / - the locator ( [ Locator ] ) , an age file sealed with tlock for the date of
/ / / the capsule ( [ openLocator ] ) , whose plaintext says where the capsule is :
/ / / the addresses of the rest , and the key , the header and the digests of
/ / / the envelope ;
/ / / - the addresses , as CheckURI of Go ( [ checkAddressUri ] ) , and the host that
/ / / a reader shows before it downloads ( [ LocatorAddress . host ] ) ;
/ / / The extension datekeys . capsule of a . dkk and what it points to ( spec § 43
/ / / to § 44.1 ) , as package locator of datekeys - go at the draft v0 . 12 , with the
/ / / same checks in the same order , the same codes and the same texts:
/ / / - the data of the extension ( [ CapsuleInfo ] , [ parseCapsuleInfo ] ) , which
/ / / says what the capsule of a key is and when it opens ;
/ / / - the locator ( [ Locator ] ) , an age file sealed with tlock for that date
/ / / ( [ openLocator ] ) , whose plaintext says where the capsule is : the
/ / / addresses of the rest , and the key , the header and the digests of the
/ / / envelope ;
/ / / - the envelope , the . dkc encrypted with age and split into a header ,
/ / / which the locator carries , and a rest , the only thing kept outside ,
/ / / alone or inside another file ( [ hideRest ] , [ Locator . restIn ] ,
@ -25,10 +25,12 @@
/ / / of NewEnvelope , need the writer of age: [ splitEnvelope ] is the rest of
/ / / NewEnvelope , the split of the age file of the envelope .
/ / /
/ / / The errors carry no normative code , as in Go: a locator that does not
/ / / read or does not open , or an address that breaks the rules of § 44.1 , is
/ / / unusable ( spec § 44.1 , § 57 ) , and each is a [ LocatorException ] with the
/ / / text of Go .
/ / / The errors of the locator carry no normative code , as in Go: a locator
/ / / that does not read or does not open , or an address that breaks the rules
/ / / of § 44.1 , is unusable ( spec § 44.1 , § 57 ) , and each is a [ LocatorException ]
/ / / with the text of Go . The data of the extension has one code ,
/ / / ERR_EXTENSION_DATA_INVALID: data that does not read makes the extension
/ / / unusable , never the . dkk ( spec § 54 ) .
library ;
import ' dart:typed_data ' ;
@ -38,8 +40,12 @@ import 'agewrap.dart';
import ' bytes.dart ' ;
import ' cbor.dart ' ;
import ' chacha20poly1305.dart ' ;
import ' datekey.dart ' ;
import ' errors.dart ' ;
import ' extension.dart ' ;
import ' ipaddr.dart ' ;
import ' note.dart ' ;
import ' profile.dart ' ;
import ' release.dart ' ;
import ' sha256.dart ' ;
import ' tlock.dart ' ;
@ -83,6 +89,194 @@ LocatorException _fail(String detail) => LocatorException('locator: $detail');
DateKeysException _undefined ( String what ) = >
DateKeysException ( ErrorCode . nonCanonicalCbor , what ) ;
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / The data of datekeys . capsule
/ / / The data of the extension datekeys . capsule ( spec § 44.1 ) , as Info of Go .
final class CapsuleInfo {
/ / / The data with the copy of the public note [ note ] , ` ' ' ` for none , the
/ / / DateKey [ dateKey ] of the capsule , and the sealed locator [ sealed ] ,
/ / / which it copies , null for none .
CapsuleInfo ( { this . note = ' ' , required this . dateKey , List < int > ? sealed } )
: sealed = sealed = = null ? null : Uint8List . fromList ( sealed ) ;
/ / / Key 0 , the copy of the public note of the capsule , ` ' ' ` for none .
final String note ;
/ / / Key 1 , the DateKey of the capsule: it says when it opens .
final DateKey dateKey ;
/ / / Key 2 , the age file of the locator , sealed with tlock for the round of
/ / / [ dateKey ] , or null for none .
final Uint8List ? sealed ;
/ / / The extension for the noncritical array of a . dkk , as Extension of Go:
/ / / the DateKey must be canonical , the sealed locator of 1 byte to 1 MiB ,
/ / / and the note must meet the rules of spec § 24.1 ; the extension is read
/ / / back with the rules of a reader ( [ parseCapsuleInfo ] ) , which also ties
/ / / the locator to the round of the DateKey ( spec § 72 ) . The rules of the
/ / / note throw their [ DateKeysException ] , ERR_EXTENSION_DATA_INVALID ; the
/ / / others a [ LocatorException ] .
Extension toExtension ( ) {
DateKey ? d ;
try {
d = parseDateKey ( compactDateKey ( dateKey ) ) ;
} on DateKeysException {
d = null ;
}
if ( d = = null | | d ! = dateKey ) {
throw _fail ( ' Info.DateKey is not a canonical DateKey ' ) ;
}
final s = sealed ;
if ( s ! = null & & ( s . isEmpty | | s . length > _maxSealed ) ) {
throw _fail (
' a sealed locator of ${ s . length } bytes, not 1 to $ _maxSealed ' ,
) ;
}
if ( note . isNotEmpty ) checkNote ( note ) ;
final e = CborEncoder ( ) ;
_encodeInfo ( e , note , compactDateKey ( dateKey ) , s ) ;
final x = newExtension ( capsuleExtensionId , 1 , e . out ( ) ) ;
/ / Spec § 72 : the encoder reads what it writes with the rules of a reader ,
/ / which also ties the locator to the round of the DateKey .
try {
parseCapsuleInfo ( x ) ;
} on DateKeysException catch ( err ) {
throw _fail (
' self-check: a reader rejects this extension: ${ err . message } ' ,
) ;
}
return x ;
}
/ / / The locator of the extension , opened with [ release ] , the release of the
/ / / round of its own DateKey , in the profile that the DateKey names , as
/ / / OpenLocator of Go: a locator for another round or another chain does
/ / / not open , and is unusable ( spec § 44.1 ) . [ registry ] holds the pinned
/ / / profiles , the default registry , Quicknet , when null , as in the opening
/ / / of a capsule . Throws a [ LocatorException ] , also when the extension has
/ / / no locator .
Locator openLocator ( Release release , { ProfileRegistry ? registry } ) {
final s = sealed ;
if ( s = = null ) throw _fail ( ' the extension has no locator ' ) ;
final p = ( registry ? ? defaultRegistry ( ) ) . lookup ( dateKey . profileId ) ;
if ( p = = null ) throw _fail ( ' the profile of the DateKey is not pinned ' ) ;
return _open ( p , dateKey . round , release , s ) ;
}
}
void _encodeInfo ( CborEncoder e , String note , String dk , Uint8List ? sealed ) {
var pairs = 1 ;
if ( note . isNotEmpty ) pairs + + ;
if ( sealed ! = null ) pairs + + ;
e . map ( pairs ) ;
if ( note . isNotEmpty ) {
e
. . uint ( 0 )
. . text ( note ) ;
}
e
. . uint ( 1 )
. . text ( dk ) ;
if ( sealed ! = null ) {
e
. . uint ( 2 )
. . bstr ( sealed ) ;
}
}
DateKeysException _dataInvalid ( String detail ) = >
DateKeysException ( ErrorCode . extensionDataInvalid , ' locator: $ detail ' ) ;
/ / / Reads the data of a datekeys . capsule extension [ x ] , as ParseInfo of Go: a
/ / / map of the profile of spec § 58 with the note , key 0 , which meets the
/ / / rules of spec § 24.1 , the canonical DateKey , key 1 , and the sealed locator ,
/ / / key 2 , an age file with one tlock stanza for the round of the DateKey ; its
/ / / chain is checked when it opens . A failure makes the extension unusable ,
/ / / not the . dkk ( spec § 54 ) : it is a [ DateKeysException ] whose only code is
/ / / ERR_EXTENSION_DATA_INVALID .
CapsuleInfo parseCapsuleInfo ( Extension x ) {
final data = x . data ;
if ( x . id ! = capsuleExtensionId | | x . version ! = 1 | | data = = null ) {
throw _dataInvalid ( ' not datekeys.capsule version 1 with data ' ) ;
}
var note = ' ' ;
var dk = ' ' ;
Uint8List ? sealed ;
try {
unmarshalCbor ( data , ( d ) {
final pairs = d . map ( 3 ) ;
var seen = 0 ;
for ( var i = 0 ; i < pairs ; i + + ) {
final k = d . key ( ) ;
switch ( k ) {
case 0 :
withContext ( ' key 0 ' , ( ) {
note = d . text ( maxNoteLen ) ;
checkNote ( note ) ;
} ) ;
case 1 :
dk = withContext ( ' key 1 ' , ( ) = > d . text ( 1024 ) ) ;
case 2 :
sealed = withContext ( ' key 2 ' , ( ) = > d . bstr ( 1 , _maxSealed ) ) ;
default :
throw _undefined ( ' key $ k is not defined ' ) ;
}
seen | = 1 < < ( k as int ) ;
}
if ( seen & 2 = = 0 ) throw _undefined ( ' key 1 is missing ' ) ;
d . endMap ( ) ;
} , ( e ) = > _encodeInfo ( e , note , dk , sealed ) ) ;
} on DateKeysException catch ( err ) {
throw _dataInvalid ( ' datekeys.capsule: ${ err . message } ' ) ;
}
DateKey ? d ;
try {
d = parseDateKey ( dk ) ;
} on DateKeysException {
d = null ;
}
if ( d = = null | | compactDateKey ( d ) ! = dk ) {
throw _dataInvalid ( ' compact_datekey is not a canonical DateKey ' ) ;
}
final s = sealed ;
if ( s ! = null & & ! _sealedFor ( s , d . round ) ) {
throw _dataInvalid (
' the locator is not an age file with one tlock stanza for round '
' ${ d . round } , the one of its DateKey ' ,
) ;
}
return CapsuleInfo ( note: note , dateKey: d , sealed: s ) ;
}
/ / Whether sealed is an age file whose header holds one tlock stanza with two
/ / arguments , the first of them round .
bool _sealedFor ( Uint8List sealed , int round ) {
final List < AgeStanza > st ;
try {
st = ageStanzas ( sealed ) ;
} on DateKeysException {
return false ;
}
return st . length = = 1 & &
st [ 0 ] . type = = stanzaTlock & &
st [ 0 ] . args . length = = 2 & &
st [ 0 ] . args [ 0 ] = = ' $ round ' ;
}
/ / / The check of the data of datekeys . capsule that a reader that knows the
/ / / extension runs ( spec § 54 ) , as the ValidateCapsule of Go ' s
/ / / locator . Standard: the rejection of [ parseCapsuleInfo ] , or null when the
/ / / data reads . [ StandardExtensions ] runs it .
Object ? checkCapsuleData ( Extension e ) {
try {
parseCapsuleInfo ( e ) ;
return null ;
} on DateKeysException catch ( err ) {
return err ;
}
}
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / Addresses