UNPKG

email-dkim-utils

Version:

Email Utils - DKIM, compose with dkim, etc...

183 lines (148 loc) 4.49 kB
/** * Signature Constructor * @return {Signature} */ function Signature( options ) { if( !(this instanceof Signature) ) return new Signature( options ) // Signing algorithm this.algorithm = 'rsa-sha256' // Headers canonicalization / normalization type this.canonical = 'simple' this.copiedHeaders = [] // Signing domain this.domain = null this.expires = null this.hash = null // Headers this.headers = [] this.identity = null this.length = null this.query = 'dns/txt' this.selector = null // DKIM Signature data (base64) this.signature = null this.timestamp = null // DKIM version this.version = '1' var self = this if( options != null ) { Object.keys( options ).forEach( function( k, v ) { if( Signature.fields.indexOf( k ) !== -1 ) { self[ k ] = options[ k ] } }) } } Signature.fields = [ 'version', 'algorithm', 'canonical', 'copiedHeaders', 'domain', 'expires', 'hash', 'headers', 'identity', 'length', 'selector', 'signature', 'timestamp', ] Signature.keys = [ 'v', // version 'a', // algorithm 'c', // canonical 'z', // copiedHeaders 'd', // domain 'x', // expires 'bh', // body hash 'h', // headers 'i', // identity 'l', // length 's', // selector 'b', // signature 't', // timestamp ] Signature.fieldMap = Signature.keys.reduce( function( map, key, i ) { map[ key ] = Signature.fields[ i ] return map }, {}) Signature.create = function( options ) { return new Signature( options ) } Signature.parse = function( dkimHeader ) { return new Signature().parse( dkimHeader ) } function splitArray( value ) { var list = [] var offset = 0 var delimiterOffset = -1 while( offset < value.length ) { delimiterOffset = value.indexOf( ':', offset ) delimiterOffset = delimiterOffset !== -1 ? delimiterOffset : value.length list.push( value.slice( offset, delimiterOffset ).trim() ) offset = delimiterOffset + 1 } return list } /** * Signature Prototype * @type {Object} */ Signature.prototype = { constructor: Signature, /** * Parse a DKIM Signature from a String or Buffer * @param {String|Buffer} input * @returns {Signature} */ parse( input ) { var value = ( input + '' ).replace( /\r?\n\s/g, '' ) var offset = 0 var assignOffset = -1 var delimiterOffset = -1 var field = '' var fieldName = '' var fieldValue = '' while( offset < value.length ) { if( /\s/.test( value[ offset ] ) ) { offset++ continue } assignOffset = value.indexOf( '=', offset + 1 ) field = value.slice( offset, assignOffset ) fieldName = Signature.fieldMap[ field ] delimiterOffset = value.indexOf( ';', assignOffset + 1 ) fieldValue = value.slice( assignOffset + 1, delimiterOffset !== -1 ? delimiterOffset : undefined ) if( fieldName === 'signature' || fieldName === 'hash' ) { this[ fieldName ] = Buffer.from( fieldValue, 'base64' ) } else if( fieldName === 'headers' || fieldName === 'copiedHeaders' ) { this[ fieldName ] = splitArray( fieldValue ) } else { this[ fieldName ] = fieldValue } offset = delimiterOffset !== -1 ? delimiterOffset + 1 : value.length } return this }, toString() { var self = this return Signature.fields.map( function( field, i ) { if( typeof self[ field ] === 'string' || typeof self[ field ] === 'number' ) { return Signature.keys[ i ] + '=' + self[ field ] } else if( Array.isArray( self[ field ] ) && self[ field ].length ) { return Signature.keys[ i ] + '=' + self[ field ].join( ':' ) } else if( Buffer.isBuffer( self[ field ] ) && self[ field ].length ) { return Signature.keys[ i ] + '=' + self[ field ].toString( 'base64' ) } }) .filter( function( field ) { return field != null }) .join( '; ' ) } } // Exports module.exports = Signature