UNPKG

node-jose-browserify

Version:

An advanced version of Cisco's node-jose module that works in both browser and the server.

475 lines (427 loc) 12 kB
/*! * util/databuffer.js - Forge-compatible Buffer based on Node.js Buffers * * Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file. */ "use strict"; var forge = require("../deps/forge.js"), base64url = require("./base64url.js"); /** * */ function DataBuffer(b, options) { options = options || {}; // treat (views of) (Array)Buffers special // NOTE: default implementation creates copies, but efficiently // wherever possible if (Buffer.isBuffer(b)) { this.data = b; } else if (forge.util.isArrayBuffer(b)) { b = new Uint8Array(b); this.data = Buffer.from(b); } else if (forge.util.isArrayBufferView(b)) { b = new Uint8Array(b.buffer, b.byteOffset, b.byteLength); this.data = Buffer.from(b); } if (this.data) { this.write = this.data.length; b = undefined; } // setup growth rate this.growSize = options.growSize || DataBuffer.DEFAULT_GROW_SIZE; // initialize pointers and data this.write = this.write || 0; this.read = this.read || 0; if (b) { this.putBytes(b); } else if (!this.data) { this.accommodate(0); } // massage read/write pointers options.readOffset = ("readOffset" in options) ? options.readOffset : this.read; this.write = ("writeOffset" in options) ? options.writeOffset : this.write; this.read = Math.min(options.readOffset, this.write); } DataBuffer.DEFAULT_GROW_SIZE = 16; DataBuffer.prototype.length = function() { return this.write - this.read; }; DataBuffer.prototype.available = function() { return this.data.length - this.write; }; DataBuffer.prototype.isEmpty = function() { return this.length() <= 0; }; DataBuffer.prototype.accommodate = function(length) { if (!this.data) { // initializes a new buffer length = Math.max(this.write + length, this.growSize); this.data = Buffer.alloc(length); } else if (this.available() < length) { length = Math.max(length, this.growSize); // create a new empty buffer, and copy current one into it var src = this.data; var dst = Buffer.alloc(src.length + length); src.copy(dst, 0); // set data as the new buffer this.data = dst; } // ensure the rest is 0 this.data.fill(0, this.write); return this; }; DataBuffer.prototype.clear = function() { this.read = this.write = 0; this.data = Buffer.alloc(0); return this; }; DataBuffer.prototype.truncate = function(count) { // chop off <count> bytes from the end this.write = this.read + Math.max(0, this.length() - count); // ensure the remainder is 0 this.data.fill(0, this.write); return this; }; DataBuffer.prototype.compact = function() { if (this.read > 0) { if (this.write === this.read) { this.read = this.write = 0; } else { this.data.copy(this.data, 0, this.read, this.write); this.write = this.write - this.read; this.read = 0; } // ensure remainder is 0 this.data.fill(0, this.write); } return this; }; DataBuffer.prototype.copy = function() { return new DataBuffer(this, { readOffset: this.read, writeOffset: this.write, growSize: this.growSize }); }; DataBuffer.prototype.equals = function(test) { if (!DataBuffer.isBuffer(test)) { return false; } if (test.length() !== this.length()) { return false; } var rval = true, delta = this.read - test.read; // constant time for (var idx = test.read; test.write > idx; idx++) { rval = rval && (this.data[idx + delta] === test.data[idx]); } return rval; }; DataBuffer.prototype.at = function(idx) { return this.data[this.read + idx]; }; DataBuffer.prototype.setAt = function(idx, b) { this.data[this.read + idx] = b; return this; }; DataBuffer.prototype.last = function() { return this.data[this.write - 1]; }; DataBuffer.prototype.bytes = function(count) { var rval; if (undefined === count) { count = this.length(); } else if (count) { count = Math.min(count, this.length()); } if (0 === count) { rval = ""; } else { var begin = this.read, end = begin + count, data = this.data.slice(begin, end); rval = String.fromCharCode.apply(null, data); } return rval; }; DataBuffer.prototype.buffer = function(count) { var rval; if (undefined === count) { count = this.length(); } else if (count) { count = Math.min(count, this.length()); } if (0 === count) { rval = new ArrayBuffer(0); } else { var begin = this.read, end = begin + count, data = this.data.slice(begin, end); rval = new Uint8Array(end - begin); rval.set(data); } return rval; }; DataBuffer.prototype.native = function(count) { var rval; if ("undefined" === typeof count) { count = this.length(); } else if (count) { count = Math.min(count, this.length()); } if (0 === count) { rval = Buffer.alloc(0); } else { var begin = this.read, end = begin + count; rval = this.data.slice(begin, end); } return rval; }; DataBuffer.prototype.toHex = function() { return this.toString("hex"); }; DataBuffer.prototype.toString = function(encoding) { // short circuit empty string if (0 === this.length()) { return ""; } var view = this.data.slice(this.read, this.write); encoding = encoding || "utf8"; // special cases, then built-in support switch (encoding) { case "raw": return view.toString("binary"); case "base64url": return base64url.encode(view); case "utf16": return view.toString("ucs2"); default: return view.toString(encoding); } }; DataBuffer.prototype.fillWithByte = function(b, n) { if (!n) { n = this.available(); } this.accommodate(n); this.data.fill(b, this.write, this.write + n); this.write += n; return this; }; DataBuffer.prototype.getBuffer = function(count) { var rval = this.buffer(count); this.read += rval.byteLength; return rval; }; DataBuffer.prototype.putBuffer = function(bytes) { return this.putBytes(bytes); }; DataBuffer.prototype.getBytes = function(count) { var rval = this.bytes(count); this.read += rval.length; return rval; }; DataBuffer.prototype.putBytes = function(bytes, encoding) { if ("string" === typeof bytes) { // fixup encoding encoding = encoding || "binary"; switch (encoding) { case "utf16": // treat as UCS-2/UTF-16BE encoding = "ucs-2"; break; case "raw": encoding = "binary"; break; case "base64url": // NOTE: this returns a Buffer bytes = base64url.decode(bytes); break; } // replace bytes with decoded Buffer (if not already) if (!Buffer.isBuffer(bytes)) { bytes = Buffer.from(bytes, encoding); } } var src, dst; if (bytes instanceof DataBuffer) { // be slightly more efficient var orig = bytes; bytes = orig.data.slice(orig.read, orig.write); orig.read = orig.write; } else if (bytes instanceof forge.util.ByteStringBuffer) { bytes = bytes.getBytes(); } // process array if (Buffer.isBuffer(bytes)) { src = bytes; } else if (Array.isArray(bytes)) { src = Buffer.from(bytes); } else if (forge.util.isArrayBuffer(bytes)) { src = new Uint8Array(bytes); src = Buffer.from(src); } else if (forge.util.isArrayBufferView(bytes)) { src = (bytes instanceof Uint8Array) ? bytes : new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); src = Buffer.from(src); } else { throw new TypeError("invalid source type"); } this.accommodate(src.length); dst = this.data; src.copy(dst, this.write); this.write += src.length; return this; }; DataBuffer.prototype.getNative = function(count) { var rval = this.native(count); this.read += rval.length; return rval; }; DataBuffer.prototype.putNative = DataBuffer.prototype.putBuffer; DataBuffer.prototype.getByte = function() { var b = this.data[this.read]; this.read = Math.min(this.read + 1, this.write); return b; }; DataBuffer.prototype.putByte = function(b) { this.accommodate(1); this.data[this.write] = b & 0xff; this.write++; return this; }; DataBuffer.prototype.getInt16 = function() { var n = (this.data[this.read] << 8) ^ (this.data[this.read + 1]); this.read = Math.min(this.read + 2, this.write); return n; }; DataBuffer.prototype.putInt16 = function(n) { this.accommodate(2); this.data[this.write] = (n >>> 8) & 0xff; this.data[this.write + 1] = n & 0xff; this.write += 2; return this; }; DataBuffer.prototype.getInt24 = function() { var n = (this.data[this.read] << 16) ^ (this.data[this.read + 1] << 8) ^ this.data[this.read + 2]; this.read = Math.min(this.read + 3, this.write); return n; }; DataBuffer.prototype.putInt24 = function(n) { this.accommodate(3); this.data[this.write] = (n >>> 16) & 0xff; this.data[this.write + 1] = (n >>> 8) & 0xff; this.data[this.write + 2] = n & 0xff; this.write += 3; return this; }; DataBuffer.prototype.getInt32 = function() { var n = (this.data[this.read] << 24) ^ (this.data[this.read + 1] << 16) ^ (this.data[this.read + 2] << 8) ^ this.data[this.read + 3]; this.read = Math.min(this.read + 4, this.write); return n; }; DataBuffer.prototype.putInt32 = function(n) { this.accommodate(4); this.data[this.write] = (n >>> 24) & 0xff; this.data[this.write + 1] = (n >>> 16) & 0xff; this.data[this.write + 2] = (n >>> 8) & 0xff; this.data[this.write + 3] = n & 0xff; this.write += 4; return this; }; DataBuffer.prototype.getInt16Le = function() { var n = (this.data[this.read + 1] << 8) ^ this.data[this.read]; this.read = Math.min(this.read + 2, this.write); return n; }; DataBuffer.prototype.putInt16Le = function(n) { this.accommodate(2); this.data[this.write + 1] = (n >>> 8) & 0xff; this.data[this.write] = n & 0xff; this.write += 2; return this; }; DataBuffer.prototype.getInt24Le = function() { var n = (this.data[this.read + 2] << 16) ^ (this.data[this.read + 1] << 8) ^ this.data[this.read]; this.read = Math.min(this.read + 3, this.write); return n; }; DataBuffer.prototype.putInt24Le = function(n) { this.accommodate(3); this.data[this.write + 2] = (n >>> 16) & 0xff; this.data[this.write + 1] = (n >>> 8) & 0xff; this.data[this.write] = n & 0xff; this.write += 3; return this; }; DataBuffer.prototype.getInt32Le = function() { var n = (this.data[this.read + 3] << 24) ^ (this.data[this.read + 2] << 16) ^ (this.data[this.read + 1] << 8) ^ this.data[this.read]; this.read = Math.min(this.read + 4, this.write); return n; }; DataBuffer.prototype.putInt32Le = function(n) { this.accommodate(4); this.data[this.write + 3] = (n >>> 24) & 0xff; this.data[this.write + 2] = (n >>> 16) & 0xff; this.data[this.write + 1] = (n >>> 8) & 0xff; this.data[this.write] = n & 0xff; this.write += 4; return this; }; DataBuffer.prototype.getInt = function(bits) { var rval = 0; do { rval = (rval << 8) | this.getByte(); bits -= 8; } while (bits > 0); return rval; }; DataBuffer.prototype.putInt = function(n, bits) { this.accommodate(Math.ceil(bits / 8)); do { bits -= 8; this.putByte((n >> bits) & 0xff); } while (bits > 0); return this; }; DataBuffer.prototype.putSignedInt = function(n, bits) { if (n < 0) { n += 2 << (bits - 1); } return this.putInt(n, bits); }; DataBuffer.prototype.putString = function(str) { return this.putBytes(str, "utf16"); }; DataBuffer.isBuffer = function(test) { return (test instanceof DataBuffer); }; DataBuffer.asBuffer = function(orig) { return DataBuffer.isBuffer(orig) ? orig : orig ? new DataBuffer(orig) : new DataBuffer(); }; module.exports = forge.util.ByteBuffer = DataBuffer;