diff --git a/README.md b/README.md
index 14e91bc..d5dcbbd 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Integrity](https://w3c.github.io/webappsec/specs/subresourceintegrity/) hashes.
* [`Integrity#pickAlgorithm`](#integrity-pick-algorithm)
* [`IntegrityMetadata#hexDigest`](#integrity-metadata-hex-digest)
* Integrity Generation
+ * [`fromHex`](#from-hex)
* [`fromData`](#from-data)
* [`fromStream`](#from-stream)
* Integrity Verification
@@ -227,6 +228,33 @@ to a hex representation of the base64 data.
ssri.parse('sha1-deadbeef', {single: true}).hexDigest() // '75e69d6de79f'
```
+#### `> ssri.fromHex(hexDigest, algorithm, [opts]) -> Integrity`
+
+Creates an `Integrity` object with a single entry, based on a hex-formatted
+hash. This is a utility function to help convert existing shasums to the
+Integrity format, and is roughly equivalent to something like:
+
+```javascript
+algorithm + '-' + Buffer.from(hexDigest, 'hex').toString('base64')
+```
+
+`opts.options` may optionally be passed in: it must be an array of option
+strings that will be added to all generated integrity metadata generated by
+`fromData`. This is a loosely-specified feature of SRIs, and currently has no
+specified semantics besides being `?`-separated. Use at your own risk, and
+probably avoid if your integrity strings are meant to be used with browsers.
+
+If `opts.strict` is true, the integrity object will be created using strict
+parsing rules. See [`ssri.parse`](#parse).
+
+If `opts.single` is true, a single `IntegrityMetadata` object will be returned.
+
+##### Example
+
+```javascript
+ssri.fromHex('75e69d6de79f', 'sha1').toString() // 'sha1-deadbeef'
+```
+
#### `> ssri.fromData(data, [opts]) -> Integrity`
Creates an `Integrity` object from either string or `Buffer` data, calculating
diff --git a/index.js b/index.js
index 9a660ef..038b639 100644
--- a/index.js
+++ b/index.js
@@ -31,11 +31,7 @@ class IntegrityMetadata {
this.options = rawOpts ? rawOpts.slice(1).split('?') : []
}
hexDigest () {
- return this.digest && (
- Buffer.from
- ? Buffer.from(this.digest, 'base64')
- : new Buffer(this.digest, 'base64')
- ).toString('hex')
+ return this.digest && bufFrom(this.digest, 'base64').toString('hex')
}
toString (opts) {
if (opts && opts.strict) {
@@ -136,6 +132,18 @@ function stringify (obj, opts) {
}
}
+module.exports.fromHex = fromHex
+function fromHex (hexDigest, algorithm, opts) {
+ const optString = (opts && opts.options && opts.options.length)
+ ? `?${opts.options.join('?')}`
+ : ''
+ return parse(
+ `${algorithm}-${
+ bufFrom(hexDigest, 'hex').toString('base64')
+ }${optString}`, opts
+ )
+}
+
module.exports.fromData = fromData
function fromData (data, opts) {
opts = opts || {}
@@ -254,3 +262,7 @@ function getPrioritizedHash (algo1, algo2) {
? algo1
: algo2
}
+
+function bufFrom (data, enc) {
+ return Buffer.from ? Buffer.from(data, enc) : new Buffer(data, enc)
+}
diff --git a/test/from.js b/test/from.js
index 5997617..d6da52d 100644
--- a/test/from.js
+++ b/test/from.js
@@ -16,6 +16,20 @@ function fileStream () {
return fs.createReadStream(__filename)
}
+test('fromHex', t => {
+ t.equal(
+ ssri.fromHex('deadbeef', 'sha1').toString(),
+ `sha1-3q2+7w==`,
+ 'created an Integrity object from a given hex + sha'
+ )
+ t.equal(
+ ssri.fromHex('deadbeef', 'sha512', {options: ['a', 'b', 'c']}).toString(),
+ `sha512-3q2+7w==?a?b?c`,
+ 'options added to entry'
+ )
+ t.done()
+})
+
test('fromData', t => {
t.equal(
ssri.fromData(TEST_DATA).toString(),