// Capsules with a large content, made from the time_only fixtures as // whoever knows their keys can (spec ยง36.1): PAYLOAD_AGE sealed again over // another plaintext with the file key that I_PAYLOAD unwraps and the nonce // of the fixture, and in format 3 CONTROL_CBOR sealed again with the L of // the new BODY, with FK_TIME, which the release of the fixture unwraps. The // streaming tests and tool/open_bench.dart open them. They read no file. library; import 'dart:typed_data'; import 'package:datekeys/datekeys.dart'; import 'package:datekeys/src/age.dart'; import 'package:datekeys/src/body.dart'; import 'package:datekeys/src/sha256.dart'; import 'package:datekeys/src/tlock.dart'; import 'age_support.dart' show streamSeal; export 'age_support.dart' show pattern; /// The parts of an age file: its header, its nonce and its STREAM. ({Uint8List header, Uint8List nonce, Uint8List stream}) ageParts( Uint8List file, ) { final h = parseAgeHeader(file).length; return ( header: Uint8List.sublistView(file, 0, h), nonce: Uint8List.sublistView(file, h, h + 16), stream: Uint8List.sublistView(file, h + 16), ); } /// The plaintext of the STREAM of [file] under [fileKey]. Uint8List openStream(Uint8List file, Uint8List fileKey) { final p = ageParts(file); final d = AgePayloadDecryptor( hkdfSha256(fileKey, p.nonce, 'payload'.codeUnits, 32), ); final out = BytesBuilder(copy: false); for (final c in d.add(p.stream)) { out.add(c); } out.add(d.close()); return out.takeBytes(); } /// [file] with its STREAM sealed again over [plaintext], its header and its /// nonce kept. Uint8List resealStream(Uint8List file, Uint8List fileKey, Uint8List plaintext) { final p = ageParts(file); return concatBytes([ p.header, p.nonce, streamSeal(fileKey, p.nonce, plaintext), ]); } /// The file key of PAYLOAD_AGE of [dkc], which [payloadIdentity] unwraps. Uint8List payloadFileKey(Uint8List dkc, Uint8List payloadIdentity) { final payload = splitCapsule(dkc).payload; return X25519Identity(payloadIdentity) .unwrap(parseAgeHeader(payload).stanzas); } /// [dkc], a time_only fixture of format 1, with [content] as its content. Uint8List withContent1( Uint8List dkc, Uint8List payloadIdentity, Uint8List content, ) { final s = splitCapsule(dkc); final fk = payloadFileKey(dkc, payloadIdentity); return concatBytes([ s.preludeBytes, s.publicHeader, s.sealedControl!, resealStream(s.payload, fk, content), ]); } /// [dkc], a time_only fixture of format 3 opened by [release], with a BODY /// of the files [files], each a path and its content, in the order of their /// paths, its security area kept, and the control that declares its L. Uint8List withFiles3( Uint8List dkc, Uint8List payloadIdentity, Release release, List<(String, Uint8List)> files, ) { final s = splitCapsule(dkc); final h = decodeHeader(s.publicHeader); final p = quicknet(); final sealed = s.sealedControl!; final tlock = parseAgeHeader(sealed).stanzas.single; final fkTime = unwrapTlockStanza( p, h.dateKey.round, release, tlock.args, tlock.body, ); final control = decodeControl( openStream(sealed, fkTime), CapsuleFormat.format3, ); // The BODY of the fixture, for its security area and its salt. final fk = payloadFileKey(dkc, payloadIdentity); final old = openStream(s.payload, fk); final frame = parseBodyFrame( Uint8List.sublistView(old, 0, bodyFrameSize), control.payloadLength!, ); final area = Uint8List.sublistView( old, bodyFrameSize, bodyFrameSize + frame.areaLen, ); final oldHead = decodeHead( Uint8List.sublistView( old, bodyFrameSize + frame.areaLen, bodyFrameSize + frame.areaLen + frame.headLen, ), ); var end = 0; final head = encodeHead( Head( salt: oldHead.salt, files: [ for (final (path, content) in files) HeadFile( path: path, size: content.length, start: end, end: end += content.length, sha256: sha256(content), ), ], ), ); final body = concatBytes([ bodyFrameBytes(BodyFrame(frame.areaLen, frame.securityLen, head.length)), area, head, for (final (_, content) in files) content, ]); final l = body.length; final padded = paddedLength(l, control.padding!); final newControl = encodeControl( Control( headerBinding: control.headerBinding, payloadIdentity: control.payloadIdentity, critical: control.critical, noncritical: control.noncritical, payloadLength: l, padding: control.padding, ), CapsuleFormat.format3, ); return concatBytes([ s.preludeBytes, s.publicHeader, resealStream(sealed, fkTime, newControl), resealStream(s.payload, fk, concatBytes([body, Uint8List(padded - l)])), ]); }