react / wstein / node_modules / jest-cli / node_modules / jsdom / node_modules / request / node_modules / http-signature / node_modules / asn1 / lib / ber / writer.js
81159 views// Copyright 2011 Mark Cavage <[email protected]> All rights reserved.12var assert = require('assert');3var ASN1 = require('./types');4var errors = require('./errors');567///--- Globals89var newInvalidAsn1Error = errors.newInvalidAsn1Error;1011var DEFAULT_OPTS = {12size: 1024,13growthFactor: 814};151617///--- Helpers1819function merge(from, to) {20assert.ok(from);21assert.equal(typeof(from), 'object');22assert.ok(to);23assert.equal(typeof(to), 'object');2425var keys = Object.getOwnPropertyNames(from);26keys.forEach(function(key) {27if (to[key])28return;2930var value = Object.getOwnPropertyDescriptor(from, key);31Object.defineProperty(to, key, value);32});3334return to;35}36373839///--- API4041function Writer(options) {42options = merge(DEFAULT_OPTS, options || {});4344this._buf = new Buffer(options.size || 1024);45this._size = this._buf.length;46this._offset = 0;47this._options = options;4849// A list of offsets in the buffer where we need to insert50// sequence tag/len pairs.51this._seq = [];5253var self = this;54this.__defineGetter__('buffer', function() {55if (self._seq.length)56throw new InvalidAsn1Error(self._seq.length + ' unended sequence(s)');5758return self._buf.slice(0, self._offset);59});60}616263Writer.prototype.writeByte = function(b) {64if (typeof(b) !== 'number')65throw new TypeError('argument must be a Number');6667this._ensure(1);68this._buf[this._offset++] = b;69};707172Writer.prototype.writeInt = function(i, tag) {73if (typeof(i) !== 'number')74throw new TypeError('argument must be a Number');75if (typeof(tag) !== 'number')76tag = ASN1.Integer;7778var sz = 4;7980while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) &&81(sz > 1)) {82sz--;83i <<= 8;84}8586if (sz > 4)87throw new InvalidAsn1Error('BER ints cannot be > 0xffffffff');8889this._ensure(2 + sz);90this._buf[this._offset++] = tag;91this._buf[this._offset++] = sz;9293while (sz-- > 0) {94this._buf[this._offset++] = ((i & 0xff000000) >> 24);95i <<= 8;96}9798};99100101Writer.prototype.writeNull = function() {102this.writeByte(ASN1.Null);103this.writeByte(0x00);104};105106107Writer.prototype.writeEnumeration = function(i, tag) {108if (typeof(i) !== 'number')109throw new TypeError('argument must be a Number');110if (typeof(tag) !== 'number')111tag = ASN1.Enumeration;112113return this.writeInt(i, tag);114};115116117Writer.prototype.writeBoolean = function(b, tag) {118if (typeof(b) !== 'boolean')119throw new TypeError('argument must be a Boolean');120if (typeof(tag) !== 'number')121tag = ASN1.Boolean;122123this._ensure(3);124this._buf[this._offset++] = tag;125this._buf[this._offset++] = 0x01;126this._buf[this._offset++] = b ? 0xff : 0x00;127};128129130Writer.prototype.writeString = function(s, tag) {131if (typeof(s) !== 'string')132throw new TypeError('argument must be a string (was: ' + typeof(s) + ')');133if (typeof(tag) !== 'number')134tag = ASN1.OctetString;135136var len = Buffer.byteLength(s);137this.writeByte(tag);138this.writeLength(len);139if (len) {140this._ensure(len);141this._buf.write(s, this._offset);142this._offset += len;143}144};145146147Writer.prototype.writeBuffer = function(buf, tag) {148if (typeof(tag) !== 'number')149throw new TypeError('tag must be a number');150if (!Buffer.isBuffer(buf))151throw new TypeError('argument must be a buffer');152153this.writeByte(tag);154this.writeLength(buf.length);155this._ensure(buf.length);156buf.copy(this._buf, this._offset, 0, buf.length);157this._offset += buf.length;158};159160161Writer.prototype.writeStringArray = function(strings) {162if ((!strings instanceof Array))163throw new TypeError('argument must be an Array[String]');164165var self = this;166strings.forEach(function(s) {167self.writeString(s);168});169};170171// This is really to solve DER cases, but whatever for now172Writer.prototype.writeOID = function(s, tag) {173if (typeof(s) !== 'string')174throw new TypeError('argument must be a string');175if (typeof(tag) !== 'number')176tag = ASN1.OID;177178if (!/^([0-9]+\.){3,}[0-9]+$/.test(s))179throw new Error('argument is not a valid OID string');180181function encodeOctet(bytes, octet) {182if (octet < 128) {183bytes.push(octet);184} else if (octet < 16384) {185bytes.push((octet >>> 7) | 0x80);186bytes.push(octet & 0x7F);187} else if (octet < 2097152) {188bytes.push((octet >>> 14) | 0x80);189bytes.push(((octet >>> 7) | 0x80) & 0xFF);190bytes.push(octet & 0x7F);191} else if (octet < 268435456) {192bytes.push((octet >>> 21) | 0x80);193bytes.push(((octet >>> 14) | 0x80) & 0xFF);194bytes.push(((octet >>> 7) | 0x80) & 0xFF);195bytes.push(octet & 0x7F);196} else {197bytes.push(((octet >>> 28) | 0x80) & 0xFF);198bytes.push(((octet >>> 21) | 0x80) & 0xFF);199bytes.push(((octet >>> 14) | 0x80) & 0xFF);200bytes.push(((octet >>> 7) | 0x80) & 0xFF);201bytes.push(octet & 0x7F);202}203}204205var tmp = s.split('.');206var bytes = [];207bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10));208tmp.slice(2).forEach(function(b) {209encodeOctet(bytes, parseInt(b, 10));210});211212var self = this;213this._ensure(2 + bytes.length);214this.writeByte(tag);215this.writeLength(bytes.length);216bytes.forEach(function(b) {217self.writeByte(b);218});219};220221222Writer.prototype.writeLength = function(len) {223if (typeof(len) !== 'number')224throw new TypeError('argument must be a Number');225226this._ensure(4);227228if (len <= 0x7f) {229this._buf[this._offset++] = len;230} else if (len <= 0xff) {231this._buf[this._offset++] = 0x81;232this._buf[this._offset++] = len;233} else if (len <= 0xffff) {234this._buf[this._offset++] = 0x82;235this._buf[this._offset++] = len >> 8;236this._buf[this._offset++] = len;237} else if (len <= 0xffffff) {238this._shift(start, len, 1);239this._buf[this._offset++] = 0x83;240this._buf[this._offset++] = len >> 16;241this._buf[this._offset++] = len >> 8;242this._buf[this._offset++] = len;243} else {244throw new InvalidAsn1ERror('Length too long (> 4 bytes)');245}246};247248Writer.prototype.startSequence = function(tag) {249if (typeof(tag) !== 'number')250tag = ASN1.Sequence | ASN1.Constructor;251252this.writeByte(tag);253this._seq.push(this._offset);254this._ensure(3);255this._offset += 3;256};257258259Writer.prototype.endSequence = function() {260var seq = this._seq.pop();261var start = seq + 3;262var len = this._offset - start;263264if (len <= 0x7f) {265this._shift(start, len, -2);266this._buf[seq] = len;267} else if (len <= 0xff) {268this._shift(start, len, -1);269this._buf[seq] = 0x81;270this._buf[seq + 1] = len;271} else if (len <= 0xffff) {272this._buf[seq] = 0x82;273this._buf[seq + 1] = len >> 8;274this._buf[seq + 2] = len;275} else if (len <= 0xffffff) {276this._shift(start, len, 1);277this._buf[seq] = 0x83;278this._buf[seq + 1] = len >> 16;279this._buf[seq + 2] = len >> 8;280this._buf[seq + 3] = len;281} else {282throw new InvalidAsn1Error('Sequence too long');283}284};285286287Writer.prototype._shift = function(start, len, shift) {288assert.ok(start !== undefined);289assert.ok(len !== undefined);290assert.ok(shift);291292this._buf.copy(this._buf, start + shift, start, start + len);293this._offset += shift;294};295296Writer.prototype._ensure = function(len) {297assert.ok(len);298299if (this._size - this._offset < len) {300var sz = this._size * this._options.growthFactor;301if (sz - this._offset < len)302sz += len;303304var buf = new Buffer(sz);305306this._buf.copy(buf, 0, 0, this._offset);307this._buf = buf;308this._size = sz;309}310};311312313314///--- Exported API315316module.exports = Writer;317318319