DVDevClientPairing.parse constructor
DVDevClientPairing.parse(
- Uri link
Parses a pairing link, refusing anything it could not trust.
Implementation
factory DVDevClientPairing.parse(Uri link) {
if (link.scheme != dvDevClientLinkScheme || link.host != 'pair') {
throw const FormatException(
'Not a Dartvel pairing link: $dvDevClientLinkScheme://pair?... '
'expected.',
);
}
final Map<String, String> q = link.queryParameters;
final Uri? server = Uri.tryParse(q['server'] ?? '');
if (server == null || server.scheme != 'https' || server.host.isEmpty) {
// A dev server is reached over TLS pinned to the link's key. Over http
// the token would cross the network in the clear, and anything that
// saw it could read every page the server serves.
throw const FormatException(
'A pairing link names an https dev server. Scan the code a current '
'`dartvel dev` prints.',
);
}
final String branch = q['branch'] ?? '';
if (branch.trim().isEmpty) {
throw const FormatException('A pairing link names the branch it serves.');
}
final Uint8List key;
try {
key = dvWebPushBase64Decode(q['key'] ?? '');
} on FormatException {
throw const FormatException('The pairing key is not base64url.');
}
if (!_isP256Point(key)) {
// Without a key there is nothing to verify a bundle against, and a
// shell that loaded anyway would load from anybody.
throw const FormatException(
'A pairing link carries the server\'s P-256 public key.',
);
}
final String token = q['token'] ?? '';
Uint8List tokenBytes;
try {
tokenBytes = dvWebPushBase64Decode(token);
} on FormatException {
tokenBytes = Uint8List(0);
}
if (tokenBytes.length < 32) {
throw const FormatException(
'A pairing token is at least 32 random bytes; a shorter one can be '
'guessed by anything on the network.',
);
}
return DVDevClientPairing(
server: server,
branch: branch,
publicKey: key,
token: token,
);
}