| 'use strict'; | 
|   | 
| const inherits = require('inherits'); | 
| const Buffer = require('safer-buffer').Buffer; | 
| const Node = require('../base/node'); | 
|   | 
| // Import DER constants | 
| const der = require('../constants/der'); | 
|   | 
| function DEREncoder(entity) { | 
|   this.enc = 'der'; | 
|   this.name = entity.name; | 
|   this.entity = entity; | 
|   | 
|   // Construct base tree | 
|   this.tree = new DERNode(); | 
|   this.tree._init(entity.body); | 
| } | 
| module.exports = DEREncoder; | 
|   | 
| DEREncoder.prototype.encode = function encode(data, reporter) { | 
|   return this.tree._encode(data, reporter).join(); | 
| }; | 
|   | 
| // Tree methods | 
|   | 
| function DERNode(parent) { | 
|   Node.call(this, 'der', parent); | 
| } | 
| inherits(DERNode, Node); | 
|   | 
| DERNode.prototype._encodeComposite = function encodeComposite(tag, | 
|   primitive, | 
|   cls, | 
|   content) { | 
|   const encodedTag = encodeTag(tag, primitive, cls, this.reporter); | 
|   | 
|   // Short form | 
|   if (content.length < 0x80) { | 
|     const header = Buffer.alloc(2); | 
|     header[0] = encodedTag; | 
|     header[1] = content.length; | 
|     return this._createEncoderBuffer([ header, content ]); | 
|   } | 
|   | 
|   // Long form | 
|   // Count octets required to store length | 
|   let lenOctets = 1; | 
|   for (let i = content.length; i >= 0x100; i >>= 8) | 
|     lenOctets++; | 
|   | 
|   const header = Buffer.alloc(1 + 1 + lenOctets); | 
|   header[0] = encodedTag; | 
|   header[1] = 0x80 | lenOctets; | 
|   | 
|   for (let i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8) | 
|     header[i] = j & 0xff; | 
|   | 
|   return this._createEncoderBuffer([ header, content ]); | 
| }; | 
|   | 
| DERNode.prototype._encodeStr = function encodeStr(str, tag) { | 
|   if (tag === 'bitstr') { | 
|     return this._createEncoderBuffer([ str.unused | 0, str.data ]); | 
|   } else if (tag === 'bmpstr') { | 
|     const buf = Buffer.alloc(str.length * 2); | 
|     for (let i = 0; i < str.length; i++) { | 
|       buf.writeUInt16BE(str.charCodeAt(i), i * 2); | 
|     } | 
|     return this._createEncoderBuffer(buf); | 
|   } else if (tag === 'numstr') { | 
|     if (!this._isNumstr(str)) { | 
|       return this.reporter.error('Encoding of string type: numstr supports ' + | 
|                                  'only digits and space'); | 
|     } | 
|     return this._createEncoderBuffer(str); | 
|   } else if (tag === 'printstr') { | 
|     if (!this._isPrintstr(str)) { | 
|       return this.reporter.error('Encoding of string type: printstr supports ' + | 
|                                  'only latin upper and lower case letters, ' + | 
|                                  'digits, space, apostrophe, left and rigth ' + | 
|                                  'parenthesis, plus sign, comma, hyphen, ' + | 
|                                  'dot, slash, colon, equal sign, ' + | 
|                                  'question mark'); | 
|     } | 
|     return this._createEncoderBuffer(str); | 
|   } else if (/str$/.test(tag)) { | 
|     return this._createEncoderBuffer(str); | 
|   } else if (tag === 'objDesc') { | 
|     return this._createEncoderBuffer(str); | 
|   } else { | 
|     return this.reporter.error('Encoding of string type: ' + tag + | 
|                                ' unsupported'); | 
|   } | 
| }; | 
|   | 
| DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) { | 
|   if (typeof id === 'string') { | 
|     if (!values) | 
|       return this.reporter.error('string objid given, but no values map found'); | 
|     if (!values.hasOwnProperty(id)) | 
|       return this.reporter.error('objid not found in values map'); | 
|     id = values[id].split(/[\s.]+/g); | 
|     for (let i = 0; i < id.length; i++) | 
|       id[i] |= 0; | 
|   } else if (Array.isArray(id)) { | 
|     id = id.slice(); | 
|     for (let i = 0; i < id.length; i++) | 
|       id[i] |= 0; | 
|   } | 
|   | 
|   if (!Array.isArray(id)) { | 
|     return this.reporter.error('objid() should be either array or string, ' + | 
|                                'got: ' + JSON.stringify(id)); | 
|   } | 
|   | 
|   if (!relative) { | 
|     if (id[1] >= 40) | 
|       return this.reporter.error('Second objid identifier OOB'); | 
|     id.splice(0, 2, id[0] * 40 + id[1]); | 
|   } | 
|   | 
|   // Count number of octets | 
|   let size = 0; | 
|   for (let i = 0; i < id.length; i++) { | 
|     let ident = id[i]; | 
|     for (size++; ident >= 0x80; ident >>= 7) | 
|       size++; | 
|   } | 
|   | 
|   const objid = Buffer.alloc(size); | 
|   let offset = objid.length - 1; | 
|   for (let i = id.length - 1; i >= 0; i--) { | 
|     let ident = id[i]; | 
|     objid[offset--] = ident & 0x7f; | 
|     while ((ident >>= 7) > 0) | 
|       objid[offset--] = 0x80 | (ident & 0x7f); | 
|   } | 
|   | 
|   return this._createEncoderBuffer(objid); | 
| }; | 
|   | 
| function two(num) { | 
|   if (num < 10) | 
|     return '0' + num; | 
|   else | 
|     return num; | 
| } | 
|   | 
| DERNode.prototype._encodeTime = function encodeTime(time, tag) { | 
|   let str; | 
|   const date = new Date(time); | 
|   | 
|   if (tag === 'gentime') { | 
|     str = [ | 
|       two(date.getUTCFullYear()), | 
|       two(date.getUTCMonth() + 1), | 
|       two(date.getUTCDate()), | 
|       two(date.getUTCHours()), | 
|       two(date.getUTCMinutes()), | 
|       two(date.getUTCSeconds()), | 
|       'Z' | 
|     ].join(''); | 
|   } else if (tag === 'utctime') { | 
|     str = [ | 
|       two(date.getUTCFullYear() % 100), | 
|       two(date.getUTCMonth() + 1), | 
|       two(date.getUTCDate()), | 
|       two(date.getUTCHours()), | 
|       two(date.getUTCMinutes()), | 
|       two(date.getUTCSeconds()), | 
|       'Z' | 
|     ].join(''); | 
|   } else { | 
|     this.reporter.error('Encoding ' + tag + ' time is not supported yet'); | 
|   } | 
|   | 
|   return this._encodeStr(str, 'octstr'); | 
| }; | 
|   | 
| DERNode.prototype._encodeNull = function encodeNull() { | 
|   return this._createEncoderBuffer(''); | 
| }; | 
|   | 
| DERNode.prototype._encodeInt = function encodeInt(num, values) { | 
|   if (typeof num === 'string') { | 
|     if (!values) | 
|       return this.reporter.error('String int or enum given, but no values map'); | 
|     if (!values.hasOwnProperty(num)) { | 
|       return this.reporter.error('Values map doesn\'t contain: ' + | 
|                                  JSON.stringify(num)); | 
|     } | 
|     num = values[num]; | 
|   } | 
|   | 
|   // Bignum, assume big endian | 
|   if (typeof num !== 'number' && !Buffer.isBuffer(num)) { | 
|     const numArray = num.toArray(); | 
|     if (!num.sign && numArray[0] & 0x80) { | 
|       numArray.unshift(0); | 
|     } | 
|     num = Buffer.from(numArray); | 
|   } | 
|   | 
|   if (Buffer.isBuffer(num)) { | 
|     let size = num.length; | 
|     if (num.length === 0) | 
|       size++; | 
|   | 
|     const out = Buffer.alloc(size); | 
|     num.copy(out); | 
|     if (num.length === 0) | 
|       out[0] = 0; | 
|     return this._createEncoderBuffer(out); | 
|   } | 
|   | 
|   if (num < 0x80) | 
|     return this._createEncoderBuffer(num); | 
|   | 
|   if (num < 0x100) | 
|     return this._createEncoderBuffer([0, num]); | 
|   | 
|   let size = 1; | 
|   for (let i = num; i >= 0x100; i >>= 8) | 
|     size++; | 
|   | 
|   const out = new Array(size); | 
|   for (let i = out.length - 1; i >= 0; i--) { | 
|     out[i] = num & 0xff; | 
|     num >>= 8; | 
|   } | 
|   if(out[0] & 0x80) { | 
|     out.unshift(0); | 
|   } | 
|   | 
|   return this._createEncoderBuffer(Buffer.from(out)); | 
| }; | 
|   | 
| DERNode.prototype._encodeBool = function encodeBool(value) { | 
|   return this._createEncoderBuffer(value ? 0xff : 0); | 
| }; | 
|   | 
| DERNode.prototype._use = function use(entity, obj) { | 
|   if (typeof entity === 'function') | 
|     entity = entity(obj); | 
|   return entity._getEncoder('der').tree; | 
| }; | 
|   | 
| DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) { | 
|   const state = this._baseState; | 
|   let i; | 
|   if (state['default'] === null) | 
|     return false; | 
|   | 
|   const data = dataBuffer.join(); | 
|   if (state.defaultBuffer === undefined) | 
|     state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join(); | 
|   | 
|   if (data.length !== state.defaultBuffer.length) | 
|     return false; | 
|   | 
|   for (i=0; i < data.length; i++) | 
|     if (data[i] !== state.defaultBuffer[i]) | 
|       return false; | 
|   | 
|   return true; | 
| }; | 
|   | 
| // Utility methods | 
|   | 
| function encodeTag(tag, primitive, cls, reporter) { | 
|   let res; | 
|   | 
|   if (tag === 'seqof') | 
|     tag = 'seq'; | 
|   else if (tag === 'setof') | 
|     tag = 'set'; | 
|   | 
|   if (der.tagByName.hasOwnProperty(tag)) | 
|     res = der.tagByName[tag]; | 
|   else if (typeof tag === 'number' && (tag | 0) === tag) | 
|     res = tag; | 
|   else | 
|     return reporter.error('Unknown tag: ' + tag); | 
|   | 
|   if (res >= 0x1f) | 
|     return reporter.error('Multi-octet tag encoding unsupported'); | 
|   | 
|   if (!primitive) | 
|     res |= 0x20; | 
|   | 
|   res |= (der.tagClassByName[cls || 'universal'] << 6); | 
|   | 
|   return res; | 
| } |