open static method
Verifies envelope against publicKey and returns what it states.
The statement is parsed from the exact bytes the signature covers, and
those bytes must be canonical: a payload with a duplicated key means one
thing to one parser and another to the next. With
requireContentVersion the bundle's version must be its content digest.
Throws DVSignedBundleException for anything else, including a bundle that was never signed at all.
Implementation
static DVSignedBundle open(
String envelope, {
required List<int> publicKey,
bool requireContentVersion = false,
}) {
final Object? outer;
try {
outer = jsonDecode(envelope);
} on FormatException {
throw const DVSignedBundleException('The body is not a bundle at all.');
}
if (outer is! Map ||
outer['format'] != dvSignedBundleFormat ||
outer['payload'] is! String ||
outer['signature'] is! String) {
throw const DVSignedBundleException(
'This bundle is not signed, and only signed bundles are loaded.',
);
}
final Uint8List payload;
final Uint8List signature;
try {
payload = dvWebPushBase64Decode(outer['payload'] as String);
signature = dvWebPushBase64Decode(outer['signature'] as String);
} on FormatException {
throw const DVSignedBundleException(
'The envelope\'s payload or signature is not base64url.',
);
}
if (!_verifyEs256(payload, signature, publicKey)) {
throw const DVSignedBundleException(
'The signature does not verify against the paired key. The bundle '
'was sealed by another key or altered after sealing.',
);
}
final String text;
final Object? statement;
try {
text = utf8.decode(payload);
statement = jsonDecode(text);
} on FormatException {
throw const DVSignedBundleException('The signed payload is not JSON.');
}
if (jsonEncode(statement) != text) {
throw const DVSignedBundleException(
'The signed payload is not canonical JSON (a key is duplicated or '
'spaced differently), so it could be read two ways.',
);
}
if (statement is! Map || statement['format'] != dvSignedBundleFormat) {
throw const DVSignedBundleException(
'The signed payload does not name the bundle format.',
);
}
final Object? bundle = statement['bundle'];
if (bundle is! Map ||
bundle['version'] is! String ||
(bundle['version'] as String).isEmpty) {
throw const DVSignedBundleException(
'The signed payload carries no versioned bundle.',
);
}
final Map<String, Object?> typed = bundle.cast<String, Object?>();
if (requireContentVersion &&
typed['version'] != dvDevClientBundleVersion(typed)) {
throw DVSignedBundleException(
'The bundle\'s version "${typed['version']}" is not the digest of '
'its content, so a changed bundle could be taken for one already '
'applied.',
);
}
final Object? channel = statement['channel'];
final Object? sequence = statement['sequence'];
final Object? requires = statement['requires'];
try {
return DVSignedBundle._(
bundle: typed,
channel: channel is String ? channel : null,
sequence: sequence is int ? sequence : null,
requires: requires is Map
? DVDevClientManifest.fromJson(requires.cast<String, Object?>())
: null,
);
} on FormatException catch (error) {
throw DVSignedBundleException(error.message);
}
}