(p: any, now: number)
| 128 | } |
| 129 | |
| 130 | async _deriveSecrets(p: any, now: number) { |
| 131 | this._logger.debug('deriving secrets from', p); |
| 132 | if (!p.interval) return [await algorithms[p.algId].derive(p.algParams, null)]; |
| 133 | const t0 = intervalStart(now, p.interval); |
| 134 | // Start of the first interval covered by these params. To accommodate clock skew, p.interval is |
| 135 | // subtracted. If we did not do this, then the following could happen: |
| 136 | // 1. Instance (A) starts up and publishes params starting at the current interval. |
| 137 | // 2. Instance (B) starts up with a clock that is in the previous interval. |
| 138 | // 3. Instance (B) reads the params published by instance (A) and sees that there's no |
| 139 | // coverage of what it thinks is the current interval. |
| 140 | // 4. Instance (B) generates and publishes new params that covers what it thinks is the |
| 141 | // current interval. |
| 142 | // 5. Instance (B) starts generating MACs from a secret derived from the new params. |
| 143 | // 6. Instance (A) fails to validate the MACs generated by instance (B) until it re-reads |
| 144 | // the published params, which might take as long as interval. |
| 145 | // An alternative approach is to backdate p.start by p.interval when creating new params, but |
| 146 | // this could affect the end time of legacy secrets. |
| 147 | const tA = intervalStart(p.start - p.interval, p.interval); |
| 148 | const tZ = intervalStart(p.end - 1, p.interval); |
| 149 | this._logger.debug('now:', now, 't0:', t0, 'tA:', tA, 'tZ:', tZ); |
| 150 | // Starts of intervals to derive keys for. |
| 151 | const tNs = []; |
| 152 | // Whether the derived secret for the interval starting at tN is still relevant. If there was no |
| 153 | // clock skew, a derived secret is relevant until p.lifetime has elapsed since the end of the |
| 154 | // interval. To accommodate clock skew, this end time is extended by p.interval. |
| 155 | const expired = (tN:number) => now >= tN + (2 * p.interval) + p.lifetime; |
| 156 | // Walk from t0 back until either the start of coverage or the derived secret is expired. t0 |
| 157 | // must always be the first entry in case p is the current params. (The first derived secret is |
| 158 | // used for generating MACs, so the secret derived for t0 must be before the secrets derived for |
| 159 | // other times.) |
| 160 | for (let tN = Math.min(t0, tZ); tN >= tA && !expired(tN); tN -= p.interval) tNs.push(tN); |
| 161 | // Include a future derived secret to accommodate clock skew. |
| 162 | if (t0 + p.interval <= tZ) tNs.push(t0 + p.interval); |
| 163 | this._logger.debug('deriving secrets for intervals with start times:', tNs); |
| 164 | return await Promise.all( |
| 165 | tNs.map(async (tN) => await algorithms[p.algId].derive(p.algParams, `${tN}`))); |
| 166 | } |
| 167 | |
| 168 | async _update() { |
| 169 | const now = this._t.now(); |
no test coverage detected