diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0c282..5dfc590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # @digitalbazaar/http-digest-header Changelog +## 2.3.0 - 2025-10-dd + +### Added +- Support parsing a digest from structured field value, i.e., expressed as + a base64-encoded byte array that is wrapped in colons; + see: https://datatracker.ietf.org/doc/html/rfc9651#name-byte-sequences. + ## 2.2.1 - 2025-10-08 ### Fixed diff --git a/lib/httpDigest.js b/lib/httpDigest.js index c6550e8..1bd8132 100644 --- a/lib/httpDigest.js +++ b/lib/httpDigest.js @@ -76,10 +76,13 @@ function _createMultihash({digest}) { } function _parseHeaderValue(headerValue) { - const [key, encodedDigest] = headerValue.split(/=(.+)/); + const [key, digestValue] = headerValue.split(/=(.+)/); + let encodedDigest; let algorithm; if(key === 'mh') { + encodedDigest = digestValue; + // if `encodedDigest` starts with `uEi`, then it is a base64url-encoded // sha-256 multihash if(encodedDigest.startsWith('uEi')) { @@ -89,6 +92,10 @@ function _parseHeaderValue(headerValue) { `Only base64url-encoded, sha-256 multihash is supported.`); } } else { + // per RFC 9651, the digest value could be a structured field value, + // expressed as a base64-encoded byte array wrapped in colons + encodedDigest = digestValue?.replace(/^:(.*):$/, '$1'); + algorithm = key.replace('-', '').toLowerCase(); if(algorithm !== 'sha256') { throw new Error(`Algorithm "${algorithm}" is not supported.`); diff --git a/test/unit/httpDigest.spec.js b/test/unit/httpDigest.spec.js index 7bd3f46..162e414 100644 --- a/test/unit/httpDigest.spec.js +++ b/test/unit/httpDigest.spec.js @@ -143,6 +143,37 @@ describe('http-signature-digest', () => { should.exist(verifyResult); verifyResult.verified.should.equal(true); }); + it('should verify a digest wrapped in colons', async () => { + const data = `{"hello": "world"}`; + const headerValue = + `sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:`; + let verifyResult; + let err; + try { + verifyResult = await verifyHeaderValue({data, headerValue}); + } catch(e) { + err = e; + } + should.not.exist(err); + should.exist(verifyResult); + verifyResult.verified.should.equal(true); + }); + it('should verify false if a multihash digest is wrapped in colons', + async () => { + const data = '{"hello":"world"}'; + const headerValue = + `mh=:uEiCTojlxqRTl6svwqNJRVM2jCcPBxy-7mRTUfGDzy2gViA:`; + let verifyResult; + let err; + try { + verifyResult = await verifyHeaderValue({data, headerValue}); + } catch(e) { + err = e; + } + should.not.exist(err); + should.exist(verifyResult); + verifyResult.verified.should.equal(false); + }); it('should verify false if verifying bad data object', async () => { const data = {hello: 'world'}; const headerValue = await createHeaderValue(