You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.8 KiB
168 lines
4.8 KiB
// 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)])),
|
|
]);
|
|
}
|