2017-05-09 14:46:02 -07:00
'use strict'
const crypto = require ( 'crypto' )
2023-05-07 03:37:34 -07:00
const { Minipass } = require ( 'minipass' )
2017-05-09 14:46:02 -07:00
2023-04-07 11:52:14 -07:00
const SPEC _ALGORITHMS = [ 'sha512' , 'sha384' , 'sha256' ]
const DEFAULT _ALGORITHMS = [ 'sha512' ]
2017-05-09 14:46:02 -07:00
2020-10-02 17:52:19 -04:00
// TODO: this should really be a hardcoded list of algorithms we support,
// rather than [a-z0-9].
2017-05-09 14:46:02 -07:00
const BASE64 _REGEX = /^[a-z0-9+/]+(?:=?=?)$/i
2020-10-02 17:52:19 -04:00
const SRI _REGEX = /^([a-z0-9]+)-([^?]+)([?\S*]*)$/
2021-01-28 17:38:39 -05:00
const STRICT _SRI _REGEX = /^([a-z0-9]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)?$/
2017-05-09 14:46:02 -07:00
const VCHAR _REGEX = /^[\x21-\x7E]+$/
2023-04-07 11:52:14 -07:00
const getOptString = options => options ? . length ? ` ? ${ options . join ( '?' ) } ` : ''
2022-05-25 21:26:36 +00:00
2023-05-07 03:37:34 -07:00
class IntegrityStream extends Minipass {
2023-04-07 11:52:14 -07:00
# emittedIntegrity
# emittedSize
# emittedVerified
2020-10-02 17:52:19 -04:00
constructor ( opts ) {
super ( )
this . size = 0
this . opts = opts
// may be overridden later, but set now for class consistency
2023-04-07 11:52:14 -07:00
this . # getOptions ( )
2020-10-02 17:52:19 -04:00
// options used for calculating stream. can't be changed.
2023-05-07 03:37:34 -07:00
if ( opts ? . algorithms ) {
this . algorithms = [ ... opts . algorithms ]
} else {
this . algorithms = [ ... DEFAULT _ALGORITHMS ]
}
if ( this . algorithm !== null && ! this . algorithms . includes ( this . algorithm ) ) {
this . algorithms . push ( this . algorithm )
}
2020-10-02 17:52:19 -04:00
this . hashes = this . algorithms . map ( crypto . createHash )
}
2023-04-07 11:52:14 -07:00
# getOptions ( ) {
2020-10-02 17:52:19 -04:00
// For verification
2023-04-07 11:52:14 -07:00
this . sri = this . opts ? . integrity ? parse ( this . opts ? . integrity , this . opts ) : null
this . expectedSize = this . opts ? . size
2023-05-07 03:37:34 -07:00
if ( ! this . sri ) {
this . algorithm = null
} else if ( this . sri . isHash ) {
this . goodSri = true
this . algorithm = this . sri . algorithm
} else {
this . goodSri = ! this . sri . isEmpty ( )
this . algorithm = this . sri . pickAlgorithm ( this . opts )
}
2020-10-02 17:52:19 -04:00
this . digests = this . goodSri ? this . sri [ this . algorithm ] : null
2023-04-07 11:52:14 -07:00
this . optString = getOptString ( this . opts ? . options )
2020-10-02 17:52:19 -04:00
}
2022-05-25 21:26:36 +00:00
on ( ev , handler ) {
2023-04-07 11:52:14 -07:00
if ( ev === 'size' && this . # emittedSize ) {
return handler ( this . # emittedSize )
2022-05-25 21:26:36 +00:00
}
2023-04-07 11:52:14 -07:00
if ( ev === 'integrity' && this . # emittedIntegrity ) {
return handler ( this . # emittedIntegrity )
2022-05-25 21:26:36 +00:00
}
2023-04-07 11:52:14 -07:00
if ( ev === 'verified' && this . # emittedVerified ) {
return handler ( this . # emittedVerified )
2022-05-25 21:26:36 +00:00
}
return super . on ( ev , handler )
}
2020-10-02 17:52:19 -04:00
emit ( ev , data ) {
2022-04-14 21:57:02 +00:00
if ( ev === 'end' ) {
2023-04-07 11:52:14 -07:00
this . # onEnd ( )
2022-04-14 21:57:02 +00:00
}
2020-10-02 17:52:19 -04:00
return super . emit ( ev , data )
}
write ( data ) {
this . size += data . length
this . hashes . forEach ( h => h . update ( data ) )
return super . write ( data )
}
2023-04-07 11:52:14 -07:00
# onEnd ( ) {
2020-10-02 17:52:19 -04:00
if ( ! this . goodSri ) {
2023-04-07 11:52:14 -07:00
this . # getOptions ( )
2020-10-02 17:52:19 -04:00
}
const newSri = parse ( this . hashes . map ( ( h , i ) => {
return ` ${ this . algorithms [ i ] } - ${ h . digest ( 'base64' ) } ${ this . optString } `
} ) . join ( ' ' ) , this . opts )
// Integrity verification mode
const match = this . goodSri && newSri . match ( this . sri , this . opts )
if ( typeof this . expectedSize === 'number' && this . size !== this . expectedSize ) {
2022-04-14 21:57:02 +00:00
/* eslint-disable-next-line max-len */
2020-10-02 17:52:19 -04:00
const err = new Error ( ` stream size mismatch when checking ${ this . sri } . \n Wanted: ${ this . expectedSize } \n Found: ${ this . size } ` )
err . code = 'EBADSIZE'
err . found = this . size
err . expected = this . expectedSize
err . sri = this . sri
this . emit ( 'error' , err )
} else if ( this . sri && ! match ) {
2022-04-14 21:57:02 +00:00
/* eslint-disable-next-line max-len */
2020-10-02 17:52:19 -04:00
const err = new Error ( ` ${ this . sri } integrity checksum failed when using ${ this . algorithm } : wanted ${ this . digests } but got ${ newSri } . ( ${ this . size } bytes) ` )
err . code = 'EINTEGRITY'
err . found = newSri
err . expected = this . digests
err . algorithm = this . algorithm
err . sri = this . sri
this . emit ( 'error' , err )
} else {
2023-04-07 11:52:14 -07:00
this . # emittedSize = this . size
2020-10-02 17:52:19 -04:00
this . emit ( 'size' , this . size )
2023-04-07 11:52:14 -07:00
this . # emittedIntegrity = newSri
2020-10-02 17:52:19 -04:00
this . emit ( 'integrity' , newSri )
2022-05-25 21:26:36 +00:00
if ( match ) {
2023-04-07 11:52:14 -07:00
this . # emittedVerified = match
2022-05-25 21:26:36 +00:00
this . emit ( 'verified' , match )
}
2020-10-02 17:52:19 -04:00
}
}
}
2018-11-29 14:05:52 -08:00
2017-05-09 14:46:02 -07:00
class Hash {
2022-04-14 21:57:02 +00:00
get isHash ( ) {
return true
}
2017-05-09 14:46:02 -07:00
constructor ( hash , opts ) {
2023-04-07 11:52:14 -07:00
const strict = opts ? . strict
2017-05-09 14:46:02 -07:00
this . source = hash . trim ( )
2020-10-02 17:52:19 -04:00
// set default values so that we make V8 happy to
// always see a familiar object template.
this . digest = ''
this . algorithm = ''
this . options = [ ]
2017-05-09 14:46:02 -07:00
// 3.1. Integrity metadata (called "Hash" by ssri)
// https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description
const match = this . source . match (
strict
2020-10-02 17:52:19 -04:00
? STRICT _SRI _REGEX
: SRI _REGEX
2017-05-09 14:46:02 -07:00
)
2022-04-14 21:57:02 +00:00
if ( ! match ) {
return
}
2023-04-07 11:52:14 -07:00
if ( strict && ! SPEC _ALGORITHMS . includes ( match [ 1 ] ) ) {
2022-04-14 21:57:02 +00:00
return
}
2017-05-09 14:46:02 -07:00
this . algorithm = match [ 1 ]
this . digest = match [ 2 ]
const rawOpts = match [ 3 ]
2020-10-02 17:52:19 -04:00
if ( rawOpts ) {
this . options = rawOpts . slice ( 1 ) . split ( '?' )
}
2017-05-09 14:46:02 -07:00
}
2020-10-02 17:52:19 -04:00
2017-05-09 14:46:02 -07:00
hexDigest ( ) {
2017-06-05 16:31:14 -07:00
return this . digest && Buffer . from ( this . digest , 'base64' ) . toString ( 'hex' )
2017-05-09 14:46:02 -07:00
}
2020-10-02 17:52:19 -04:00
2017-05-09 14:46:02 -07:00
toJSON ( ) {
return this . toString ( )
}
2020-10-02 17:52:19 -04:00
2023-05-07 03:37:34 -07:00
match ( integrity , opts ) {
const other = parse ( integrity , opts )
if ( ! other ) {
return false
}
if ( other . isIntegrity ) {
const algo = other . pickAlgorithm ( opts , [ this . algorithm ] )
if ( ! algo ) {
return false
}
const foundHash = other [ algo ] . find ( hash => hash . digest === this . digest )
if ( foundHash ) {
return foundHash
}
return false
}
return other . digest === this . digest ? other : false
}
2017-05-09 14:46:02 -07:00
toString ( opts ) {
2023-04-07 11:52:14 -07:00
if ( opts ? . strict ) {
2017-05-09 14:46:02 -07:00
// Strict mode enforces the standard as close to the foot of the
// letter as it can.
if ( ! (
// The spec has very restricted productions for algorithms.
// https://www.w3.org/TR/CSP2/#source-list-syntax
2023-04-07 11:52:14 -07:00
SPEC _ALGORITHMS . includes ( this . algorithm ) &&
2017-05-09 14:46:02 -07:00
// Usually, if someone insists on using a "different" base64, we
// leave it as-is, since there's multiple standards, and the
// specified is not a URL-safe variant.
// https://www.w3.org/TR/CSP2/#base64_value
this . digest . match ( BASE64 _REGEX ) &&
// Option syntax is strictly visual chars.
// https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression
// https://tools.ietf.org/html/rfc5234#appendix-B.1
2020-10-02 17:52:19 -04:00
this . options . every ( opt => opt . match ( VCHAR _REGEX ) )
2017-05-09 14:46:02 -07:00
) ) {
return ''
}
}
2023-04-07 11:52:14 -07:00
return ` ${ this . algorithm } - ${ this . digest } ${ getOptString ( this . options ) } `
}
}
function integrityHashToString ( toString , sep , opts , hashes ) {
const toStringIsNotEmpty = toString !== ''
let shouldAddFirstSep = false
let complement = ''
const lastIndex = hashes . length - 1
for ( let i = 0 ; i < lastIndex ; i ++ ) {
const hashString = Hash . prototype . toString . call ( hashes [ i ] , opts )
if ( hashString ) {
shouldAddFirstSep = true
complement += hashString
complement += sep
}
}
const finalHashString = Hash . prototype . toString . call ( hashes [ lastIndex ] , opts )
if ( finalHashString ) {
shouldAddFirstSep = true
complement += finalHashString
2017-05-09 14:46:02 -07:00
}
2023-04-07 11:52:14 -07:00
if ( toStringIsNotEmpty && shouldAddFirstSep ) {
return toString + sep + complement
}
return toString + complement
2017-05-09 14:46:02 -07:00
}
class Integrity {
2022-04-14 21:57:02 +00:00
get isIntegrity ( ) {
return true
}
2017-05-09 14:46:02 -07:00
toJSON ( ) {
return this . toString ( )
}
2020-10-02 17:52:19 -04:00
isEmpty ( ) {
return Object . keys ( this ) . length === 0
}
2017-05-09 14:46:02 -07:00
toString ( opts ) {
2023-04-07 11:52:14 -07:00
let sep = opts ? . sep || ' '
let toString = ''
if ( opts ? . strict ) {
2017-05-09 14:46:02 -07:00
// Entries must be separated by whitespace, according to spec.
sep = sep . replace ( /\S+/g , ' ' )
2023-04-07 11:52:14 -07:00
for ( const hash of SPEC _ALGORITHMS ) {
if ( this [ hash ] ) {
toString = integrityHashToString ( toString , sep , opts , this [ hash ] )
}
}
} else {
for ( const hash of Object . keys ( this ) ) {
toString = integrityHashToString ( toString , sep , opts , this [ hash ] )
}
2017-05-09 14:46:02 -07:00
}
2023-04-07 11:52:14 -07:00
return toString
2017-05-09 14:46:02 -07:00
}
2020-10-02 17:52:19 -04:00
2017-05-09 14:46:02 -07:00
concat ( integrity , opts ) {
const other = typeof integrity === 'string'
2020-10-02 17:52:19 -04:00
? integrity
: stringify ( integrity , opts )
2017-05-09 14:46:02 -07:00
return parse ( ` ${ this . toString ( opts ) } ${ other } ` , opts )
}
2020-10-02 17:52:19 -04:00
2017-05-09 14:46:02 -07:00
hexDigest ( ) {
2020-10-02 17:52:19 -04:00
return parse ( this , { single : true } ) . hexDigest ( )
2017-05-09 14:46:02 -07:00
}
2020-10-02 17:52:19 -04:00
// add additional hashes to an integrity value, but prevent
// *changing* an existing integrity hash.
merge ( integrity , opts ) {
const other = parse ( integrity , opts )
for ( const algo in other ) {
if ( this [ algo ] ) {
if ( ! this [ algo ] . find ( hash =>
other [ algo ] . find ( otherhash =>
hash . digest === otherhash . digest ) ) ) {
throw new Error ( 'hashes do not match, cannot update integrity' )
}
} else {
this [ algo ] = other [ algo ]
}
}
}
2018-04-20 18:26:37 -07:00
match ( integrity , opts ) {
const other = parse ( integrity , opts )
2022-12-06 22:18:33 -05:00
if ( ! other ) {
return false
}
2023-05-07 03:37:34 -07:00
const algo = other . pickAlgorithm ( opts , Object . keys ( this ) )
2018-04-20 18:26:37 -07:00
return (
2023-05-07 03:37:34 -07:00
! ! algo &&
2018-04-20 18:26:37 -07:00
this [ algo ] &&
other [ algo ] &&
this [ algo ] . find ( hash =>
other [ algo ] . find ( otherhash =>
hash . digest === otherhash . digest
)
)
) || false
}
2020-10-02 17:52:19 -04:00
2023-05-07 03:37:34 -07:00
// Pick the highest priority algorithm present, optionally also limited to a
// set of hashes found in another integrity. When limiting it may return
// nothing.
pickAlgorithm ( opts , hashes ) {
2023-04-07 11:52:14 -07:00
const pickAlgorithm = opts ? . pickAlgorithm || getPrioritizedHash
2023-05-07 03:37:34 -07:00
const keys = Object . keys ( this ) . filter ( k => {
if ( hashes ? . length ) {
return hashes . includes ( k )
}
return true
2017-05-09 14:46:02 -07:00
} )
2023-05-07 03:37:34 -07:00
if ( keys . length ) {
return keys . reduce ( ( acc , algo ) => pickAlgorithm ( acc , algo ) || acc )
}
// no intersection between this and hashes,
return null
2017-05-09 14:46:02 -07:00
}
}
module . exports . parse = parse
function parse ( sri , opts ) {
2022-04-14 21:57:02 +00:00
if ( ! sri ) {
return null
}
2017-05-09 14:46:02 -07:00
if ( typeof sri === 'string' ) {
return _parse ( sri , opts )
} else if ( sri . algorithm && sri . digest ) {
const fullSri = new Integrity ( )
fullSri [ sri . algorithm ] = [ sri ]
return _parse ( stringify ( fullSri , opts ) , opts )
} else {
return _parse ( stringify ( sri , opts ) , opts )
}
}
function _parse ( integrity , opts ) {
// 3.4.3. Parse metadata
// https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
2023-04-07 11:52:14 -07:00
if ( opts ? . single ) {
2017-05-09 14:46:02 -07:00
return new Hash ( integrity , opts )
}
2020-10-02 17:52:19 -04:00
const hashes = integrity . trim ( ) . split ( /\s+/ ) . reduce ( ( acc , string ) => {
2017-05-09 14:46:02 -07:00
const hash = new Hash ( string , opts )
if ( hash . algorithm && hash . digest ) {
const algo = hash . algorithm
2022-04-14 21:57:02 +00:00
if ( ! acc [ algo ] ) {
acc [ algo ] = [ ]
}
2017-05-09 14:46:02 -07:00
acc [ algo ] . push ( hash )
}
return acc
} , new Integrity ( ) )
2020-10-02 17:52:19 -04:00
return hashes . isEmpty ( ) ? null : hashes
2017-05-09 14:46:02 -07:00
}
module . exports . stringify = stringify
function stringify ( obj , opts ) {
if ( obj . algorithm && obj . digest ) {
return Hash . prototype . toString . call ( obj , opts )
} else if ( typeof obj === 'string' ) {
return stringify ( parse ( obj , opts ) , opts )
} else {
return Integrity . prototype . toString . call ( obj , opts )
}
}
module . exports . fromHex = fromHex
function fromHex ( hexDigest , algorithm , opts ) {
2023-04-07 11:52:14 -07:00
const optString = getOptString ( opts ? . options )
2017-05-09 14:46:02 -07:00
return parse (
` ${ algorithm } - ${
2017-06-05 16:31:14 -07:00
Buffer . from ( hexDigest , 'hex' ) . toString ( 'base64' )
2017-05-09 14:46:02 -07:00
} $ { optString } ` , opts
)
}
module . exports . fromData = fromData
function fromData ( data , opts ) {
2023-05-07 03:37:34 -07:00
const algorithms = opts ? . algorithms || [ ... DEFAULT _ALGORITHMS ]
2023-04-07 11:52:14 -07:00
const optString = getOptString ( opts ? . options )
2017-05-09 14:46:02 -07:00
return algorithms . reduce ( ( acc , algo ) => {
const digest = crypto . createHash ( algo ) . update ( data ) . digest ( 'base64' )
const hash = new Hash (
` ${ algo } - ${ digest } ${ optString } ` ,
2020-10-02 17:52:19 -04:00
opts
2017-05-09 14:46:02 -07:00
)
2020-10-02 17:52:19 -04:00
/ * i s t a n b u l i g n o r e e l s e - i t w o u l d b e V E R Y s t r a n g e i f t h e s t r i n g w e
* just calculated with an algo did not have an algo or digest .
* /
2017-05-09 14:46:02 -07:00
if ( hash . algorithm && hash . digest ) {
2022-04-14 21:57:02 +00:00
const hashAlgo = hash . algorithm
if ( ! acc [ hashAlgo ] ) {
acc [ hashAlgo ] = [ ]
}
acc [ hashAlgo ] . push ( hash )
2017-05-09 14:46:02 -07:00
}
return acc
} , new Integrity ( ) )
}
module . exports . fromStream = fromStream
function fromStream ( stream , opts ) {
const istream = integrityStream ( opts )
2020-10-02 17:52:19 -04:00
return new Promise ( ( resolve , reject ) => {
2017-05-09 14:46:02 -07:00
stream . pipe ( istream )
stream . on ( 'error' , reject )
istream . on ( 'error' , reject )
let sri
2022-04-14 21:57:02 +00:00
istream . on ( 'integrity' , s => {
sri = s
} )
2017-05-09 14:46:02 -07:00
istream . on ( 'end' , ( ) => resolve ( sri ) )
2023-05-07 03:37:34 -07:00
istream . resume ( )
2017-05-09 14:46:02 -07:00
} )
}
module . exports . checkData = checkData
function checkData ( data , sri , opts ) {
sri = parse ( sri , opts )
2020-10-02 17:52:19 -04:00
if ( ! sri || ! Object . keys ( sri ) . length ) {
2023-04-07 11:52:14 -07:00
if ( opts ? . error ) {
2018-04-20 18:26:37 -07:00
throw Object . assign (
new Error ( 'No valid integrity hashes to check against' ) , {
2022-04-14 21:57:02 +00:00
code : 'EINTEGRITY' ,
2018-04-20 18:26:37 -07:00
}
)
} else {
return false
}
}
2017-05-09 14:46:02 -07:00
const algorithm = sri . pickAlgorithm ( opts )
const digest = crypto . createHash ( algorithm ) . update ( data ) . digest ( 'base64' )
2020-10-02 17:52:19 -04:00
const newSri = parse ( { algorithm , digest } )
2018-04-20 18:26:37 -07:00
const match = newSri . match ( sri , opts )
2023-04-07 11:52:14 -07:00
opts = opts || { }
if ( match || ! ( opts . error ) ) {
2018-04-20 18:26:37 -07:00
return match
} else if ( typeof opts . size === 'number' && ( data . length !== opts . size ) ) {
2022-04-14 21:57:02 +00:00
/* eslint-disable-next-line max-len */
2018-04-20 18:26:37 -07:00
const err = new Error ( ` data size mismatch when checking ${ sri } . \n Wanted: ${ opts . size } \n Found: ${ data . length } ` )
err . code = 'EBADSIZE'
err . found = data . length
err . expected = opts . size
err . sri = sri
throw err
} else {
2022-04-14 21:57:02 +00:00
/* eslint-disable-next-line max-len */
2018-04-20 18:26:37 -07:00
const err = new Error ( ` Integrity checksum failed when using ${ algorithm } : Wanted ${ sri } , but got ${ newSri } . ( ${ data . length } bytes) ` )
err . code = 'EINTEGRITY'
err . found = newSri
err . expected = sri
err . algorithm = algorithm
err . sri = sri
throw err
}
2017-05-09 14:46:02 -07:00
}
module . exports . checkStream = checkStream
function checkStream ( stream , sri , opts ) {
2023-04-07 11:52:14 -07:00
opts = opts || Object . create ( null )
2020-10-02 17:52:19 -04:00
opts . integrity = sri
sri = parse ( sri , opts )
if ( ! sri || ! Object . keys ( sri ) . length ) {
return Promise . reject ( Object . assign (
new Error ( 'No valid integrity hashes to check against' ) , {
2022-04-14 21:57:02 +00:00
code : 'EINTEGRITY' ,
2020-10-02 17:52:19 -04:00
}
) )
}
const checker = integrityStream ( opts )
return new Promise ( ( resolve , reject ) => {
2017-05-09 14:46:02 -07:00
stream . pipe ( checker )
stream . on ( 'error' , reject )
checker . on ( 'error' , reject )
2022-04-14 21:57:02 +00:00
let verified
checker . on ( 'verified' , s => {
verified = s
} )
checker . on ( 'end' , ( ) => resolve ( verified ) )
2023-05-07 03:37:34 -07:00
checker . resume ( )
2017-05-09 14:46:02 -07:00
} )
}
module . exports . integrityStream = integrityStream
2023-04-07 11:52:14 -07:00
function integrityStream ( opts = Object . create ( null ) ) {
2020-10-02 17:52:19 -04:00
return new IntegrityStream ( opts )
2017-05-09 14:46:02 -07:00
}
module . exports . create = createIntegrity
function createIntegrity ( opts ) {
2023-05-07 03:37:34 -07:00
const algorithms = opts ? . algorithms || [ ... DEFAULT _ALGORITHMS ]
2023-04-07 11:52:14 -07:00
const optString = getOptString ( opts ? . options )
2017-05-09 14:46:02 -07:00
const hashes = algorithms . map ( crypto . createHash )
return {
update : function ( chunk , enc ) {
hashes . forEach ( h => h . update ( chunk , enc ) )
return this
} ,
2024-05-16 05:38:49 -07:00
digest : function ( ) {
2017-05-09 14:46:02 -07:00
const integrity = algorithms . reduce ( ( acc , algo ) => {
const digest = hashes . shift ( ) . digest ( 'base64' )
const hash = new Hash (
` ${ algo } - ${ digest } ${ optString } ` ,
opts
)
2020-10-02 17:52:19 -04:00
/ * i s t a n b u l i g n o r e e l s e - i t w o u l d b e V E R Y s t r a n g e i f t h e h a s h w e
* just calculated with an algo did not have an algo or digest .
* /
2017-05-09 14:46:02 -07:00
if ( hash . algorithm && hash . digest ) {
2022-04-14 21:57:02 +00:00
const hashAlgo = hash . algorithm
if ( ! acc [ hashAlgo ] ) {
acc [ hashAlgo ] = [ ]
}
acc [ hashAlgo ] . push ( hash )
2017-05-09 14:46:02 -07:00
}
return acc
} , new Integrity ( ) )
return integrity
2022-04-14 21:57:02 +00:00
} ,
2017-05-09 14:46:02 -07:00
}
}
2023-05-07 03:37:34 -07:00
const NODE _HASHES = crypto . getHashes ( )
2018-04-20 18:26:37 -07:00
2017-05-09 14:46:02 -07:00
// This is a Best Effort™ at a reasonable priority for hash algos
const DEFAULT _PRIORITY = [
2018-04-20 18:26:37 -07:00
'md5' , 'whirlpool' , 'sha1' , 'sha224' , 'sha256' , 'sha384' , 'sha512' ,
// TODO - it's unclear _which_ of these Node will actually use as its name
// for the algorithm, so we guesswork it based on the OpenSSL names.
'sha3' ,
'sha3-256' , 'sha3-384' , 'sha3-512' ,
2022-04-14 21:57:02 +00:00
'sha3_256' , 'sha3_384' , 'sha3_512' ,
2023-05-07 03:37:34 -07:00
] . filter ( algo => NODE _HASHES . includes ( algo ) )
2018-04-20 18:26:37 -07:00
2017-05-09 14:46:02 -07:00
function getPrioritizedHash ( algo1 , algo2 ) {
2022-04-14 21:57:02 +00:00
/* eslint-disable-next-line max-len */
2017-05-09 14:46:02 -07:00
return DEFAULT _PRIORITY . indexOf ( algo1 . toLowerCase ( ) ) >= DEFAULT _PRIORITY . indexOf ( algo2 . toLowerCase ( ) )
2020-10-02 17:52:19 -04:00
? algo1
: algo2
2017-05-09 14:46:02 -07:00
}