| 'use strict'; |
| |
| var BN = require('bn.js'); |
| |
| var utils = require('../utils'); |
| var assert = utils.assert; |
| |
| function Signature(options, enc) { |
| if (options instanceof Signature) |
| return options; |
| |
| if (this._importDER(options, enc)) |
| return; |
| |
| assert(options.r && options.s, 'Signature without r or s'); |
| this.r = new BN(options.r, 16); |
| this.s = new BN(options.s, 16); |
| if (options.recoveryParam === undefined) |
| this.recoveryParam = null; |
| else |
| this.recoveryParam = options.recoveryParam; |
| } |
| module.exports = Signature; |
| |
| function Position() { |
| this.place = 0; |
| } |
| |
| function getLength(buf, p) { |
| var initial = buf[p.place++]; |
| if (!(initial & 0x80)) { |
| return initial; |
| } |
| var octetLen = initial & 0xf; |
| var val = 0; |
| for (var i = 0, off = p.place; i < octetLen; i++, off++) { |
| val <<= 8; |
| val |= buf[off]; |
| } |
| p.place = off; |
| return val; |
| } |
| |
| function rmPadding(buf) { |
| var i = 0; |
| var len = buf.length - 1; |
| while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) { |
| i++; |
| } |
| if (i === 0) { |
| return buf; |
| } |
| return buf.slice(i); |
| } |
| |
| Signature.prototype._importDER = function _importDER(data, enc) { |
| data = utils.toArray(data, enc); |
| var p = new Position(); |
| if (data[p.place++] !== 0x30) { |
| return false; |
| } |
| var len = getLength(data, p); |
| if ((len + p.place) !== data.length) { |
| return false; |
| } |
| if (data[p.place++] !== 0x02) { |
| return false; |
| } |
| var rlen = getLength(data, p); |
| var r = data.slice(p.place, rlen + p.place); |
| p.place += rlen; |
| if (data[p.place++] !== 0x02) { |
| return false; |
| } |
| var slen = getLength(data, p); |
| if (data.length !== slen + p.place) { |
| return false; |
| } |
| var s = data.slice(p.place, slen + p.place); |
| if (r[0] === 0 && (r[1] & 0x80)) { |
| r = r.slice(1); |
| } |
| if (s[0] === 0 && (s[1] & 0x80)) { |
| s = s.slice(1); |
| } |
| |
| this.r = new BN(r); |
| this.s = new BN(s); |
| this.recoveryParam = null; |
| |
| return true; |
| }; |
| |
| function constructLength(arr, len) { |
| if (len < 0x80) { |
| arr.push(len); |
| return; |
| } |
| var octets = 1 + (Math.log(len) / Math.LN2 >>> 3); |
| arr.push(octets | 0x80); |
| while (--octets) { |
| arr.push((len >>> (octets << 3)) & 0xff); |
| } |
| arr.push(len); |
| } |
| |
| Signature.prototype.toDER = function toDER(enc) { |
| var r = this.r.toArray(); |
| var s = this.s.toArray(); |
| |
| // Pad values |
| if (r[0] & 0x80) |
| r = [ 0 ].concat(r); |
| // Pad values |
| if (s[0] & 0x80) |
| s = [ 0 ].concat(s); |
| |
| r = rmPadding(r); |
| s = rmPadding(s); |
| |
| while (!s[0] && !(s[1] & 0x80)) { |
| s = s.slice(1); |
| } |
| var arr = [ 0x02 ]; |
| constructLength(arr, r.length); |
| arr = arr.concat(r); |
| arr.push(0x02); |
| constructLength(arr, s.length); |
| var backHalf = arr.concat(s); |
| var res = [ 0x30 ]; |
| constructLength(res, backHalf.length); |
| res = res.concat(backHalf); |
| return utils.encode(res, enc); |
| }; |