Skip to content

Commit

Permalink
feat(pickAlgorithm): Intergrity#pickAlgorithm() added
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Apr 2, 2017
1 parent 2301e74 commit b97a796
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 24 deletions.
44 changes: 28 additions & 16 deletions README.md
Expand Up @@ -20,6 +20,7 @@ Integrity](https://w3c.github.io/webappsec/specs/subresourceintegrity/) hashes.
* [`stringify`](#stringify)
* [`Integrity#concat`](#integrity-concat)
* [`Integrity#toString`](#integrity-to-string)
* [`Integrity#pickAlgorithm`](#integrity-pick-algorithm)
* Integrity Generation
* [`fromData`](#from-data)
* [`fromStream`](#from-stream)
Expand Down Expand Up @@ -193,6 +194,23 @@ const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xp
ssri.parse(integrity).toString() === integrity
```

#### <a name="integrity-pick-algorithm"></a> `> Integrity#pickAlgorithm([opts]) -> String`

Returns the "best" algorithm from those available in the integrity object.

If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
arguments. ssri will prioritize whichever of the two algorithms is returned by
this function. Note that the function may be called multiple times, and it
**must** return one of the two algorithms provided. By default, ssri will make
a best-effort to pick the strongest/most reliable of the given algorithms. It
may intentionally deprioritize algorithms with known vulnerabilities.

##### Example

```javascript
ssri.parse('sha1-WEakDigEST sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1').pickAlgorithm() // sha512
```

#### <a name="from-data"></a> `> ssri.fromData(data, [opts]) -> Integrity`

Creates an `Integrity` object from either string or `Buffer` data, calculating
Expand Down Expand Up @@ -258,12 +276,9 @@ representation that [`ssri.parse`](#parse) can handle.
If verification succeeds, `checkData` will return the name of the algorithm that
was used for verification (a truthy value). Otherwise, it will return `false`.

If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
arguments. ssri will prioritize whichever of the two algorithms is returned by
this function. Note that the function may be called multiple times, and it
**must** return one of the two algorithms provided. By default, ssri will make
a best-effort to pick the strongest/most reliable of the given algorithms. It
may intentionally deprioritize algorithms with known vulnerabilities.
If `opts.pickAlgorithm` is provided, it will be used by
[`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of
the available digests to match against.

##### Example

Expand All @@ -274,25 +289,22 @@ ssri.checkData(data, 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0')
ssri.checkData(data, 'sha1-BaDDigEST') // -> false
```

#### <a name="check-stream"></a> `> ssri.checkStream(stream, sri, [opts]) -> Promise<Algorithm>`
#### <a name="check-stream"></a> `> ssri.checkStream(stream, sri, [opts]) -> Promise<IntegrityMetadata>`

Verifies the contents of `stream` against an `sri` argument. `stream` will be
consumed in its entirety by this process. `sri` can be any subresource integrity
representation that [`ssri.parse`](#parse) can handle.

`checkStream` will return a Promise that either resolves to the string name of
the algorithm that verification was done with, or, if the verification fails or
an error happens with `stream`, the Promise will be rejected.
`checkStream` will return a Promise that either resolves to the
`IntegrityMetadata` that succeeded verification, or, if the verification fails
or an error happens with `stream`, the Promise will be rejected.

If the Promise is rejected because verification failed, the returned error will
have `err.code` as `EBADCHECKSUM`.

If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
arguments. ssri will prioritize whichever of the two algorithms is returned by
this function. Note that the function may be called multiple times, and it
**must** return one of the two algorithms provided. By default, ssri will make
a best-effort to pick the strongest/most reliable of the given algorithms. It
may intentionally deprioritize algorithms with known vulnerabilities.
If `opts.pickAlgorithm` is provided, it will be used by
[`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of
the available digests to match against.

##### Example

Expand Down
16 changes: 8 additions & 8 deletions index.js
Expand Up @@ -77,6 +77,12 @@ class Integrity {
: stringify(integrity, opts)
return parse(`${this.toString(opts)} ${other}`, opts)
}
pickAlgorithm (opts) {
const pickAlgorithm = (opts && opts.pickAlgorithm) || getPrioritizedHash
return Object.keys(this).reduce((acc, algo) => {
return pickAlgorithm(acc, algo) || acc
})
}
}

module.exports.parse = parse
Expand Down Expand Up @@ -175,10 +181,7 @@ module.exports.checkData = checkData
function checkData (data, sri, opts) {
opts = opts || {}
sri = parse(sri, opts)
const pickAlgorithm = opts.pickAlgorithm || getPrioritizedHash
const algorithm = Object.keys(sri).reduce((acc, algo) => {
return pickAlgorithm(acc, algo) || acc
})
const algorithm = sri.pickAlgorithm(opts)
const digests = sri[algorithm]
const digest = crypto.createHash(algorithm).update(data).digest('base64')
return digests.find(meta => meta.digest === digest) || false
Expand All @@ -203,10 +206,7 @@ module.exports.createCheckerStream = createCheckerStream
function createCheckerStream (sri, opts) {
opts = opts || {}
sri = parse(sri, opts)
const pickAlgorithm = opts.pickAlgorithm || getPrioritizedHash
const algorithm = Object.keys(sri).reduce((acc, algo) => {
return pickAlgorithm(acc, algo) || acc
})
const algorithm = sri.pickAlgorithm(opts)
const digests = sri[algorithm]
const hash = crypto.createHash(algorithm)
const stream = new Transform({
Expand Down
18 changes: 18 additions & 0 deletions test/integrity.js
Expand Up @@ -63,6 +63,24 @@ test('concat()', t => {
t.done()
})

test('pickAlgorithm()', t => {
const sri = ssri.parse('sha1-foo sha512-bar sha384-baz')
t.equal(sri.pickAlgorithm(), 'sha512', 'picked best algorithm')
t.equal(
ssri.parse('unknown-deadbeef uncertain-bada55').pickAlgorithm(),
'unknown',
'unrecognized algorithm returned if none others known'
)
t.equal(
sri.pickAlgorithm({
pickAlgorithm: (a, b) => 'sha384'
}),
'sha384',
'custom pickAlgorithm function accepted'
)
t.done()
})

test('semi-private', t => {
t.equal(ssri.Integrity, undefined, 'Integrity class is module-private.')
t.done()
Expand Down

0 comments on commit b97a796

Please sign in to comment.