diff --git a/communication/src/errors.js b/communication/src/errors.js index 5c6d3401..4d6dd007 100644 --- a/communication/src/errors.js +++ b/communication/src/errors.js @@ -20,8 +20,8 @@ * in the code (e.g. configuration errors, incorrect use of the API). */ class ExtendableError extends Error { - constructor(message) { - super(message); + constructor(message, options) { + super(message, options); this.name = this.constructor.name; if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor); @@ -55,8 +55,8 @@ class ExtendableError extends Error { * retried at a later point, or replaced by updated messages. */ class RecoverableError extends ExtendableError { - constructor(message) { - super(message); + constructor(message, options) { + super(message, options); this.isRecoverableError = true; this.isPermanentError = false; } @@ -70,15 +70,26 @@ class RecoverableError extends ExtendableError { * trying to send it again will again trigger the identical error. */ class PermanentError extends ExtendableError { - constructor(message) { - super(message); + constructor(message, options) { + super(message, options); this.isRecoverableError = false; this.isPermanentError = true; } } +/** + * The message size exceeded the limit of 32K. + */ export class TooBigMsgError extends PermanentError {} + export class TransportError extends RecoverableError {} export class ProtocolError extends PermanentError {} export class InvalidMessageError extends PermanentError {} export class ClockOutOfSync extends RecoverableError {} + +/** + * Thrown when the client failed to fetch public keys. Without + * current keys, it cannot setup the end-to-end encryption + * needed to communicate with the server. + */ +export class FailedToFetchPublicKeys extends RecoverableError {} diff --git a/communication/src/server-public-key-accessor.js b/communication/src/server-public-key-accessor.js index b7843f52..2d505dd8 100644 --- a/communication/src/server-public-key-accessor.js +++ b/communication/src/server-public-key-accessor.js @@ -12,6 +12,7 @@ import logger from './logger.js'; import { getTimeAsYYYYMMDD } from './timestamps.js'; import { fromBase64 } from './encoding.js'; +import { FailedToFetchPublicKeys } from './errors.js'; function isYYYYMMDD(date) { return typeof date === 'string' && /^[0-9]{8}$/.test(date); @@ -72,21 +73,7 @@ export default class ServerPublicKeyAccessor { // not found on disk or outdated -> fetch from server if (!knownKeys) { - const url = `${this.collectorUrl}/config?fields=pubKeys`; - logger.info('Fetching new server public keys from', url); - const response = await fetch(url, { - method: 'GET', - credentials: 'omit', - redirect: 'manual', - }); - if (!response.ok) { - throw new Error( - `Failed to get config (${response.statusText}) from url=${url}`, - ); - } - const { pubKeys } = await response.json(); - logger.info('Fetched server public keys:', pubKeys); - + const pubKeys = await this._fetchPublicKeys(); const allKeys = Object.keys(pubKeys) .filter(isYYYYMMDD) .map((date) => [date, fromBase64(pubKeys[date])]); @@ -104,6 +91,31 @@ export default class ServerPublicKeyAccessor { this._knownKeys = knownKeys; } + async _fetchPublicKeys() { + const url = `${this.collectorUrl}/config?fields=pubKeys`; + logger.info('Fetching new server public keys from', url); + try { + const response = await fetch(url, { + method: 'GET', + credentials: 'omit', + redirect: 'manual', + }); + if (!response.ok) { + throw new Error( + `Failed to get config (${response.statusText}) from '${url}'`, + ); + } + const { pubKeys } = await response.json(); + logger.info('Fetched server public keys:', pubKeys); + return pubKeys; + } catch (e) { + throw new FailedToFetchPublicKeys( + `Failed to fetch public keys from '${url}'`, + { cause: e }, + ); + } + } + async importAndVerifyPubKeys(allKeys) { return new Map( await Promise.all(