diff --git a/__tests__/plugins/signed-integrity.test.js b/__tests__/plugins/signed-integrity.test.js index c053780..dfd5cea 100644 --- a/__tests__/plugins/signed-integrity.test.js +++ b/__tests__/plugins/signed-integrity.test.js @@ -11,6 +11,9 @@ describe("plugin: signed-integrity", () => { global.btoa = (bin) => { return Buffer.from(bin, 'binary').toString('base64') } + global.atob = (ascii) => { + return Buffer.from(ascii, 'base64').toString('binary') + } global.LibResilientPluginConstructors = new Map() LR = { @@ -19,12 +22,16 @@ describe("plugin: signed-integrity", () => { } } + header = btoa('{"alg": "ES384"}').replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, '') + payload = btoa('{"integrity": "sha256-eiMrFuthzteJuj8fPwUMyNQMb2SMW7VITmmt2oAxGj0="}').replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, '') + signature = btoa('FIXME').replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, '') + global.resolvingFetch = jest.fn((url, init)=>{ var content = '{"test": "success"}' var status = 200 var statusText = "OK" if (url == 'https://resilient.is/test.json.integrity') { - content = '{"integrity": "sha256-eiMrFuthzteJuj8fPwUMyNQMb2SMW7VITmmt2oAxGj0="}' + content = header + '.' + payload + '.' + signature } else if (url == 'https://resilient.is/fail.json.integrity') { content = '{"test": "fail"}' status = 404 diff --git a/plugins/signed-integrity.js b/plugins/signed-integrity.js index 5f86601..cc9cb4e 100644 --- a/plugins/signed-integrity.js +++ b/plugins/signed-integrity.js @@ -46,6 +46,31 @@ throw new Error(`Expected exactly one plugin to wrap, but ${config.uses.length} configured.`) } + /** + * utility function + * base64url decode + */ + let b64urlDecode = (data) => { + // figure out the padding to add + var pad = 4 - (data.length % 4); + // that's no bueno + if (pad == 3) { + throw new Error(`Invalid base64-encoded string!`) + } + // no padding, then + if (pad == 4) { + pad = 0 + } + // we're done, atob that thing + return atob( + data + .replace(/_/g, '/') + .replace(/-/g, '+') + + '='.repeat(pad) + ) + } + + /** * getting content using the configured plugin, * but also making sure integrity data file is fetched, signature checked, @@ -64,8 +89,20 @@ // did we get anything sane? if (integrityResponse.status == 200) { - LR.log(pluginName, `fetched integrity data file`) + // this is where magic happens + LR.log(pluginName, `fetched integrity data file`) + + // get the JWT + var jwt = await integrityResponse.text() + console.log('jwt: ' + jwt) + jwt = jwt.split('.') + + // unpack it + var header = b64urlDecode(jwt[0]) + var payload = b64urlDecode(jwt[1]) + var signature = jwt[2] + LR.log(pluginName, `got a JWT:\n- header : ${header}\n- payload: ${payload}`) } else { LR.log(pluginName, `fetching integrity data failed: ${integrityResponse.status} ${integrityResponse.statusText}`) }