├── README.md ├── rsa ├── base64.gs ├── rsapem-1.1.gs ├── crypto-comp-sha1.gs ├── rsa.gs ├── rsa2.gs ├── asn1hex-1.1.gs ├── x509-1.1.gs ├── rsasign-1.2.gs ├── jsbn.gs ├── jsbn2.gs ├── crypto-comp-core.gs └── crypto-1.1.gs ├── Xero.gs └── Code.gs /README.md: -------------------------------------------------------------------------------- 1 | # xero-api-apps-script 2 | Integrating Xero.com REST API with Google Apps Script 3 | 4 | How to use this Script 5 | 1. Create a new Google Spreadsheet. 6 | 2. From Spreadsheet menu go to Tools >> Script Editor. 7 | 3. In the Script, copy and add following files. Add new script file for each of the following: 8 | a) rsa/jsbn.gs 9 | b) rsa/jsbn2.gs 10 | c) rsa/rsa.gs 11 | d) rsa/rsa2.gs 12 | e) rsa/base64.gs 13 | f) rsa/crypto-comp-core.gs 14 | g) rsa/crypto-comp-sha1.gs 15 | h) rsa/rsapem-1.1.gs 16 | i) rsa/rsasign-1.2.gs 17 | j) rsa/asn1hex-1.1.gs 18 | k) rsa/x509-1.1.gs 19 | l) rsa/crypto-1.1.gs 20 | m) Xero.gs 21 | 4. Add code in Code.gs to existing Code.gs file. 22 | 5. Refresh spreadsheet. 23 | 6. From Custom menu XERO go to Xero Settings. Add details for Private App. 24 | 7. We have only tested this script with Private App and "Manual Download Invoices". 25 | -------------------------------------------------------------------------------- /rsa/base64.gs: -------------------------------------------------------------------------------- 1 | /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ 2 | */ 3 | 4 | var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 5 | var b64pad="="; 6 | 7 | function hex2b64(h) { 8 | var i; 9 | var c; 10 | var ret = ""; 11 | for(i = 0; i+3 <= h.length; i+=3) { 12 | c = parseInt(h.substring(i,i+3),16); 13 | ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63); 14 | } 15 | if(i+1 == h.length) { 16 | c = parseInt(h.substring(i,i+1),16); 17 | ret += b64map.charAt(c << 2); 18 | } 19 | else if(i+2 == h.length) { 20 | c = parseInt(h.substring(i,i+2),16); 21 | ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4); 22 | } 23 | if (b64pad) while((ret.length & 3) > 0) ret += b64pad; 24 | return ret; 25 | } 26 | 27 | // convert a base64 string to hex 28 | function b64tohex(s) { 29 | var ret = "" 30 | var i; 31 | var k = 0; // b64 state, 0-3 32 | var slop; 33 | var v; 34 | for(i = 0; i < s.length; ++i) { 35 | if(s.charAt(i) == b64pad) break; 36 | v = b64map.indexOf(s.charAt(i)); 37 | if(v < 0) continue; 38 | if(k == 0) { 39 | ret += int2char(v >> 2); 40 | slop = v & 3; 41 | k = 1; 42 | } 43 | else if(k == 1) { 44 | ret += int2char((slop << 2) | (v >> 4)); 45 | slop = v & 0xf; 46 | k = 2; 47 | } 48 | else if(k == 2) { 49 | ret += int2char(slop); 50 | ret += int2char(v >> 2); 51 | slop = v & 3; 52 | k = 3; 53 | } 54 | else { 55 | ret += int2char((slop << 2) | (v >> 4)); 56 | ret += int2char(v & 0xf); 57 | k = 0; 58 | } 59 | } 60 | if(k == 1) 61 | ret += int2char(slop << 2); 62 | return ret; 63 | } 64 | 65 | // convert a base64 string to a byte/number array 66 | function b64toBA(s) { 67 | //piggyback on b64tohex for now, optimize later 68 | var h = b64tohex(s); 69 | var i; 70 | var a = new Array(); 71 | for(i = 0; 2*i < h.length; ++i) { 72 | a[i] = parseInt(h.substring(2*i,2*i+2),16); 73 | } 74 | return a; 75 | } 76 | -------------------------------------------------------------------------------- /rsa/rsapem-1.1.gs: -------------------------------------------------------------------------------- 1 | /*! rsapem-1.1.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license 2 | */ 3 | // 4 | // rsa-pem.js - adding function for reading/writing PKCS#1 PEM private key 5 | // to RSAKey class. 6 | // 7 | // version: 1.1.1 (2013-Apr-12) 8 | // 9 | // Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com) 10 | // 11 | // This software is licensed under the terms of the MIT License. 12 | // http://kjur.github.com/jsrsasign/license/ 13 | // 14 | // The above copyright and license notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // 18 | // Depends on: 19 | // 20 | // 21 | // 22 | // _RSApem_pemToBase64(sPEM) 23 | // 24 | // removing PEM header, PEM footer and space characters including 25 | // new lines from PEM formatted RSA private key string. 26 | // 27 | 28 | /** 29 | * @fileOverview 30 | * @name rsapem-1.1.js 31 | * @author Kenji Urushima kenji.urushima@gmail.com 32 | * @version 1.1 33 | * @license MIT License 34 | */ 35 | function _rsapem_pemToBase64(sPEMPrivateKey) { 36 | var s = sPEMPrivateKey; 37 | s = s.replace("-----BEGIN RSA PRIVATE KEY-----", ""); 38 | s = s.replace("-----END RSA PRIVATE KEY-----", ""); 39 | s = s.replace(/[ \n]+/g, ""); 40 | return s; 41 | } 42 | 43 | function _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey) { 44 | var a = new Array(); 45 | var v1 = ASN1HEX.getStartPosOfV_AtObj(hPrivateKey, 0); 46 | var n1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, v1); 47 | var e1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, n1); 48 | var d1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, e1); 49 | var p1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, d1); 50 | var q1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, p1); 51 | var dp1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, q1); 52 | var dq1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dp1); 53 | var co1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dq1); 54 | a.push(v1, n1, e1, d1, p1, q1, dp1, dq1, co1); 55 | return a; 56 | } 57 | 58 | function _rsapem_getHexValueArrayOfChildrenFromHex(hPrivateKey) { 59 | var posArray = _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey); 60 | var v = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[0]); 61 | var n = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[1]); 62 | var e = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[2]); 63 | var d = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[3]); 64 | var p = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[4]); 65 | var q = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[5]); 66 | var dp = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[6]); 67 | var dq = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[7]); 68 | var co = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[8]); 69 | var a = new Array(); 70 | a.push(v, n, e, d, p, q, dp, dq, co); 71 | return a; 72 | } 73 | 74 | /** 75 | * read RSA private key from a ASN.1 hexadecimal string 76 | * @name readPrivateKeyFromASN1HexString 77 | * @memberOf RSAKey# 78 | * @function 79 | * @param {String} keyHex ASN.1 hexadecimal string of PKCS#1 private key. 80 | * @since 1.1.1 81 | */ 82 | function _rsapem_readPrivateKeyFromASN1HexString(keyHex) { 83 | var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex); 84 | this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]); 85 | } 86 | 87 | /** 88 | * read PKCS#1 private key from a string 89 | * @name readPrivateKeyFromPEMString 90 | * @memberOf RSAKey# 91 | * @function 92 | * @param {String} keyPEM string of PKCS#1 private key. 93 | */ 94 | function _rsapem_readPrivateKeyFromPEMString(keyPEM) { 95 | var keyB64 = _rsapem_pemToBase64(keyPEM); 96 | var keyHex = b64tohex(keyB64) // depends base64.js 97 | var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex); 98 | this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]); 99 | } 100 | 101 | RSAKey.prototype.readPrivateKeyFromPEMString = _rsapem_readPrivateKeyFromPEMString; 102 | RSAKey.prototype.readPrivateKeyFromASN1HexString = _rsapem_readPrivateKeyFromASN1HexString; 103 | -------------------------------------------------------------------------------- /rsa/crypto-comp-sha1.gs: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | (function () { 8 | // Shortcuts 9 | var C = CryptoJS; 10 | var C_lib = C.lib; 11 | var WordArray = C_lib.WordArray; 12 | var Hasher = C_lib.Hasher; 13 | var C_algo = C.algo; 14 | 15 | // Reusable object 16 | var W = []; 17 | 18 | /** 19 | * SHA-1 hash algorithm. 20 | */ 21 | var SHA1 = C_algo.SHA1 = Hasher.extend({ 22 | _doReset: function () { 23 | this._hash = new WordArray.init([ 24 | 0x67452301, 0xefcdab89, 25 | 0x98badcfe, 0x10325476, 26 | 0xc3d2e1f0 27 | ]); 28 | }, 29 | 30 | _doProcessBlock: function (M, offset) { 31 | // Shortcut 32 | var H = this._hash.words; 33 | 34 | // Working variables 35 | var a = H[0]; 36 | var b = H[1]; 37 | var c = H[2]; 38 | var d = H[3]; 39 | var e = H[4]; 40 | 41 | // Computation 42 | for (var i = 0; i < 80; i++) { 43 | if (i < 16) { 44 | W[i] = M[offset + i] | 0; 45 | } else { 46 | var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; 47 | W[i] = (n << 1) | (n >>> 31); 48 | } 49 | 50 | var t = ((a << 5) | (a >>> 27)) + e + W[i]; 51 | if (i < 20) { 52 | t += ((b & c) | (~b & d)) + 0x5a827999; 53 | } else if (i < 40) { 54 | t += (b ^ c ^ d) + 0x6ed9eba1; 55 | } else if (i < 60) { 56 | t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; 57 | } else /* if (i < 80) */ { 58 | t += (b ^ c ^ d) - 0x359d3e2a; 59 | } 60 | 61 | e = d; 62 | d = c; 63 | c = (b << 30) | (b >>> 2); 64 | b = a; 65 | a = t; 66 | } 67 | 68 | // Intermediate hash value 69 | H[0] = (H[0] + a) | 0; 70 | H[1] = (H[1] + b) | 0; 71 | H[2] = (H[2] + c) | 0; 72 | H[3] = (H[3] + d) | 0; 73 | H[4] = (H[4] + e) | 0; 74 | }, 75 | 76 | _doFinalize: function () { 77 | // Shortcuts 78 | var data = this._data; 79 | var dataWords = data.words; 80 | 81 | var nBitsTotal = this._nDataBytes * 8; 82 | var nBitsLeft = data.sigBytes * 8; 83 | 84 | // Add padding 85 | dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); 86 | dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); 87 | dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; 88 | data.sigBytes = dataWords.length * 4; 89 | 90 | // Hash final blocks 91 | this._process(); 92 | 93 | // Return final computed hash 94 | return this._hash; 95 | }, 96 | 97 | clone: function () { 98 | var clone = Hasher.clone.call(this); 99 | clone._hash = this._hash.clone(); 100 | 101 | return clone; 102 | } 103 | }); 104 | 105 | /** 106 | * Shortcut function to the hasher's object interface. 107 | * 108 | * @param {WordArray|string} message The message to hash. 109 | * 110 | * @return {WordArray} The hash. 111 | * 112 | * @static 113 | * 114 | * @example 115 | * 116 | * var hash = CryptoJS.SHA1('message'); 117 | * var hash = CryptoJS.SHA1(wordArray); 118 | */ 119 | C.SHA1 = Hasher._createHelper(SHA1); 120 | 121 | /** 122 | * Shortcut function to the HMAC's object interface. 123 | * 124 | * @param {WordArray|string} message The message to hash. 125 | * @param {WordArray|string} key The secret key. 126 | * 127 | * @return {WordArray} The HMAC. 128 | * 129 | * @static 130 | * 131 | * @example 132 | * 133 | * var hmac = CryptoJS.HmacSHA1(message, key); 134 | */ 135 | C.HmacSHA1 = Hasher._createHmacHelper(SHA1); 136 | }()); 137 | -------------------------------------------------------------------------------- /rsa/rsa.gs: -------------------------------------------------------------------------------- 1 | /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ 2 | */ 3 | // Depends on jsbn.js and rng.js 4 | 5 | // Version 1.1: support utf-8 encoding in pkcs1pad2 6 | 7 | // convert a (hex) string to a bignum object 8 | function parseBigInt(str,r) { 9 | return new BigInteger(str,r); 10 | } 11 | 12 | function linebrk(s,n) { 13 | var ret = ""; 14 | var i = 0; 15 | while(i + n < s.length) { 16 | ret += s.substring(i,i+n) + "\n"; 17 | i += n; 18 | } 19 | return ret + s.substring(i,s.length); 20 | } 21 | 22 | function byte2Hex(b) { 23 | if(b < 0x10) 24 | return "0" + b.toString(16); 25 | else 26 | return b.toString(16); 27 | } 28 | 29 | // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint 30 | function pkcs1pad2(s,n) { 31 | if(n < s.length + 11) { // TODO: fix for utf-8 32 | Browser.msgBox("Message too long for RSA"); 33 | return null; 34 | } 35 | var ba = new Array(); 36 | var i = s.length - 1; 37 | while(i >= 0 && n > 0) { 38 | var c = s.charCodeAt(i--); 39 | if(c < 128) { // encode using utf-8 40 | ba[--n] = c; 41 | } 42 | else if((c > 127) && (c < 2048)) { 43 | ba[--n] = (c & 63) | 128; 44 | ba[--n] = (c >> 6) | 192; 45 | } 46 | else { 47 | ba[--n] = (c & 63) | 128; 48 | ba[--n] = ((c >> 6) & 63) | 128; 49 | ba[--n] = (c >> 12) | 224; 50 | } 51 | } 52 | ba[--n] = 0; 53 | var rng = new SecureRandom(); 54 | var x = new Array(); 55 | while(n > 2) { // random non-zero pad 56 | x[0] = 0; 57 | while(x[0] == 0) rng.nextBytes(x); 58 | ba[--n] = x[0]; 59 | } 60 | ba[--n] = 2; 61 | ba[--n] = 0; 62 | return new BigInteger(ba); 63 | } 64 | 65 | // PKCS#1 (OAEP) mask generation function 66 | function oaep_mgf1_arr(seed, len, hash) 67 | { 68 | var mask = '', i = 0; 69 | 70 | while (mask.length < len) 71 | { 72 | mask += hash(String.fromCharCode.apply(String, seed.concat([ 73 | (i & 0xff000000) >> 24, 74 | (i & 0x00ff0000) >> 16, 75 | (i & 0x0000ff00) >> 8, 76 | i & 0x000000ff]))); 77 | i += 1; 78 | } 79 | 80 | return mask; 81 | } 82 | 83 | var SHA1_SIZE = 20; 84 | 85 | // PKCS#1 (OAEP) pad input string s to n bytes, and return a bigint 86 | function oaep_pad(s, n, hash) 87 | { 88 | if (s.length + 2 * SHA1_SIZE + 2 > n) 89 | { 90 | throw "Message too long for RSA"; 91 | } 92 | 93 | var PS = '', i; 94 | 95 | for (i = 0; i < n - s.length - 2 * SHA1_SIZE - 2; i += 1) 96 | { 97 | PS += '\x00'; 98 | } 99 | 100 | var DB = rstr_sha1('') + PS + '\x01' + s; 101 | var seed = new Array(SHA1_SIZE); 102 | new SecureRandom().nextBytes(seed); 103 | 104 | var dbMask = oaep_mgf1_arr(seed, DB.length, hash || rstr_sha1); 105 | var maskedDB = []; 106 | 107 | for (i = 0; i < DB.length; i += 1) 108 | { 109 | maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i); 110 | } 111 | 112 | var seedMask = oaep_mgf1_arr(maskedDB, seed.length, rstr_sha1); 113 | var maskedSeed = [0]; 114 | 115 | for (i = 0; i < seed.length; i += 1) 116 | { 117 | maskedSeed[i + 1] = seed[i] ^ seedMask.charCodeAt(i); 118 | } 119 | 120 | return new BigInteger(maskedSeed.concat(maskedDB)); 121 | } 122 | 123 | // "empty" RSA key constructor 124 | function RSAKey() { 125 | this.n = null; 126 | this.e = 0; 127 | this.d = null; 128 | this.p = null; 129 | this.q = null; 130 | this.dmp1 = null; 131 | this.dmq1 = null; 132 | this.coeff = null; 133 | } 134 | 135 | // Set the public key fields N and e from hex strings 136 | function RSASetPublic(N,E) { 137 | this.isPublic = true; 138 | if (typeof N !== "string") 139 | { 140 | this.n = N; 141 | this.e = E; 142 | } 143 | else if(N != null && E != null && N.length > 0 && E.length > 0) { 144 | this.n = parseBigInt(N,16); 145 | this.e = parseInt(E,16); 146 | } 147 | else 148 | Browser.msgBox("Invalid RSA public key"); 149 | } 150 | 151 | // Perform raw public operation on "x": return x^e (mod n) 152 | function RSADoPublic(x) { 153 | return x.modPowInt(this.e, this.n); 154 | } 155 | 156 | // Return the PKCS#1 RSA encryption of "text" as an even-length hex string 157 | function RSAEncrypt(text) { 158 | var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); 159 | if(m == null) return null; 160 | var c = this.doPublic(m); 161 | if(c == null) return null; 162 | var h = c.toString(16); 163 | if((h.length & 1) == 0) return h; else return "0" + h; 164 | } 165 | 166 | // Return the PKCS#1 OAEP RSA encryption of "text" as an even-length hex string 167 | function RSAEncryptOAEP(text, hash) { 168 | var m = oaep_pad(text, (this.n.bitLength()+7)>>3, hash); 169 | if(m == null) return null; 170 | var c = this.doPublic(m); 171 | if(c == null) return null; 172 | var h = c.toString(16); 173 | if((h.length & 1) == 0) return h; else return "0" + h; 174 | } 175 | 176 | // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string 177 | //function RSAEncryptB64(text) { 178 | // var h = this.encrypt(text); 179 | // if(h) return hex2b64(h); else return null; 180 | //} 181 | 182 | // protected 183 | RSAKey.prototype.doPublic = RSADoPublic; 184 | 185 | // public 186 | RSAKey.prototype.setPublic = RSASetPublic; 187 | RSAKey.prototype.encrypt = RSAEncrypt; 188 | RSAKey.prototype.encryptOAEP = RSAEncryptOAEP; 189 | //RSAKey.prototype.encrypt_b64 = RSAEncryptB64; 190 | 191 | RSAKey.prototype.type = "RSA"; 192 | -------------------------------------------------------------------------------- /rsa/rsa2.gs: -------------------------------------------------------------------------------- 1 | /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ 2 | */ 3 | // Depends on rsa.js and jsbn2.js 4 | 5 | // Version 1.1: support utf-8 decoding in pkcs1unpad2 6 | 7 | // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext 8 | function pkcs1unpad2(d,n) { 9 | var b = d.toByteArray(); 10 | var i = 0; 11 | while(i < b.length && b[i] == 0) ++i; 12 | if(b.length-i != n-1 || b[i] != 2) 13 | return null; 14 | ++i; 15 | while(b[i] != 0) 16 | if(++i >= b.length) return null; 17 | var ret = ""; 18 | while(++i < b.length) { 19 | var c = b[i] & 255; 20 | if(c < 128) { // utf-8 decode 21 | ret += String.fromCharCode(c); 22 | } 23 | else if((c > 191) && (c < 224)) { 24 | ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63)); 25 | ++i; 26 | } 27 | else { 28 | ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63)); 29 | i += 2; 30 | } 31 | } 32 | return ret; 33 | } 34 | 35 | // PKCS#1 (OAEP) mask generation function 36 | function oaep_mgf1_str(seed, len, hash) 37 | { 38 | var mask = '', i = 0; 39 | 40 | while (mask.length < len) 41 | { 42 | mask += hash(seed + String.fromCharCode.apply(String, [ 43 | (i & 0xff000000) >> 24, 44 | (i & 0x00ff0000) >> 16, 45 | (i & 0x0000ff00) >> 8, 46 | i & 0x000000ff])); 47 | i += 1; 48 | } 49 | 50 | return mask; 51 | } 52 | 53 | var SHA1_SIZE = 20; 54 | 55 | // Undo PKCS#1 (OAEP) padding and, if valid, return the plaintext 56 | function oaep_unpad(d, n, hash) 57 | { 58 | d = d.toByteArray(); 59 | 60 | var i; 61 | 62 | for (i = 0; i < d.length; i += 1) 63 | { 64 | d[i] &= 0xff; 65 | } 66 | 67 | while (d.length < n) 68 | { 69 | d.unshift(0); 70 | } 71 | 72 | d = String.fromCharCode.apply(String, d); 73 | 74 | if (d.length < 2 * SHA1_SIZE + 2) 75 | { 76 | throw "Cipher too short"; 77 | } 78 | 79 | var maskedSeed = d.substr(1, SHA1_SIZE) 80 | var maskedDB = d.substr(SHA1_SIZE + 1); 81 | 82 | var seedMask = oaep_mgf1_str(maskedDB, SHA1_SIZE, hash || rstr_sha1); 83 | var seed = [], i; 84 | 85 | for (i = 0; i < maskedSeed.length; i += 1) 86 | { 87 | seed[i] = maskedSeed.charCodeAt(i) ^ seedMask.charCodeAt(i); 88 | } 89 | 90 | var dbMask = oaep_mgf1_str(String.fromCharCode.apply(String, seed), 91 | d.length - SHA1_SIZE, rstr_sha1); 92 | 93 | var DB = []; 94 | 95 | for (i = 0; i < maskedDB.length; i += 1) 96 | { 97 | DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i); 98 | } 99 | 100 | DB = String.fromCharCode.apply(String, DB); 101 | 102 | if (DB.substr(0, SHA1_SIZE) !== rstr_sha1('')) 103 | { 104 | throw "Hash mismatch"; 105 | } 106 | 107 | DB = DB.substr(SHA1_SIZE); 108 | 109 | var first_one = DB.indexOf('\x01'); 110 | var last_zero = (first_one != -1) ? DB.substr(0, first_one).lastIndexOf('\x00') : -1; 111 | 112 | if (last_zero + 1 != first_one) 113 | { 114 | throw "Malformed data"; 115 | } 116 | 117 | return DB.substr(first_one + 1); 118 | } 119 | 120 | // Set the private key fields N, e, and d from hex strings 121 | function RSASetPrivate(N,E,D) { 122 | this.isPrivate = true; 123 | if (typeof N !== "string") 124 | { 125 | this.n = N; 126 | this.e = E; 127 | this.d = D; 128 | } 129 | else if(N != null && E != null && N.length > 0 && E.length > 0) { 130 | this.n = parseBigInt(N,16); 131 | this.e = parseInt(E,16); 132 | this.d = parseBigInt(D,16); 133 | } 134 | else 135 | Browser.msgBox("Invalid RSA private key"); 136 | } 137 | 138 | // Set the private key fields N, e, d and CRT params from hex strings 139 | function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { 140 | this.isPrivate = true; 141 | var errorMsg = "Invalid RSA Private Key." 142 | if (N == null) throw errorMsg; //"RSASetPrivateEx N == null"; 143 | if (E == null) throw errorMsg; //"RSASetPrivateEx E == null"; 144 | if (N.length == 0) throw errorMsg //"RSASetPrivateEx N.length == 0"; 145 | if (E.length == 0) throw errorMsg //"RSASetPrivateEx E.length == 0"; 146 | 147 | if (N != null && E != null && N.length > 0 && E.length > 0) { 148 | this.n = parseBigInt(N,16); 149 | this.e = parseInt(E,16); 150 | this.d = parseBigInt(D,16); 151 | this.p = parseBigInt(P,16); 152 | this.q = parseBigInt(Q,16); 153 | this.dmp1 = parseBigInt(DP,16); 154 | this.dmq1 = parseBigInt(DQ,16); 155 | this.coeff = parseBigInt(C,16); 156 | } else { 157 | Browser.msgBox("Invalid RSA Private Key."); 158 | } 159 | } 160 | 161 | // Generate a new random private key B bits long, using public expt E 162 | function RSAGenerate(B,E) { 163 | var rng = new SecureRandom(); 164 | var qs = B>>1; 165 | this.e = parseInt(E,16); 166 | var ee = new BigInteger(E,16); 167 | for(;;) { 168 | for(;;) { 169 | this.p = new BigInteger(B-qs,1,rng); 170 | if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; 171 | } 172 | for(;;) { 173 | this.q = new BigInteger(qs,1,rng); 174 | if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; 175 | } 176 | if(this.p.compareTo(this.q) <= 0) { 177 | var t = this.p; 178 | this.p = this.q; 179 | this.q = t; 180 | } 181 | var p1 = this.p.subtract(BigInteger.ONE); // p1 = p - 1 182 | var q1 = this.q.subtract(BigInteger.ONE); // q1 = q - 1 183 | var phi = p1.multiply(q1); 184 | if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { 185 | this.n = this.p.multiply(this.q); // this.n = p * q 186 | this.d = ee.modInverse(phi); // this.d = 187 | this.dmp1 = this.d.mod(p1); // this.dmp1 = d mod (p - 1) 188 | this.dmq1 = this.d.mod(q1); // this.dmq1 = d mod (q - 1) 189 | this.coeff = this.q.modInverse(this.p); // this.coeff = (q ^ -1) mod p 190 | break; 191 | } 192 | } 193 | this.isPrivate = true; 194 | } 195 | 196 | // Perform raw private operation on "x": return x^d (mod n) 197 | function RSADoPrivate(x) { 198 | if(this.p == null || this.q == null) 199 | return x.modPow(this.d, this.n); 200 | 201 | // TODO: re-calculate any missing CRT params 202 | var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp? 203 | var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq? 204 | 205 | while(xp.compareTo(xq) < 0) 206 | xp = xp.add(this.p); 207 | // NOTE: 208 | // xp.subtract(xq) => cp -cq 209 | // xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h 210 | // xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M 211 | return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); 212 | } 213 | 214 | // Return the PKCS#1 RSA decryption of "ctext". 215 | // "ctext" is an even-length hex string and the output is a plain string. 216 | function RSADecrypt(ctext) { 217 | var c = parseBigInt(ctext, 16); 218 | var m = this.doPrivate(c); 219 | if(m == null) return null; 220 | return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); 221 | } 222 | 223 | // Return the PKCS#1 OAEP RSA decryption of "ctext". 224 | // "ctext" is an even-length hex string and the output is a plain string. 225 | function RSADecryptOAEP(ctext, hash) { 226 | var c = parseBigInt(ctext, 16); 227 | var m = this.doPrivate(c); 228 | if(m == null) return null; 229 | return oaep_unpad(m, (this.n.bitLength()+7)>>3, hash); 230 | } 231 | 232 | // Return the PKCS#1 RSA decryption of "ctext". 233 | // "ctext" is a Base64-encoded string and the output is a plain string. 234 | //function RSAB64Decrypt(ctext) { 235 | // var h = b64tohex(ctext); 236 | // if(h) return this.decrypt(h); else return null; 237 | //} 238 | 239 | // protected 240 | RSAKey.prototype.doPrivate = RSADoPrivate; 241 | 242 | // public 243 | RSAKey.prototype.setPrivate = RSASetPrivate; 244 | RSAKey.prototype.setPrivateEx = RSASetPrivateEx; 245 | RSAKey.prototype.generate = RSAGenerate; 246 | RSAKey.prototype.decrypt = RSADecrypt; 247 | RSAKey.prototype.decryptOAEP = RSADecryptOAEP; 248 | //RSAKey.prototype.b64_decrypt = RSAB64Decrypt; 249 | -------------------------------------------------------------------------------- /Xero.gs: -------------------------------------------------------------------------------- 1 | var baseURL = 'https://api.xero.com'; 2 | var requestTokenURL = baseURL + '/oauth/RequestToken'; 3 | var authorizeURL = baseURL + '/oauth/Authorize'; 4 | var accessTokenURL = baseURL + '/oauth/AccessToken'; 5 | var apiEndPoint = baseURL + '/api.xro/2.0'; 6 | 7 | var Xero = { 8 | appType: '', userAgent: '', consumerKey: '', consumerSecret: '', callbackURL: '', rsaKey: '', isConnected: false, 9 | 10 | getProperty: function(propName) { 11 | if (this[propName] != null) 12 | return this[propName]; 13 | else 14 | return false; 15 | }, 16 | 17 | getSettings: function() { 18 | var p = PropertiesService.getScriptProperties().getProperties(); 19 | if (p.appType == null || p.appType == '') { 20 | Browser.msgBox('Please enter Xero Settings.'); 21 | return false; 22 | } 23 | else if (p.userAgent == null || p.userAgent == '' || p.consumerKey == null || p.consumerKey == '' || p.consumerSecret == null || p.consumerSecret == '') { 24 | Browser.msgBox('Error: Missing Xero Settings (Apllication Name/ Consumer Key/ Consumer Secret).'); 25 | return false; 26 | } 27 | else if (p.appType == 'Public') { 28 | if (p.callbackURL == null || p.callbackURL == '') { 29 | Browser.msgBox('Error: Missing Xero Settings (Callback URL.'); 30 | return false; 31 | } 32 | } 33 | else if (p.appType == 'Partner') { 34 | if (p.callbackURL == null || p.callbackURL == '' || p.rsaKey == null || p.rsaKey == '' ) { 35 | Browser.msgBox('Error: Missing Xero Settings (Callback URL/ RSA Key'); 36 | return false; 37 | } 38 | } 39 | 40 | this.appType = p.appType; 41 | this.userAgent = p.userAgent; 42 | this.consumerKey = p.consumerKey; 43 | this.consumerSecret = p.consumerSecret; 44 | this.rsaKey = p.rsaKey; 45 | this.callbackURL = p.callbackURL; 46 | this.requestTokenSecret = ""; 47 | this.accessToken = ""; 48 | this.accessTokenSecret = ""; 49 | if (p.requestTokenSecret != null) 50 | this.requestTokenSecret = p.requestTokenSecret; 51 | if (p.accessToken != null) 52 | this.accessToken = p.accessToken; 53 | if (p.accessTokenSecret != null) 54 | this.accessTokenSecret = p.accessTokenSecret; 55 | if (p.isConnected != null) 56 | this.isConnected = p.isConnected; 57 | return true; 58 | }, 59 | 60 | connect: function() { 61 | this.getSettings(); 62 | if (this.appType != 'Private' /*&& !this.isConnected*/) { 63 | // Ask user to connect to Xero first 64 | // Step 1. Get an Unauthorised Request Token 65 | var payload = {"oauth_consumer_key": this.consumerKey, 66 | "oauth_signature_method": "PLAINTEXT", 67 | "oauth_signature": encodeURIComponent(this.consumerSecret + '&'), 68 | "oauth_timestamp": ((new Date().getTime())/1000).toFixed(0), 69 | "oauth_nonce": generateRandomString(Math.floor(Math.round(25))), 70 | "oauth_version": "1.0", 71 | "oauth_callback": this.callbackURL}; 72 | var options = {"method": "post", "payload": payload}; 73 | var response = UrlFetchApp.fetch(requestTokenURL, options); 74 | var reoAuthToken = /(oauth_token=)([a-zA-Z0-9]+)/; 75 | var tokenMatch = reoAuthToken.exec(response.getContentText()); 76 | var oAuthRequestToken = tokenMatch[2]; 77 | var reTokenSecret = /(oauth_token_secret=)([a-zA-Z0-9]+)/; 78 | var secretMatch = reTokenSecret.exec(response.getContentText()) ; 79 | var tokenSecret = secretMatch[2]; 80 | PropertiesService.getScriptProperties().setProperty('requestTokenSecret', tokenSecret); 81 | return authorizeURL + '?oauth_token=' + oAuthRequestToken; 82 | 83 | // Step 2 Show user the link to connect to Xero 84 | /* 85 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 86 | var app = UiApp.createApplication(); 87 | app.setHeight(50);app.setWidth(100); 88 | var link = app.createAnchor('Connect to Xero', true, authorizeURL + '?oauth_token=' + oAuthRequestToken); 89 | var handler = app.createServerHandler('closeApp'); 90 | link.addClickHandler(handler); 91 | app.add(link); 92 | ss.show(app); 93 | */ 94 | } 95 | }, 96 | 97 | fetchPrivateAppData: function(item, pageNo) { 98 | /* FETCH PRIVATE APPLICATION DATA */ 99 | var method = 'GET'; 100 | var requestURL = apiEndPoint + '/' + item ; 101 | var oauth_signature_method = 'RSA-SHA1'; 102 | var oauth_timestamp = (new Date().getTime()/1000).toFixed(); 103 | var oauth_nonce = generateRandomString(Math.floor(Math.random() * 50)); 104 | var oauth_version = '1.0'; 105 | var signBase = 'GET' + '&' + encodeURIComponent(requestURL) + '&' 106 | + encodeURIComponent('oauth_consumer_key=' + this.consumerKey + '&oauth_nonce=' + oauth_nonce + '&oauth_signature_method=' 107 | + oauth_signature_method + '&oauth_timestamp=' + oauth_timestamp + '&oauth_token=' + this.consumerKey + '&oauth_version=' 108 | + oauth_version + '&page=' + pageNo); 109 | if (!this.rsa) { 110 | this.rsa = new RSAKey(); 111 | this.rsa.readPrivateKeyFromPEMString(this.rsaKey); 112 | var sbSigned = this.rsa.signString(signBase, 'sha1'); 113 | } 114 | else 115 | var sbSigned = this.rsa.signString(signBase, 'sha1'); 116 | 117 | var data = new Array(); 118 | for (var i =0; i < sbSigned.length; i += 2) 119 | data.push(parseInt("0x" + sbSigned.substr(i, 2))); 120 | var oauth_signature = hex2b64(sbSigned); 121 | 122 | var authHeader = "OAuth oauth_token=\"" + this.consumerKey + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_consumer_key=\"" + this.consumerKey 123 | + "\",oauth_signature_method=\"RSA-SHA1\",oauth_timestamp=\"" + oauth_timestamp + "\",oauth_version=\"1.0\",oauth_signature=\"" 124 | + encodeURIComponent(oauth_signature) + "\""; 125 | 126 | var headers = { "User-Agent": this.userAgent, "Authorization": authHeader, "Accept": "application/json"}; 127 | var options = { muteHttpExceptions: true, "headers": headers}; 128 | var response = UrlFetchApp.fetch(requestURL + '?page=' + pageNo, options); 129 | 130 | if (response.getResponseCode() == 200) 131 | return JSON.parse(response.getContentText()); 132 | else 133 | return false; 134 | }, 135 | 136 | fetchData: function(item, pageNo) { 137 | this.getSettings(); 138 | // lets find out which method to use for fetching data 139 | switch(this.appType) { 140 | case 'Private': 141 | return this.fetchPrivateAppData(item, pageNo); 142 | break; 143 | case 'Public': 144 | if (!this.isConnected) 145 | this.connect(); 146 | else 147 | return this.fetchPublicAppData(item, pageNo); 148 | break; 149 | case 'Partner': 150 | if (!this.isConnected) 151 | this.connect(); 152 | else 153 | return this.fetchPartnerAppData(item); 154 | break; 155 | } 156 | }, 157 | 158 | 159 | fetchPublicAppData: function(item, pageNo) { 160 | /* For PUBLIC APPLICATION TYPE */ 161 | this.getSettings(); // get latest settings 162 | var method = 'GET'; 163 | var requestURL = apiEndPoint + '/' + item ; 164 | var oauth_signature_method = 'HMAC-SHA1'; 165 | var oauth_timestamp = (new Date().getTime()/1000).toFixed(); 166 | var oauth_nonce = generateRandomString(Math.floor(Math.random() * 50)); 167 | var oauth_version = '1.0'; 168 | var signBase = 'GET' + '&' + encodeURIComponent(requestURL) + '&' 169 | + encodeURIComponent('oauth_consumer_key=' + this.consumerKey + '&oauth_nonce=' + oauth_nonce + '&oauth_signature_method=' + oauth_signature_method 170 | + '&oauth_timestamp=' + oauth_timestamp + '&oauth_token=' + this.accessToken + '&oauth_version=' + oauth_version + '&page=' + pageNo); 171 | var sbSigned = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, signBase, 172 | encodeURIComponent(this.consumerSecret) + '&' + encodeURIComponent(this.accessTokenSecret)); 173 | var oauth_signature = Utilities.base64Encode(sbSigned); 174 | 175 | var authHeader = "OAuth oauth_consumer_key=\"" + this.consumerKey + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_token=\"" + this.accessToken 176 | + "\",oauth_signature_method=\"" + oauth_signature_method +"\",oauth_timestamp=\"" + oauth_timestamp + "\",oauth_version=\"1.0\",oauth_signature=\"" 177 | + encodeURIComponent(oauth_signature) + "\""; 178 | 179 | var headers = { "User-Agent": + this.userAgent, "Authorization": authHeader, "Accept": "application/json" }; 180 | var options = { "headers": headers}; 181 | try { 182 | var response = UrlFetchApp.fetch(requestURL + '?page='+ pageNo, options); 183 | return JSON.parse(response.getContentText()); 184 | } 185 | catch(e) { 186 | Logger.log(e.message); 187 | Browser.msgBox(e.message); 188 | } 189 | }, 190 | 191 | fetchPartnerAppData: function(item) { 192 | return false; 193 | }, 194 | 195 | uploadData: function(item, xml, method) { 196 | var method = method || 'POST'; 197 | var requestURL = apiEndPoint + '/' + item ; 198 | var oauth_signature_method = 'RSA-SHA1'; 199 | var oauth_timestamp = (new Date().getTime()/1000).toFixed(); 200 | var oauth_nonce = generateRandomString(Math.floor(Math.random() * 50)); 201 | var oauth_version = '1.0'; 202 | var signBase = method + '&' + encodeURIComponent(requestURL) + '&' + 'SummarizeErrors=false' + 203 | + encodeURIComponent('oauth_consumer_key=' + this.consumerKey + '&oauth_nonce=' + oauth_nonce + '&oauth_signature_method=' 204 | + oauth_signature_method + '&oauth_timestamp=' + oauth_timestamp + '&oauth_token=' + this.consumerKey + '&oauth_version=' 205 | + oauth_version + '&order='); 206 | if (method == 'POST') 207 | signBase += '&xml=' + xml; 208 | 209 | var rsa = new RSAKey(); 210 | rsa.readPrivateKeyFromPEMString(this.rsaKey); 211 | var sbSigned = rsa.signString(signBase, 'sha1'); 212 | 213 | var data = new Array(); 214 | for (var i =0; i < sbSigned.length; i += 2) 215 | data.push(parseInt("0x" + sbSigned.substr(i, 2))); 216 | var oauth_signature = hex2b64(sbSigned); 217 | 218 | var authHeader = "OAuth oauth_token=\"" + this.consumerKey + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_consumer_key=\"" + this.consumerKey 219 | + "\",oauth_signature_method=\"RSA-SHA1\",oauth_timestamp=\"" + oauth_timestamp + "\",oauth_version=\"1.0\",oauth_signature=\"" 220 | + encodeURIComponent(oauth_signature) + "\""; 221 | var payload = {"order": "", "xml": xml}; 222 | var headers = { "User-Agent": this.userAgent, "Authorization": authHeader, "Accept": "application/json", "muteHttpExceptions": true }; 223 | var options = { "headers": headers, "method": "post", "payload": payload }; 224 | try { 225 | var response = UrlFetchApp.fetch(requestURL + '?SummarizeErrors=false', options); 226 | if (response.getResponseCode() == 200 && response.getHeaders().Status == "OK") 227 | return JSON.parse(response.getContentText()); 228 | else 229 | throw "Request Failed: Response Code: " + response.getResponseCode(); 230 | } 231 | catch(e) { 232 | throw e.message; 233 | } 234 | return false; 235 | } 236 | } 237 | 238 | -------------------------------------------------------------------------------- /rsa/asn1hex-1.1.gs: -------------------------------------------------------------------------------- 1 | /*! asn1hex-1.1.5.js (c) 2012-2014 Kenji Urushima | kjur.github.com/jsrsasign/license 2 | */ 3 | /* 4 | * asn1hex.js - Hexadecimal represented ASN.1 string library 5 | * 6 | * Copyright (c) 2010-2014 Kenji Urushima (kenji.urushima@gmail.com) 7 | * 8 | * This software is licensed under the terms of the MIT License. 9 | * http://kjur.github.com/jsrsasign/license/ 10 | * 11 | * The above copyright and license notice shall be 12 | * included in all copies or substantial portions of the Software. 13 | */ 14 | 15 | /** 16 | * @fileOverview 17 | * @name asn1hex-1.1.js 18 | * @author Kenji Urushima kenji.urushima@gmail.com 19 | * @version asn1hex 1.1.5 (2014-May-25) 20 | * @license MIT License 21 | */ 22 | 23 | /* 24 | * MEMO: 25 | * f('3082025b02...', 2) ... 82025b ... 3bytes 26 | * f('020100', 2) ... 01 ... 1byte 27 | * f('0203001...', 2) ... 03 ... 1byte 28 | * f('02818003...', 2) ... 8180 ... 2bytes 29 | * f('3080....0000', 2) ... 80 ... -1 30 | * 31 | * Requirements: 32 | * - ASN.1 type octet length MUST be 1. 33 | * (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...) 34 | */ 35 | 36 | /** 37 | * ASN.1 DER encoded hexadecimal string utility class 38 | * @name ASN1HEX 39 | * @class ASN.1 DER encoded hexadecimal string utility class 40 | * @since jsrsasign 1.1 41 | */ 42 | var ASN1HEX = new function() { 43 | /** 44 | * get byte length for ASN.1 L(length) bytes 45 | * @name getByteLengthOfL_AtObj 46 | * @memberOf ASN1HEX 47 | * @function 48 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 49 | * @param {Number} pos string index 50 | * @return byte length for ASN.1 L(length) bytes 51 | */ 52 | this.getByteLengthOfL_AtObj = function(s, pos) { 53 | if (s.substring(pos + 2, pos + 3) != '8') return 1; 54 | var i = parseInt(s.substring(pos + 3, pos + 4)); 55 | if (i == 0) return -1; // length octet '80' indefinite length 56 | if (0 < i && i < 10) return i + 1; // including '8?' octet; 57 | return -2; // malformed format 58 | }; 59 | 60 | /** 61 | * get hexadecimal string for ASN.1 L(length) bytes 62 | * @name getHexOfL_AtObj 63 | * @memberOf ASN1HEX 64 | * @function 65 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 66 | * @param {Number} pos string index 67 | * @return {String} hexadecimal string for ASN.1 L(length) bytes 68 | */ 69 | this.getHexOfL_AtObj = function(s, pos) { 70 | var len = this.getByteLengthOfL_AtObj(s, pos); 71 | if (len < 1) return ''; 72 | return s.substring(pos + 2, pos + 2 + len * 2); 73 | }; 74 | 75 | // getting ASN.1 length value at the position 'idx' of 76 | // hexa decimal string 's'. 77 | // 78 | // f('3082025b02...', 0) ... 82025b ... ??? 79 | // f('020100', 0) ... 01 ... 1 80 | // f('0203001...', 0) ... 03 ... 3 81 | // f('02818003...', 0) ... 8180 ... 128 82 | /** 83 | * get integer value of ASN.1 length for ASN.1 data 84 | * @name getIntOfL_AtObj 85 | * @memberOf ASN1HEX 86 | * @function 87 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 88 | * @param {Number} pos string index 89 | * @return ASN.1 L(length) integer value 90 | */ 91 | this.getIntOfL_AtObj = function(s, pos) { 92 | var hLength = this.getHexOfL_AtObj(s, pos); 93 | if (hLength == '') return -1; 94 | var bi; 95 | if (parseInt(hLength.substring(0, 1)) < 8) { 96 | bi = new BigInteger(hLength, 16); 97 | } else { 98 | bi = new BigInteger(hLength.substring(2), 16); 99 | } 100 | return bi.intValue(); 101 | }; 102 | 103 | /** 104 | * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'. 105 | * @name getStartPosOfV_AtObj 106 | * @memberOf ASN1HEX 107 | * @function 108 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 109 | * @param {Number} pos string index 110 | */ 111 | this.getStartPosOfV_AtObj = function(s, pos) { 112 | var l_len = this.getByteLengthOfL_AtObj(s, pos); 113 | if (l_len < 0) return l_len; 114 | return pos + (l_len + 1) * 2; 115 | }; 116 | 117 | /** 118 | * get hexadecimal string of ASN.1 V(value) 119 | * @name getHexOfV_AtObj 120 | * @memberOf ASN1HEX 121 | * @function 122 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 123 | * @param {Number} pos string index 124 | * @return {String} hexadecimal string of ASN.1 value. 125 | */ 126 | this.getHexOfV_AtObj = function(s, pos) { 127 | var pos1 = this.getStartPosOfV_AtObj(s, pos); 128 | var len = this.getIntOfL_AtObj(s, pos); 129 | return s.substring(pos1, pos1 + len * 2); 130 | }; 131 | 132 | /** 133 | * get hexadecimal string of ASN.1 TLV at 134 | * @name getHexOfTLV_AtObj 135 | * @memberOf ASN1HEX 136 | * @function 137 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 138 | * @param {Number} pos string index 139 | * @return {String} hexadecimal string of ASN.1 TLV. 140 | * @since 1.1 141 | */ 142 | this.getHexOfTLV_AtObj = function(s, pos) { 143 | var hT = s.substr(pos, 2); 144 | var hL = this.getHexOfL_AtObj(s, pos); 145 | var hV = this.getHexOfV_AtObj(s, pos); 146 | return hT + hL + hV; 147 | }; 148 | 149 | /** 150 | * get next sibling starting index for ASN.1 object string 151 | * @name getPosOfNextSibling_AtObj 152 | * @memberOf ASN1HEX 153 | * @function 154 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 155 | * @param {Number} pos string index 156 | * @return next sibling starting index for ASN.1 object string 157 | */ 158 | this.getPosOfNextSibling_AtObj = function(s, pos) { 159 | var pos1 = this.getStartPosOfV_AtObj(s, pos); 160 | var len = this.getIntOfL_AtObj(s, pos); 161 | return pos1 + len * 2; 162 | }; 163 | 164 | /** 165 | * get array of indexes of child ASN.1 objects 166 | * @name getPosArrayOfChildren_AtObj 167 | * @memberOf ASN1HEX 168 | * @function 169 | * @param {String} s hexadecimal string of ASN.1 DER encoded data 170 | * @param {Number} start string index of ASN.1 object 171 | * @return {Array of Number} array of indexes for childen of ASN.1 objects 172 | */ 173 | this.getPosArrayOfChildren_AtObj = function(h, pos) { 174 | var a = new Array(); 175 | var p0 = this.getStartPosOfV_AtObj(h, pos); 176 | a.push(p0); 177 | 178 | var len = this.getIntOfL_AtObj(h, pos); 179 | var p = p0; 180 | var k = 0; 181 | while (1) { 182 | var pNext = this.getPosOfNextSibling_AtObj(h, p); 183 | if (pNext == null || (pNext - p0 >= (len * 2))) break; 184 | if (k >= 200) break; 185 | 186 | a.push(pNext); 187 | p = pNext; 188 | 189 | k++; 190 | } 191 | 192 | return a; 193 | }; 194 | 195 | /** 196 | * get string index of nth child object of ASN.1 object refered by h, idx 197 | * @name getNthChildIndex_AtObj 198 | * @memberOf ASN1HEX 199 | * @function 200 | * @param {String} h hexadecimal string of ASN.1 DER encoded data 201 | * @param {Number} idx start string index of ASN.1 object 202 | * @param {Number} nth for child 203 | * @return {Number} string index of nth child. 204 | * @since 1.1 205 | */ 206 | this.getNthChildIndex_AtObj = function(h, idx, nth) { 207 | var a = this.getPosArrayOfChildren_AtObj(h, idx); 208 | return a[nth]; 209 | }; 210 | 211 | // ========== decendant methods ============================== 212 | /** 213 | * get string index of nth child object of ASN.1 object refered by h, idx 214 | * @name getDecendantIndexByNthList 215 | * @memberOf ASN1HEX 216 | * @function 217 | * @param {String} h hexadecimal string of ASN.1 DER encoded data 218 | * @param {Number} currentIndex start string index of ASN.1 object 219 | * @param {Array of Number} nthList array list of nth 220 | * @return {Number} string index refered by nthList 221 | * @since 1.1 222 | * @example 223 | * The "nthList" is a index list of structured ASN.1 object 224 | * reference. Here is a sample structure and "nthList"s which 225 | * refers each objects. 226 | * 227 | * SQUENCE - 228 | * SEQUENCE - [0] 229 | * IA5STRING 000 - [0, 0] 230 | * UTF8STRING 001 - [0, 1] 231 | * SET - [1] 232 | * IA5STRING 010 - [1, 0] 233 | * UTF8STRING 011 - [1, 1] 234 | */ 235 | this.getDecendantIndexByNthList = function(h, currentIndex, nthList) { 236 | if (nthList.length == 0) { 237 | return currentIndex; 238 | } 239 | var firstNth = nthList.shift(); 240 | var a = this.getPosArrayOfChildren_AtObj(h, currentIndex); 241 | return this.getDecendantIndexByNthList(h, a[firstNth], nthList); 242 | }; 243 | 244 | /** 245 | * get hexadecimal string of ASN.1 TLV refered by current index and nth index list. 246 | * @name getDecendantHexTLVByNthList 247 | * @memberOf ASN1HEX 248 | * @function 249 | * @param {String} h hexadecimal string of ASN.1 DER encoded data 250 | * @param {Number} currentIndex start string index of ASN.1 object 251 | * @param {Array of Number} nthList array list of nth 252 | * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList 253 | * @since 1.1 254 | */ 255 | this.getDecendantHexTLVByNthList = function(h, currentIndex, nthList) { 256 | var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); 257 | return this.getHexOfTLV_AtObj(h, idx); 258 | }; 259 | 260 | /** 261 | * get hexadecimal string of ASN.1 V refered by current index and nth index list. 262 | * @name getDecendantHexVByNthList 263 | * @memberOf ASN1HEX 264 | * @function 265 | * @param {String} h hexadecimal string of ASN.1 DER encoded data 266 | * @param {Number} currentIndex start string index of ASN.1 object 267 | * @param {Array of Number} nthList array list of nth 268 | * @return {Number} hexadecimal string of ASN.1 V refered by nthList 269 | * @since 1.1 270 | */ 271 | this.getDecendantHexVByNthList = function(h, currentIndex, nthList) { 272 | var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); 273 | return this.getHexOfV_AtObj(h, idx); 274 | }; 275 | }; 276 | 277 | /* 278 | * @since asn1hex 1.1.4 279 | */ 280 | ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag) { 281 | var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); 282 | if (idx === undefined) { 283 | throw "can't find nthList object"; 284 | } 285 | if (checkingTag !== undefined) { 286 | if (h.substr(idx, 2) != checkingTag) { 287 | throw "checking tag doesn't match: " + 288 | h.substr(idx,2) + "!=" + checkingTag; 289 | } 290 | } 291 | return this.getHexOfV_AtObj(h, idx); 292 | }; 293 | 294 | /** 295 | * get OID string from hexadecimal encoded value 296 | * @name hextooidstr 297 | * @memberOf ASN1HEX 298 | * @function 299 | * @param {String} hex hexadecmal string of ASN.1 DER encoded OID value 300 | * @return {String} OID string (ex. '1.2.3.4.567') 301 | * @since asn1hex 1.1.5 302 | */ 303 | ASN1HEX.hextooidstr = function(hex) { 304 | var zeroPadding = function(s, len) { 305 | if (s.length >= len) return s; 306 | return new Array(len - s.length + 1).join('0') + s; 307 | }; 308 | 309 | var a = []; 310 | 311 | // a[0], a[1] 312 | var hex0 = hex.substr(0, 2); 313 | var i0 = parseInt(hex0, 16); 314 | a[0] = new String(Math.floor(i0 / 40)); 315 | a[1] = new String(i0 % 40); 316 | 317 | // a[2]..a[n] 318 | var hex1 = hex.substr(2); 319 | var b = []; 320 | for (var i = 0; i < hex1.length / 2; i++) { 321 | b.push(parseInt(hex1.substr(i * 2, 2), 16)); 322 | } 323 | var c = []; 324 | var cbin = ""; 325 | for (var i = 0; i < b.length; i++) { 326 | if (b[i] & 0x80) { 327 | cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); 328 | } else { 329 | cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); 330 | c.push(new String(parseInt(cbin, 2))); 331 | cbin = ""; 332 | } 333 | } 334 | 335 | var s = a.join("."); 336 | if (c.length > 0) s = s + "." + c.join("."); 337 | return s; 338 | }; 339 | -------------------------------------------------------------------------------- /rsa/x509-1.1.gs: -------------------------------------------------------------------------------- 1 | /*! x509-1.1.3.js (c) 2012-2014 Kenji Urushima | kjur.github.com/jsrsasign/license 2 | */ 3 | /* 4 | * x509.js - X509 class to read subject public key from certificate. 5 | * 6 | * Copyright (c) 2010-2014 Kenji Urushima (kenji.urushima@gmail.com) 7 | * 8 | * This software is licensed under the terms of the MIT License. 9 | * http://kjur.github.com/jsrsasign/license 10 | * 11 | * The above copyright and license notice shall be 12 | * included in all copies or substantial portions of the Software. 13 | */ 14 | 15 | /** 16 | * @fileOverview 17 | * @name x509-1.1.js 18 | * @author Kenji Urushima kenji.urushima@gmail.com 19 | * @version x509 1.1.3 (2014-May-17) 20 | * @since jsrsasign 1.x.x 21 | * @license MIT License 22 | */ 23 | 24 | /* 25 | * Depends: 26 | * base64.js 27 | * rsa.js 28 | * asn1hex.js 29 | */ 30 | 31 | /** 32 | * X.509 certificate class.
33 | * @class X.509 certificate class 34 | * @property {RSAKey} subjectPublicKeyRSA Tom Wu's RSAKey object 35 | * @property {String} subjectPublicKeyRSA_hN hexadecimal string for modulus of RSA public key 36 | * @property {String} subjectPublicKeyRSA_hE hexadecimal string for public exponent of RSA public key 37 | * @property {String} hex hexacedimal string for X.509 certificate. 38 | * @author Kenji Urushima 39 | * @version 1.0.1 (08 May 2012) 40 | * @see 'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/ 41 | */ 42 | function X509() { 43 | this.subjectPublicKeyRSA = null; 44 | this.subjectPublicKeyRSA_hN = null; 45 | this.subjectPublicKeyRSA_hE = null; 46 | this.hex = null; 47 | 48 | // ===== get basic fields from hex ===================================== 49 | 50 | /** 51 | * get hexadecimal string of serialNumber field of certificate.
52 | * @name getSerialNumberHex 53 | * @memberOf X509# 54 | * @function 55 | */ 56 | this.getSerialNumberHex = function() { 57 | return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]); 58 | }; 59 | 60 | /** 61 | * get hexadecimal string of issuer field TLV of certificate.
62 | * @name getIssuerHex 63 | * @memberOf X509# 64 | * @function 65 | */ 66 | this.getIssuerHex = function() { 67 | return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]); 68 | }; 69 | 70 | /** 71 | * get string of issuer field of certificate.
72 | * @name getIssuerString 73 | * @memberOf X509# 74 | * @function 75 | */ 76 | this.getIssuerString = function() { 77 | return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3])); 78 | }; 79 | 80 | /** 81 | * get hexadecimal string of subject field of certificate.
82 | * @name getSubjectHex 83 | * @memberOf X509# 84 | * @function 85 | */ 86 | this.getSubjectHex = function() { 87 | return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]); 88 | }; 89 | 90 | /** 91 | * get string of subject field of certificate.
92 | * @name getSubjectString 93 | * @memberOf X509# 94 | * @function 95 | */ 96 | this.getSubjectString = function() { 97 | return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5])); 98 | }; 99 | 100 | /** 101 | * get notBefore field string of certificate.
102 | * @name getNotBefore 103 | * @memberOf X509# 104 | * @function 105 | */ 106 | this.getNotBefore = function() { 107 | var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]); 108 | s = s.replace(/(..)/g, "%$1"); 109 | s = decodeURIComponent(s); 110 | return s; 111 | }; 112 | 113 | /** 114 | * get notAfter field string of certificate.
115 | * @name getNotAfter 116 | * @memberOf X509# 117 | * @function 118 | */ 119 | this.getNotAfter = function() { 120 | var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]); 121 | s = s.replace(/(..)/g, "%$1"); 122 | s = decodeURIComponent(s); 123 | return s; 124 | }; 125 | 126 | // ===== read certificate public key ========================== 127 | 128 | // ===== read certificate ===================================== 129 | /** 130 | * read PEM formatted X.509 certificate from string.
131 | * @name readCertPEM 132 | * @memberOf X509# 133 | * @function 134 | * @param {String} sCertPEM string for PEM formatted X.509 certificate 135 | */ 136 | this.readCertPEM = function(sCertPEM) { 137 | var hCert = X509.pemToHex(sCertPEM); 138 | var a = X509.getPublicKeyHexArrayFromCertHex(hCert); 139 | var rsa = new RSAKey(); 140 | rsa.setPublic(a[0], a[1]); 141 | this.subjectPublicKeyRSA = rsa; 142 | this.subjectPublicKeyRSA_hN = a[0]; 143 | this.subjectPublicKeyRSA_hE = a[1]; 144 | this.hex = hCert; 145 | }; 146 | 147 | this.readCertPEMWithoutRSAInit = function(sCertPEM) { 148 | var hCert = X509.pemToHex(sCertPEM); 149 | var a = X509.getPublicKeyHexArrayFromCertHex(hCert); 150 | this.subjectPublicKeyRSA.setPublic(a[0], a[1]); 151 | this.subjectPublicKeyRSA_hN = a[0]; 152 | this.subjectPublicKeyRSA_hE = a[1]; 153 | this.hex = hCert; 154 | }; 155 | }; 156 | 157 | X509.pemToBase64 = function(sCertPEM) { 158 | var s = sCertPEM; 159 | s = s.replace("-----BEGIN CERTIFICATE-----", ""); 160 | s = s.replace("-----END CERTIFICATE-----", ""); 161 | s = s.replace(/[ \n]+/g, ""); 162 | return s; 163 | }; 164 | 165 | X509.pemToHex = function(sCertPEM) { 166 | var b64Cert = X509.pemToBase64(sCertPEM); 167 | var hCert = b64tohex(b64Cert); 168 | return hCert; 169 | }; 170 | 171 | // NOTE: Without BITSTRING encapsulation. 172 | X509.getSubjectPublicKeyPosFromCertHex = function(hCert) { 173 | var pInfo = X509.getSubjectPublicKeyInfoPosFromCertHex(hCert); 174 | if (pInfo == -1) return -1; 175 | var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo); 176 | if (a.length != 2) return -1; 177 | var pBitString = a[1]; 178 | if (hCert.substring(pBitString, pBitString + 2) != '03') return -1; 179 | var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString); 180 | 181 | if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1; 182 | return pBitStringV + 2; 183 | }; 184 | 185 | // NOTE: privateKeyUsagePeriod field of X509v2 not supported. 186 | // NOTE: v1 and v3 supported 187 | X509.getSubjectPublicKeyInfoPosFromCertHex = function(hCert) { 188 | var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); 189 | var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert); 190 | if (a.length < 1) return -1; 191 | if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3 192 | if (a.length < 6) return -1; 193 | return a[6]; 194 | } else { 195 | if (a.length < 5) return -1; 196 | return a[5]; 197 | } 198 | }; 199 | 200 | X509.getPublicKeyHexArrayFromCertHex = function(hCert) { 201 | var p = X509.getSubjectPublicKeyPosFromCertHex(hCert); 202 | var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p); 203 | if (a.length != 2) return []; 204 | var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]); 205 | var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]); 206 | if (hN != null && hE != null) { 207 | return [hN, hE]; 208 | } else { 209 | return []; 210 | } 211 | }; 212 | 213 | X509.getHexTbsCertificateFromCert = function(hCert) { 214 | var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); 215 | return pTbsCert; 216 | }; 217 | 218 | X509.getPublicKeyHexArrayFromCertPEM = function(sCertPEM) { 219 | var hCert = X509.pemToHex(sCertPEM); 220 | var a = X509.getPublicKeyHexArrayFromCertHex(hCert); 221 | return a; 222 | }; 223 | 224 | X509.hex2dn = function(hDN) { 225 | var s = ""; 226 | var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0); 227 | for (var i = 0; i < a.length; i++) { 228 | var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]); 229 | s = s + "/" + X509.hex2rdn(hRDN); 230 | } 231 | return s; 232 | }; 233 | 234 | X509.hex2rdn = function(hRDN) { 235 | var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]); 236 | var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]); 237 | var type = ""; 238 | try { type = X509.DN_ATTRHEX[hType]; } catch (ex) { type = hType; } 239 | hValue = hValue.replace(/(..)/g, "%$1"); 240 | var value = decodeURIComponent(hValue); 241 | return type + "=" + value; 242 | }; 243 | 244 | X509.DN_ATTRHEX = { 245 | "0603550406": "C", 246 | "060355040a": "O", 247 | "060355040b": "OU", 248 | "0603550403": "CN", 249 | "0603550405": "SN", 250 | "0603550408": "ST", 251 | "0603550407": "L", 252 | }; 253 | 254 | /** 255 | * get RSAKey/ECDSA public key object from PEM certificate string 256 | * @name getPublicKeyFromCertPEM 257 | * @memberOf X509 258 | * @function 259 | * @param {String} sCertPEM PEM formatted RSA/ECDSA/DSA X.509 certificate 260 | * @return returns RSAKey/KJUR.crypto.{ECDSA,DSA} object of public key 261 | * @since x509 1.1.1 262 | * @description 263 | * NOTE: DSA is also supported since x509 1.1.2. 264 | */ 265 | X509.getPublicKeyFromCertPEM = function(sCertPEM) { 266 | var info = X509.getPublicKeyInfoPropOfCertPEM(sCertPEM); 267 | 268 | if (info.algoid == "2a864886f70d010101") { // RSA 269 | var aRSA = KEYUTIL.parsePublicRawRSAKeyHex(info.keyhex); 270 | var key = new RSAKey(); 271 | key.setPublic(aRSA.n, aRSA.e); 272 | return key; 273 | } else if (info.algoid == "2a8648ce3d0201") { // ECC 274 | var curveName = KJUR.crypto.OID.oidhex2name[info.algparam]; 275 | var key = new KJUR.crypto.ECDSA({'curve': curveName, 'info': info.keyhex}); 276 | key.setPublicKeyHex(info.keyhex); 277 | return key; 278 | } else if (info.algoid == "2a8648ce380401") { // DSA 1.2.840.10040.4.1 279 | var p = ASN1HEX.getVbyList(info.algparam, 0, [0], "02"); 280 | var q = ASN1HEX.getVbyList(info.algparam, 0, [1], "02"); 281 | var g = ASN1HEX.getVbyList(info.algparam, 0, [2], "02"); 282 | var y = ASN1HEX.getHexOfV_AtObj(info.keyhex, 0); 283 | y = y.substr(2); 284 | var key = new KJUR.crypto.DSA(); 285 | key.setPublic(new BigInteger(p, 16), 286 | new BigInteger(q, 16), 287 | new BigInteger(g, 16), 288 | new BigInteger(y, 16)); 289 | return key; 290 | } else { 291 | throw "unsupported key"; 292 | } 293 | }; 294 | 295 | /** 296 | * get public key information from PEM certificate 297 | * @name getPublicKeyInfoPropOfCertPEM 298 | * @memberOf X509 299 | * @function 300 | * @param {String} sCertPEM string of PEM formatted certificate 301 | * @return {Hash} hash of information for public key 302 | * @since x509 1.1.1 303 | * @description 304 | * Resulted associative array has following properties: 305 | * 310 | * @since x509 1.1.1 311 | */ 312 | X509.getPublicKeyInfoPropOfCertPEM = function(sCertPEM) { 313 | var result = {}; 314 | result.algparam = null; 315 | var hCert = X509.pemToHex(sCertPEM); 316 | 317 | // 1. Certificate ASN.1 318 | var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0); 319 | if (a1.length != 3) 320 | throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert 321 | 322 | // 2. tbsCertificate 323 | if (hCert.substr(a1[0], 2) != "30") 324 | throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq 325 | 326 | var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]); 327 | 328 | // 3. subjectPublicKeyInfo 329 | if (a2.length < 7) 330 | throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo 331 | 332 | var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[6]); 333 | 334 | if (a3.length != 2) 335 | throw "malformed X.509 certificate PEM (code:004)"; // not AlgId and PubKey 336 | 337 | // 4. AlgId 338 | var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]); 339 | 340 | if (a4.length != 2) 341 | throw "malformed X.509 certificate PEM (code:005)"; // not 2 item in AlgId 342 | 343 | result.algoid = ASN1HEX.getHexOfV_AtObj(hCert, a4[0]); 344 | 345 | if (hCert.substr(a4[1], 2) == "06") { // EC 346 | result.algparam = ASN1HEX.getHexOfV_AtObj(hCert, a4[1]); 347 | } else if (hCert.substr(a4[1], 2) == "30") { // DSA 348 | result.algparam = ASN1HEX.getHexOfTLV_AtObj(hCert, a4[1]); 349 | } 350 | 351 | // 5. Public Key Hex 352 | if (hCert.substr(a3[1], 2) != "03") 353 | throw "malformed X.509 certificate PEM (code:006)"; // not bitstring 354 | 355 | var unusedBitAndKeyHex = ASN1HEX.getHexOfV_AtObj(hCert, a3[1]); 356 | result.keyhex = unusedBitAndKeyHex.substr(2); 357 | 358 | return result; 359 | }; 360 | 361 | /* 362 | X509.prototype.readCertPEM = _x509_readCertPEM; 363 | X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit; 364 | X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex; 365 | X509.prototype.getIssuerHex = _x509_getIssuerHex; 366 | X509.prototype.getSubjectHex = _x509_getSubjectHex; 367 | X509.prototype.getIssuerString = _x509_getIssuerString; 368 | X509.prototype.getSubjectString = _x509_getSubjectString; 369 | X509.prototype.getNotBefore = _x509_getNotBefore; 370 | X509.prototype.getNotAfter = _x509_getNotAfter; 371 | */ 372 | -------------------------------------------------------------------------------- /rsa/rsasign-1.2.gs: -------------------------------------------------------------------------------- 1 | /*! rsasign-1.2.7.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license 2 | */ 3 | /* 4 | * rsa-sign.js - adding signing functions to RSAKey class. 5 | * 6 | * version: 1.2.7 (2013 Aug 25) 7 | * 8 | * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com) 9 | * 10 | * This software is licensed under the terms of the MIT License. 11 | * http://kjur.github.com/jsrsasign/license/ 12 | * 13 | * The above copyright and license notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | */ 16 | 17 | /** 18 | * @fileOverview 19 | * @name rsasign-1.2.js 20 | * @author Kenji Urushima kenji.urushima@gmail.com 21 | * @version rsasign 1.2.7 22 | * @license MIT License 23 | */ 24 | 25 | var _RE_HEXDECONLY = new RegExp(""); 26 | _RE_HEXDECONLY.compile("[^0-9a-f]", "gi"); 27 | 28 | // ======================================================================== 29 | // Signature Generation 30 | // ======================================================================== 31 | 32 | function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) { 33 | var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); }; 34 | var sHashHex = hashFunc(s); 35 | 36 | return KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, keySize); 37 | } 38 | 39 | function _zeroPaddingOfSignature(hex, bitLength) { 40 | var s = ""; 41 | var nZero = bitLength / 4 - hex.length; 42 | for (var i = 0; i < nZero; i++) { 43 | s = s + "0"; 44 | } 45 | return s + hex; 46 | } 47 | 48 | /** 49 | * sign for a message string with RSA private key.
50 | * @name signString 51 | * @memberOf RSAKey 52 | * @function 53 | * @param {String} s message string to be signed. 54 | * @param {String} hashAlg hash algorithm name for signing.
55 | * @return returns hexadecimal string of signature value. 56 | */ 57 | function _rsasign_signString(s, hashAlg) { 58 | var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); }; 59 | var sHashHex = hashFunc(s); 60 | 61 | return this.signWithMessageHash(sHashHex, hashAlg); 62 | } 63 | 64 | /** 65 | * sign hash value of message to be signed with RSA private key.
66 | * @name signWithMessageHash 67 | * @memberOf RSAKey 68 | * @function 69 | * @param {String} sHashHex hexadecimal string of hash value of message to be signed. 70 | * @param {String} hashAlg hash algorithm name for signing.
71 | * @return returns hexadecimal string of signature value. 72 | * @since rsasign 1.2.6 73 | */ 74 | function _rsasign_signWithMessageHash(sHashHex, hashAlg) { 75 | var hPM = KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, this.n.bitLength()); 76 | var biPaddedMessage = parseBigInt(hPM, 16); 77 | var biSign = this.doPrivate(biPaddedMessage); 78 | var hexSign = biSign.toString(16); 79 | return _zeroPaddingOfSignature(hexSign, this.n.bitLength()); 80 | } 81 | 82 | function _rsasign_signStringWithSHA1(s) { 83 | return _rsasign_signString.call(this, s, 'sha1'); 84 | } 85 | 86 | function _rsasign_signStringWithSHA256(s) { 87 | return _rsasign_signString.call(this, s, 'sha256'); 88 | } 89 | 90 | // PKCS#1 (PSS) mask generation function 91 | function pss_mgf1_str(seed, len, hash) { 92 | var mask = '', i = 0; 93 | 94 | while (mask.length < len) { 95 | mask += hextorstr(hash(rstrtohex(seed + String.fromCharCode.apply(String, [ 96 | (i & 0xff000000) >> 24, 97 | (i & 0x00ff0000) >> 16, 98 | (i & 0x0000ff00) >> 8, 99 | i & 0x000000ff])))); 100 | i += 1; 101 | } 102 | 103 | return mask; 104 | } 105 | 106 | /** 107 | * sign for a message string with RSA private key by PKCS#1 PSS signing.
108 | * @name signStringPSS 109 | * @memberOf RSAKey 110 | * @function 111 | * @param {String} s message string to be signed. 112 | * @param {String} hashAlg hash algorithm name for signing. 113 | * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2). 114 | * There are two special values: 115 | * 120 | * DEFAULT is -1. (NOTE: OpenSSL's default is -2.) 121 | * @return returns hexadecimal string of signature value. 122 | */ 123 | function _rsasign_signStringPSS(s, hashAlg, sLen) { 124 | var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); } 125 | var hHash = hashFunc(rstrtohex(s)); 126 | 127 | if (sLen === undefined) sLen = -1; 128 | return this.signWithMessageHashPSS(hHash, hashAlg, sLen); 129 | } 130 | 131 | /** 132 | * sign hash value of message with RSA private key by PKCS#1 PSS signing.
133 | * @name signWithMessageHashPSS 134 | * @memberOf RSAKey 135 | * @function 136 | * @param {String} hHash hexadecimal hash value of message to be signed. 137 | * @param {String} hashAlg hash algorithm name for signing. 138 | * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2). 139 | * There are two special values: 140 | * 145 | * DEFAULT is -1. (NOTE: OpenSSL's default is -2.) 146 | * @return returns hexadecimal string of signature value. 147 | * @since rsasign 1.2.6 148 | */ 149 | function _rsasign_signWithMessageHashPSS(hHash, hashAlg, sLen) { 150 | var mHash = hextorstr(hHash); 151 | var hLen = mHash.length; 152 | var emBits = this.n.bitLength() - 1; 153 | var emLen = Math.ceil(emBits / 8); 154 | var i; 155 | var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); } 156 | 157 | if (sLen === -1 || sLen === undefined) { 158 | sLen = hLen; // same as hash length 159 | } else if (sLen === -2) { 160 | sLen = emLen - hLen - 2; // maximum 161 | } else if (sLen < -2) { 162 | throw "invalid salt length"; 163 | } 164 | 165 | if (emLen < (hLen + sLen + 2)) { 166 | throw "data too long"; 167 | } 168 | 169 | var salt = ''; 170 | 171 | if (sLen > 0) { 172 | salt = new Array(sLen); 173 | new SecureRandom().nextBytes(salt); 174 | salt = String.fromCharCode.apply(String, salt); 175 | } 176 | 177 | var H = hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash + salt))); 178 | var PS = []; 179 | 180 | for (i = 0; i < emLen - sLen - hLen - 2; i += 1) { 181 | PS[i] = 0x00; 182 | } 183 | 184 | var DB = String.fromCharCode.apply(String, PS) + '\x01' + salt; 185 | var dbMask = pss_mgf1_str(H, DB.length, hashFunc); 186 | var maskedDB = []; 187 | 188 | for (i = 0; i < DB.length; i += 1) { 189 | maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i); 190 | } 191 | 192 | var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff; 193 | maskedDB[0] &= ~mask; 194 | 195 | for (i = 0; i < hLen; i++) { 196 | maskedDB.push(H.charCodeAt(i)); 197 | } 198 | 199 | maskedDB.push(0xbc); 200 | 201 | return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(maskedDB)).toString(16), 202 | this.n.bitLength()); 203 | } 204 | 205 | // ======================================================================== 206 | // Signature Verification 207 | // ======================================================================== 208 | 209 | function _rsasign_getDecryptSignatureBI(biSig, hN, hE) { 210 | var rsa = new RSAKey(); 211 | rsa.setPublic(hN, hE); 212 | var biDecryptedSig = rsa.doPublic(biSig); 213 | return biDecryptedSig; 214 | } 215 | 216 | function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) { 217 | var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE); 218 | var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, ''); 219 | return hDigestInfo; 220 | } 221 | 222 | function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) { 223 | for (var algName in KJUR.crypto.Util.DIGESTINFOHEAD) { 224 | var head = KJUR.crypto.Util.DIGESTINFOHEAD[algName]; 225 | var len = head.length; 226 | if (hDigestInfo.substring(0, len) == head) { 227 | var a = [algName, hDigestInfo.substring(len)]; 228 | return a; 229 | } 230 | } 231 | return []; 232 | } 233 | 234 | function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) { 235 | var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE); 236 | var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo); 237 | if (digestInfoAry.length == 0) return false; 238 | var algName = digestInfoAry[0]; 239 | var diHashValue = digestInfoAry[1]; 240 | var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); }; 241 | var msgHashValue = ff(sMsg); 242 | return (diHashValue == msgHashValue); 243 | } 244 | 245 | function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) { 246 | var biSig = parseBigInt(hSig, 16); 247 | var result = _rsasign_verifySignatureWithArgs(sMsg, biSig, 248 | this.n.toString(16), 249 | this.e.toString(16)); 250 | return result; 251 | } 252 | 253 | /** 254 | * verifies a sigature for a message string with RSA public key.
255 | * @name verifyString 256 | * @memberOf RSAKey# 257 | * @function 258 | * @param {String} sMsg message string to be verified. 259 | * @param {String} hSig hexadecimal string of siganture.
260 | * non-hexadecimal charactors including new lines will be ignored. 261 | * @return returns 1 if valid, otherwise 0 262 | */ 263 | function _rsasign_verifyString(sMsg, hSig) { 264 | hSig = hSig.replace(_RE_HEXDECONLY, ''); 265 | hSig = hSig.replace(/[ \n]+/g, ""); 266 | var biSig = parseBigInt(hSig, 16); 267 | if (biSig.bitLength() > this.n.bitLength()) return 0; 268 | var biDecryptedSig = this.doPublic(biSig); 269 | var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, ''); 270 | var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo); 271 | 272 | if (digestInfoAry.length == 0) return false; 273 | var algName = digestInfoAry[0]; 274 | var diHashValue = digestInfoAry[1]; 275 | var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); }; 276 | var msgHashValue = ff(sMsg); 277 | return (diHashValue == msgHashValue); 278 | } 279 | 280 | /** 281 | * verifies a sigature for a message string with RSA public key.
282 | * @name verifyWithMessageHash 283 | * @memberOf RSAKey 284 | * @function 285 | * @param {String} sHashHex hexadecimal hash value of message to be verified. 286 | * @param {String} hSig hexadecimal string of siganture.
287 | * non-hexadecimal charactors including new lines will be ignored. 288 | * @return returns 1 if valid, otherwise 0 289 | * @since rsasign 1.2.6 290 | */ 291 | function _rsasign_verifyWithMessageHash(sHashHex, hSig) { 292 | hSig = hSig.replace(_RE_HEXDECONLY, ''); 293 | hSig = hSig.replace(/[ \n]+/g, ""); 294 | var biSig = parseBigInt(hSig, 16); 295 | if (biSig.bitLength() > this.n.bitLength()) return 0; 296 | var biDecryptedSig = this.doPublic(biSig); 297 | var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, ''); 298 | var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo); 299 | 300 | if (digestInfoAry.length == 0) return false; 301 | var algName = digestInfoAry[0]; 302 | var diHashValue = digestInfoAry[1]; 303 | return (diHashValue == sHashHex); 304 | } 305 | 306 | /** 307 | * verifies a sigature for a message string with RSA public key by PKCS#1 PSS sign.
308 | * @name verifyStringPSS 309 | * @memberOf RSAKey 310 | * @function 311 | * @param {String} sMsg message string to be verified. 312 | * @param {String} hSig hexadecimal string of signature value 313 | * @param {String} hashAlg hash algorithm name 314 | * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2). 315 | * There are two special values: 316 | * 321 | * DEFAULT is -1. (NOTE: OpenSSL's default is -2.) 322 | * @return returns true if valid, otherwise false 323 | */ 324 | function _rsasign_verifyStringPSS(sMsg, hSig, hashAlg, sLen) { 325 | var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); }; 326 | var hHash = hashFunc(rstrtohex(sMsg)); 327 | 328 | if (sLen === undefined) sLen = -1; 329 | return this.verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen); 330 | } 331 | 332 | /** 333 | * verifies a sigature for a hash value of message string with RSA public key by PKCS#1 PSS sign.
334 | * @name verifyWithMessageHashPSS 335 | * @memberOf RSAKey 336 | * @function 337 | * @param {String} hHash hexadecimal hash value of message string to be verified. 338 | * @param {String} hSig hexadecimal string of signature value 339 | * @param {String} hashAlg hash algorithm name 340 | * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2). 341 | * There are two special values: 342 | * 347 | * DEFAULT is -1 (NOTE: OpenSSL's default is -2.) 348 | * @return returns true if valid, otherwise false 349 | * @since rsasign 1.2.6 350 | */ 351 | function _rsasign_verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen) { 352 | var biSig = new BigInteger(hSig, 16); 353 | 354 | if (biSig.bitLength() > this.n.bitLength()) { 355 | return false; 356 | } 357 | 358 | var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); }; 359 | var mHash = hextorstr(hHash); 360 | var hLen = mHash.length; 361 | var emBits = this.n.bitLength() - 1; 362 | var emLen = Math.ceil(emBits / 8); 363 | var i; 364 | 365 | if (sLen === -1 || sLen === undefined) { 366 | sLen = hLen; // same as hash length 367 | } else if (sLen === -2) { 368 | sLen = emLen - hLen - 2; // recover 369 | } else if (sLen < -2) { 370 | throw "invalid salt length"; 371 | } 372 | 373 | if (emLen < (hLen + sLen + 2)) { 374 | throw "data too long"; 375 | } 376 | 377 | var em = this.doPublic(biSig).toByteArray(); 378 | 379 | for (i = 0; i < em.length; i += 1) { 380 | em[i] &= 0xff; 381 | } 382 | 383 | while (em.length < emLen) { 384 | em.unshift(0); 385 | } 386 | 387 | if (em[emLen -1] !== 0xbc) { 388 | throw "encoded message does not end in 0xbc"; 389 | } 390 | 391 | em = String.fromCharCode.apply(String, em); 392 | 393 | var maskedDB = em.substr(0, emLen - hLen - 1); 394 | var H = em.substr(maskedDB.length, hLen); 395 | 396 | var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff; 397 | 398 | if ((maskedDB.charCodeAt(0) & mask) !== 0) { 399 | throw "bits beyond keysize not zero"; 400 | } 401 | 402 | var dbMask = pss_mgf1_str(H, maskedDB.length, hashFunc); 403 | var DB = []; 404 | 405 | for (i = 0; i < maskedDB.length; i += 1) { 406 | DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i); 407 | } 408 | 409 | DB[0] &= ~mask; 410 | 411 | var checkLen = emLen - hLen - sLen - 2; 412 | 413 | for (i = 0; i < checkLen; i += 1) { 414 | if (DB[i] !== 0x00) { 415 | throw "leftmost octets not zero"; 416 | } 417 | } 418 | 419 | if (DB[checkLen] !== 0x01) { 420 | throw "0x01 marker not found"; 421 | } 422 | 423 | return H === hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash + 424 | String.fromCharCode.apply(String, DB.slice(-sLen))))); 425 | } 426 | 427 | RSAKey.prototype.signWithMessageHash = _rsasign_signWithMessageHash; 428 | RSAKey.prototype.signString = _rsasign_signString; 429 | RSAKey.prototype.signStringWithSHA1 = _rsasign_signStringWithSHA1; 430 | RSAKey.prototype.signStringWithSHA256 = _rsasign_signStringWithSHA256; 431 | RSAKey.prototype.sign = _rsasign_signString; 432 | RSAKey.prototype.signWithSHA1 = _rsasign_signStringWithSHA1; 433 | RSAKey.prototype.signWithSHA256 = _rsasign_signStringWithSHA256; 434 | 435 | RSAKey.prototype.signWithMessageHashPSS = _rsasign_signWithMessageHashPSS; 436 | RSAKey.prototype.signStringPSS = _rsasign_signStringPSS; 437 | RSAKey.prototype.signPSS = _rsasign_signStringPSS; 438 | RSAKey.SALT_LEN_HLEN = -1; 439 | RSAKey.SALT_LEN_MAX = -2; 440 | 441 | RSAKey.prototype.verifyWithMessageHash = _rsasign_verifyWithMessageHash; 442 | RSAKey.prototype.verifyString = _rsasign_verifyString; 443 | RSAKey.prototype.verifyHexSignatureForMessage = _rsasign_verifyHexSignatureForMessage; 444 | RSAKey.prototype.verify = _rsasign_verifyString; 445 | RSAKey.prototype.verifyHexSignatureForByteArrayMessage = _rsasign_verifyHexSignatureForMessage; 446 | 447 | RSAKey.prototype.verifyWithMessageHashPSS = _rsasign_verifyWithMessageHashPSS; 448 | RSAKey.prototype.verifyStringPSS = _rsasign_verifyStringPSS; 449 | RSAKey.prototype.verifyPSS = _rsasign_verifyStringPSS; 450 | RSAKey.SALT_LEN_RECOVER = -2; 451 | 452 | /** 453 | * @name RSAKey 454 | * @class key of RSA public key algorithm 455 | * @description Tom Wu's RSA Key class and extension 456 | */ 457 | -------------------------------------------------------------------------------- /rsa/jsbn.gs: -------------------------------------------------------------------------------- 1 | /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ 2 | */ 3 | // Copyright (c) 2005 Tom Wu 4 | // All Rights Reserved. 5 | // See "LICENSE" for details. 6 | 7 | // Basic JavaScript BN library - subset useful for RSA encryption. 8 | 9 | // Bits per digit 10 | var dbits; 11 | 12 | // JavaScript engine analysis 13 | var canary = 0xdeadbeefcafe; 14 | var j_lm = ((canary&0xffffff)==0xefcafe); 15 | 16 | // (public) Constructor 17 | function BigInteger(a,b,c) { 18 | if(a != null) 19 | if("number" == typeof a) this.fromNumber(a,b,c); 20 | else if(b == null && "string" != typeof a) this.fromString(a,256); 21 | else this.fromString(a,b); 22 | } 23 | 24 | // return new, unset BigInteger 25 | function nbi() { return new BigInteger(null); } 26 | 27 | // am: Compute w_j += (x*this_i), propagate carries, 28 | // c is initial carry, returns final carry. 29 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue 30 | // We need to select the fastest one that works in this environment. 31 | 32 | // am1: use a single mult and divide to get the high bits, 33 | // max digit bits should be 26 because 34 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53) 35 | function am1(i,x,w,j,c,n) { 36 | while(--n >= 0) { 37 | var v = x*this[i++]+w[j]+c; 38 | c = Math.floor(v/0x4000000); 39 | w[j++] = v&0x3ffffff; 40 | } 41 | return c; 42 | } 43 | // am2 avoids a big mult-and-extract completely. 44 | // Max digit bits should be <= 30 because we do bitwise ops 45 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) 46 | function am2(i,x,w,j,c,n) { 47 | var xl = x&0x7fff, xh = x>>15; 48 | while(--n >= 0) { 49 | var l = this[i]&0x7fff; 50 | var h = this[i++]>>15; 51 | var m = xh*l+h*xl; 52 | l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); 53 | c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); 54 | w[j++] = l&0x3fffffff; 55 | } 56 | return c; 57 | } 58 | // Alternately, set max digit bits to 28 since some 59 | // browsers slow down when dealing with 32-bit numbers. 60 | function am3(i,x,w,j,c,n) { 61 | var xl = x&0x3fff, xh = x>>14; 62 | while(--n >= 0) { 63 | var l = this[i]&0x3fff; 64 | var h = this[i++]>>14; 65 | var m = xh*l+h*xl; 66 | l = xl*l+((m&0x3fff)<<14)+w[j]+c; 67 | c = (l>>28)+(m>>14)+xh*h; 68 | w[j++] = l&0xfffffff; 69 | } 70 | return c; 71 | } 72 | /*if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { 73 | BigInteger.prototype.am = am2; 74 | dbits = 30; 75 | } 76 | else if(j_lm && (navigator.appName != "Netscape")) { 77 | BigInteger.prototype.am = am1; 78 | dbits = 26; 79 | } 80 | else { // Mozilla/Netscape seems to prefer am3 81 | BigInteger.prototype.am = am3; 82 | dbits = 28; 83 | }*/ 84 | BigInteger.prototype.am = am3; 85 | dbits = 28; 86 | 87 | BigInteger.prototype.DB = dbits; 88 | BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; 116 | r.t = this.t; 117 | r.s = this.s; 118 | } 119 | 120 | // (protected) set from integer value x, -DV <= x < DV 121 | function bnpFromInt(x) { 122 | this.t = 1; 123 | this.s = (x<0)?-1:0; 124 | if(x > 0) this[0] = x; 125 | else if(x < -1) this[0] = x+this.DV; 126 | else this.t = 0; 127 | } 128 | 129 | // return bigint initialized to value 130 | function nbv(i) { var r = nbi(); r.fromInt(i); return r; } 131 | 132 | // (protected) set from string and radix 133 | function bnpFromString(s,b) { 134 | var k; 135 | if(b == 16) k = 4; 136 | else if(b == 8) k = 3; 137 | else if(b == 256) k = 8; // byte array 138 | else if(b == 2) k = 1; 139 | else if(b == 32) k = 5; 140 | else if(b == 4) k = 2; 141 | else { this.fromRadix(s,b); return; } 142 | this.t = 0; 143 | this.s = 0; 144 | var i = s.length, mi = false, sh = 0; 145 | while(--i >= 0) { 146 | var x = (k==8)?s[i]&0xff:intAt(s,i); 147 | if(x < 0) { 148 | if(s.charAt(i) == "-") mi = true; 149 | continue; 150 | } 151 | mi = false; 152 | if(sh == 0) 153 | this[this.t++] = x; 154 | else if(sh+k > this.DB) { 155 | this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); 157 | } 158 | else 159 | this[this.t-1] |= x<= this.DB) sh -= this.DB; 162 | } 163 | if(k == 8 && (s[0]&0x80) != 0) { 164 | this.s = -1; 165 | if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; 175 | } 176 | 177 | // (public) return string representation in given radix 178 | function bnToString(b) { 179 | if(this.s < 0) return "-"+this.negate().toString(b); 180 | var k; 181 | if(b == 16) k = 4; 182 | else if(b == 8) k = 3; 183 | else if(b == 2) k = 1; 184 | else if(b == 32) k = 5; 185 | else if(b == 4) k = 2; 186 | else return this.toRadix(b); 187 | var km = (1< 0) { 190 | if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } 191 | while(i >= 0) { 192 | if(p < k) { 193 | d = (this[i]&((1<>(p+=this.DB-k); 195 | } 196 | else { 197 | d = (this[i]>>(p-=k))&km; 198 | if(p <= 0) { p += this.DB; --i; } 199 | } 200 | if(d > 0) m = true; 201 | if(m) r += int2char(d); 202 | } 203 | } 204 | return m?r:"0"; 205 | } 206 | 207 | // (public) -this 208 | function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } 209 | 210 | // (public) |this| 211 | function bnAbs() { return (this.s<0)?this.negate():this; } 212 | 213 | // (public) return + if this > a, - if this < a, 0 if equal 214 | function bnCompareTo(a) { 215 | var r = this.s-a.s; 216 | if(r != 0) return r; 217 | var i = this.t; 218 | r = i-a.t; 219 | if(r != 0) return (this.s<0)?-r:r; 220 | while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; 221 | return 0; 222 | } 223 | 224 | // returns bit length of the integer x 225 | function nbits(x) { 226 | var r = 1, t; 227 | if((t=x>>>16) != 0) { x = t; r += 16; } 228 | if((t=x>>8) != 0) { x = t; r += 8; } 229 | if((t=x>>4) != 0) { x = t; r += 4; } 230 | if((t=x>>2) != 0) { x = t; r += 2; } 231 | if((t=x>>1) != 0) { x = t; r += 1; } 232 | return r; 233 | } 234 | 235 | // (public) return the number of bits in "this" 236 | function bnBitLength() { 237 | if(this.t <= 0) return 0; 238 | return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); 239 | } 240 | 241 | // (protected) r = this << n*DB 242 | function bnpDLShiftTo(n,r) { 243 | var i; 244 | for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; 245 | for(i = n-1; i >= 0; --i) r[i] = 0; 246 | r.t = this.t+n; 247 | r.s = this.s; 248 | } 249 | 250 | // (protected) r = this >> n*DB 251 | function bnpDRShiftTo(n,r) { 252 | for(var i = n; i < this.t; ++i) r[i-n] = this[i]; 253 | r.t = Math.max(this.t-n,0); 254 | r.s = this.s; 255 | } 256 | 257 | // (protected) r = this << n 258 | function bnpLShiftTo(n,r) { 259 | var bs = n%this.DB; 260 | var cbs = this.DB-bs; 261 | var bm = (1<= 0; --i) { 264 | r[i+ds+1] = (this[i]>>cbs)|c; 265 | c = (this[i]&bm)<= 0; --i) r[i] = 0; 268 | r[ds] = c; 269 | r.t = this.t+ds+1; 270 | r.s = this.s; 271 | r.clamp(); 272 | } 273 | 274 | // (protected) r = this >> n 275 | function bnpRShiftTo(n,r) { 276 | r.s = this.s; 277 | var ds = Math.floor(n/this.DB); 278 | if(ds >= this.t) { r.t = 0; return; } 279 | var bs = n%this.DB; 280 | var cbs = this.DB-bs; 281 | var bm = (1<>bs; 283 | for(var i = ds+1; i < this.t; ++i) { 284 | r[i-ds-1] |= (this[i]&bm)<>bs; 286 | } 287 | if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; 299 | } 300 | if(a.t < this.t) { 301 | c -= a.s; 302 | while(i < this.t) { 303 | c += this[i]; 304 | r[i++] = c&this.DM; 305 | c >>= this.DB; 306 | } 307 | c += this.s; 308 | } 309 | else { 310 | c += this.s; 311 | while(i < a.t) { 312 | c -= a[i]; 313 | r[i++] = c&this.DM; 314 | c >>= this.DB; 315 | } 316 | c -= a.s; 317 | } 318 | r.s = (c<0)?-1:0; 319 | if(c < -1) r[i++] = this.DV+c; 320 | else if(c > 0) r[i++] = c; 321 | r.t = i; 322 | r.clamp(); 323 | } 324 | 325 | // (protected) r = this * a, r != this,a (HAC 14.12) 326 | // "this" should be the larger one if appropriate. 327 | function bnpMultiplyTo(a,r) { 328 | var x = this.abs(), y = a.abs(); 329 | var i = x.t; 330 | r.t = i+y.t; 331 | while(--i >= 0) r[i] = 0; 332 | for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); 333 | r.s = 0; 334 | r.clamp(); 335 | if(this.s != a.s) BigInteger.ZERO.subTo(r,r); 336 | } 337 | 338 | // (protected) r = this^2, r != this (HAC 14.16) 339 | function bnpSquareTo(r) { 340 | var x = this.abs(); 341 | var i = r.t = 2*x.t; 342 | while(--i >= 0) r[i] = 0; 343 | for(i = 0; i < x.t-1; ++i) { 344 | var c = x.am(i,x[i],r,2*i,0,1); 345 | if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { 346 | r[i+x.t] -= x.DV; 347 | r[i+x.t+1] = 1; 348 | } 349 | } 350 | if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); 351 | r.s = 0; 352 | r.clamp(); 353 | } 354 | 355 | // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) 356 | // r != q, this != m. q or r may be null. 357 | function bnpDivRemTo(m,q,r) { 358 | var pm = m.abs(); 359 | if(pm.t <= 0) return; 360 | var pt = this.abs(); 361 | if(pt.t < pm.t) { 362 | if(q != null) q.fromInt(0); 363 | if(r != null) this.copyTo(r); 364 | return; 365 | } 366 | if(r == null) r = nbi(); 367 | var y = nbi(), ts = this.s, ms = m.s; 368 | var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus 369 | if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } 370 | else { pm.copyTo(y); pt.copyTo(r); } 371 | var ys = y.t; 372 | var y0 = y[ys-1]; 373 | if(y0 == 0) return; 374 | var yt = y0*(1<1)?y[ys-2]>>this.F2:0); 375 | var d1 = this.FV/yt, d2 = (1<= 0) { 379 | r[r.t++] = 1; 380 | r.subTo(t,r); 381 | } 382 | BigInteger.ONE.dlShiftTo(ys,t); 383 | t.subTo(y,y); // "negative" y so we can replace sub with am later 384 | while(y.t < ys) y[y.t++] = 0; 385 | while(--j >= 0) { 386 | // Estimate quotient digit 387 | var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); 388 | if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out 389 | y.dlShiftTo(j,t); 390 | r.subTo(t,r); 391 | while(r[i] < --qd) r.subTo(t,r); 392 | } 393 | } 394 | if(q != null) { 395 | r.drShiftTo(ys,q); 396 | if(ts != ms) BigInteger.ZERO.subTo(q,q); 397 | } 398 | r.t = ys; 399 | r.clamp(); 400 | if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder 401 | if(ts < 0) BigInteger.ZERO.subTo(r,r); 402 | } 403 | 404 | // (public) this mod a 405 | function bnMod(a) { 406 | var r = nbi(); 407 | this.abs().divRemTo(a,null,r); 408 | if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); 409 | return r; 410 | } 411 | 412 | // Modular reduction using "classic" algorithm 413 | function Classic(m) { this.m = m; } 414 | function cConvert(x) { 415 | if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); 416 | else return x; 417 | } 418 | function cRevert(x) { return x; } 419 | function cReduce(x) { x.divRemTo(this.m,null,x); } 420 | function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 421 | function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 422 | 423 | Classic.prototype.convert = cConvert; 424 | Classic.prototype.revert = cRevert; 425 | Classic.prototype.reduce = cReduce; 426 | Classic.prototype.mulTo = cMulTo; 427 | Classic.prototype.sqrTo = cSqrTo; 428 | 429 | // (protected) return "-1/this % 2^DB"; useful for Mont. reduction 430 | // justification: 431 | // xy == 1 (mod m) 432 | // xy = 1+km 433 | // xy(2-xy) = (1+km)(1-km) 434 | // x[y(2-xy)] = 1-k^2m^2 435 | // x[y(2-xy)] == 1 (mod m^2) 436 | // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 437 | // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. 438 | // JS multiply "overflows" differently from C/C++, so care is needed here. 439 | function bnpInvDigit() { 440 | if(this.t < 1) return 0; 441 | var x = this[0]; 442 | if((x&1) == 0) return 0; 443 | var y = x&3; // y == 1/x mod 2^2 444 | y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 445 | y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 446 | y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 447 | // last step - calculate inverse mod DV directly; 448 | // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints 449 | y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits 450 | // we really want the negative inverse, and -DV < y < DV 451 | return (y>0)?this.DV-y:-y; 452 | } 453 | 454 | // Montgomery reduction 455 | function Montgomery(m) { 456 | this.m = m; 457 | this.mp = m.invDigit(); 458 | this.mpl = this.mp&0x7fff; 459 | this.mph = this.mp>>15; 460 | this.um = (1<<(m.DB-15))-1; 461 | this.mt2 = 2*m.t; 462 | } 463 | 464 | // xR mod m 465 | function montConvert(x) { 466 | var r = nbi(); 467 | x.abs().dlShiftTo(this.m.t,r); 468 | r.divRemTo(this.m,null,r); 469 | if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); 470 | return r; 471 | } 472 | 473 | // x/R mod m 474 | function montRevert(x) { 475 | var r = nbi(); 476 | x.copyTo(r); 477 | this.reduce(r); 478 | return r; 479 | } 480 | 481 | // x = x/R mod m (HAC 14.32) 482 | function montReduce(x) { 483 | while(x.t <= this.mt2) // pad x so am has enough room later 484 | x[x.t++] = 0; 485 | for(var i = 0; i < this.m.t; ++i) { 486 | // faster way of calculating u0 = x[i]*mp mod DV 487 | var j = x[i]&0x7fff; 488 | var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; 489 | // use am to combine the multiply-shift-add into one call 490 | j = i+this.m.t; 491 | x[j] += this.m.am(0,u0,x,i,0,this.m.t); 492 | // propagate carry 493 | while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } 494 | } 495 | x.clamp(); 496 | x.drShiftTo(this.m.t,x); 497 | if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 498 | } 499 | 500 | // r = "x^2/R mod m"; x != r 501 | function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 502 | 503 | // r = "xy/R mod m"; x,y != r 504 | function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 505 | 506 | Montgomery.prototype.convert = montConvert; 507 | Montgomery.prototype.revert = montRevert; 508 | Montgomery.prototype.reduce = montReduce; 509 | Montgomery.prototype.mulTo = montMulTo; 510 | Montgomery.prototype.sqrTo = montSqrTo; 511 | 512 | // (protected) true iff this is even 513 | function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } 514 | 515 | // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) 516 | function bnpExp(e,z) { 517 | if(e > 0xffffffff || e < 1) return BigInteger.ONE; 518 | var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; 519 | g.copyTo(r); 520 | while(--i >= 0) { 521 | z.sqrTo(r,r2); 522 | if((e&(1< 0) z.mulTo(r2,g,r); 523 | else { var t = r; r = r2; r2 = t; } 524 | } 525 | return z.revert(r); 526 | } 527 | 528 | // (public) this^e % m, 0 <= e < 2^32 529 | function bnModPowInt(e,m) { 530 | var z; 531 | if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); 532 | return this.exp(e,z); 533 | } 534 | 535 | // protected 536 | BigInteger.prototype.copyTo = bnpCopyTo; 537 | BigInteger.prototype.fromInt = bnpFromInt; 538 | BigInteger.prototype.fromString = bnpFromString; 539 | BigInteger.prototype.clamp = bnpClamp; 540 | BigInteger.prototype.dlShiftTo = bnpDLShiftTo; 541 | BigInteger.prototype.drShiftTo = bnpDRShiftTo; 542 | BigInteger.prototype.lShiftTo = bnpLShiftTo; 543 | BigInteger.prototype.rShiftTo = bnpRShiftTo; 544 | BigInteger.prototype.subTo = bnpSubTo; 545 | BigInteger.prototype.multiplyTo = bnpMultiplyTo; 546 | BigInteger.prototype.squareTo = bnpSquareTo; 547 | BigInteger.prototype.divRemTo = bnpDivRemTo; 548 | BigInteger.prototype.invDigit = bnpInvDigit; 549 | BigInteger.prototype.isEven = bnpIsEven; 550 | BigInteger.prototype.exp = bnpExp; 551 | 552 | // public 553 | BigInteger.prototype.toString = bnToString; 554 | BigInteger.prototype.negate = bnNegate; 555 | BigInteger.prototype.abs = bnAbs; 556 | BigInteger.prototype.compareTo = bnCompareTo; 557 | BigInteger.prototype.bitLength = bnBitLength; 558 | BigInteger.prototype.mod = bnMod; 559 | BigInteger.prototype.modPowInt = bnModPowInt; 560 | 561 | // "constants" 562 | BigInteger.ZERO = nbv(0); 563 | BigInteger.ONE = nbv(1); 564 | -------------------------------------------------------------------------------- /rsa/jsbn2.gs: -------------------------------------------------------------------------------- 1 | /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ 2 | */ 3 | // Copyright (c) 2005-2009 Tom Wu 4 | // All Rights Reserved. 5 | // See "LICENSE" for details. 6 | 7 | // Extended JavaScript BN functions, required for RSA private ops. 8 | 9 | // Version 1.1: new BigInteger("0", 10) returns "proper" zero 10 | // Version 1.2: square() API, isProbablePrime fix 11 | 12 | // (public) 13 | function bnClone() { var r = nbi(); this.copyTo(r); return r; } 14 | 15 | // (public) return value as integer 16 | function bnIntValue() { 17 | if(this.s < 0) { 18 | if(this.t == 1) return this[0]-this.DV; 19 | else if(this.t == 0) return -1; 20 | } 21 | else if(this.t == 1) return this[0]; 22 | else if(this.t == 0) return 0; 23 | // assumes 16 < DB < 32 24 | return ((this[1]&((1<<(32-this.DB))-1))<>24; } 29 | 30 | // (public) return value as short (assumes DB>=16) 31 | function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } 32 | 33 | // (protected) return x s.t. r^x < DV 34 | function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } 35 | 36 | // (public) 0 if this == 0, 1 if this > 0 37 | function bnSigNum() { 38 | if(this.s < 0) return -1; 39 | else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; 40 | else return 1; 41 | } 42 | 43 | // (protected) convert to radix string 44 | function bnpToRadix(b) { 45 | if(b == null) b = 10; 46 | if(this.signum() == 0 || b < 2 || b > 36) return "0"; 47 | var cs = this.chunkSize(b); 48 | var a = Math.pow(b,cs); 49 | var d = nbv(a), y = nbi(), z = nbi(), r = ""; 50 | this.divRemTo(d,y,z); 51 | while(y.signum() > 0) { 52 | r = (a+z.intValue()).toString(b).substr(1) + r; 53 | y.divRemTo(d,y,z); 54 | } 55 | return z.intValue().toString(b) + r; 56 | } 57 | 58 | // (protected) convert from radix string 59 | function bnpFromRadix(s,b) { 60 | this.fromInt(0); 61 | if(b == null) b = 10; 62 | var cs = this.chunkSize(b); 63 | var d = Math.pow(b,cs), mi = false, j = 0, w = 0; 64 | for(var i = 0; i < s.length; ++i) { 65 | var x = intAt(s,i); 66 | if(x < 0) { 67 | if(s.charAt(i) == "-" && this.signum() == 0) mi = true; 68 | continue; 69 | } 70 | w = b*w+x; 71 | if(++j >= cs) { 72 | this.dMultiply(d); 73 | this.dAddOffset(w,0); 74 | j = 0; 75 | w = 0; 76 | } 77 | } 78 | if(j > 0) { 79 | this.dMultiply(Math.pow(b,j)); 80 | this.dAddOffset(w,0); 81 | } 82 | if(mi) BigInteger.ZERO.subTo(this,this); 83 | } 84 | 85 | // (protected) alternate constructor 86 | function bnpFromNumber(a,b,c) { 87 | if("number" == typeof b) { 88 | // new BigInteger(int,int,RNG) 89 | if(a < 2) this.fromInt(1); 90 | else { 91 | this.fromNumber(a,c); 92 | if(!this.testBit(a-1)) // force MSB set 93 | this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); 94 | if(this.isEven()) this.dAddOffset(1,0); // force odd 95 | while(!this.isProbablePrime(b)) { 96 | this.dAddOffset(2,0); 97 | if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); 98 | } 99 | } 100 | } 101 | else { 102 | // new BigInteger(int,RNG) 103 | var x = new Array(), t = a&7; 104 | x.length = (a>>3)+1; 105 | b.nextBytes(x); 106 | if(t > 0) x[0] &= ((1< 0) { 117 | if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) 118 | r[k++] = d|(this.s<<(this.DB-p)); 119 | while(i >= 0) { 120 | if(p < 8) { 121 | d = (this[i]&((1<>(p+=this.DB-8); 123 | } 124 | else { 125 | d = (this[i]>>(p-=8))&0xff; 126 | if(p <= 0) { p += this.DB; --i; } 127 | } 128 | if((d&0x80) != 0) d |= -256; 129 | if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; 130 | if(k > 0 || d != this.s) r[k++] = d; 131 | } 132 | } 133 | return r; 134 | } 135 | 136 | function bnEquals(a) { return(this.compareTo(a)==0); } 137 | function bnMin(a) { return(this.compareTo(a)<0)?this:a; } 138 | function bnMax(a) { return(this.compareTo(a)>0)?this:a; } 139 | 140 | // (protected) r = this op a (bitwise) 141 | function bnpBitwiseTo(a,op,r) { 142 | var i, f, m = Math.min(a.t,this.t); 143 | for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); 144 | if(a.t < this.t) { 145 | f = a.s&this.DM; 146 | for(i = m; i < this.t; ++i) r[i] = op(this[i],f); 147 | r.t = this.t; 148 | } 149 | else { 150 | f = this.s&this.DM; 151 | for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); 152 | r.t = a.t; 153 | } 154 | r.s = op(this.s,a.s); 155 | r.clamp(); 156 | } 157 | 158 | // (public) this & a 159 | function op_and(x,y) { return x&y; } 160 | function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } 161 | 162 | // (public) this | a 163 | function op_or(x,y) { return x|y; } 164 | function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } 165 | 166 | // (public) this ^ a 167 | function op_xor(x,y) { return x^y; } 168 | function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } 169 | 170 | // (public) this & ~a 171 | function op_andnot(x,y) { return x&~y; } 172 | function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } 173 | 174 | // (public) ~this 175 | function bnNot() { 176 | var r = nbi(); 177 | for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; 178 | r.t = this.t; 179 | r.s = ~this.s; 180 | return r; 181 | } 182 | 183 | // (public) this << n 184 | function bnShiftLeft(n) { 185 | var r = nbi(); 186 | if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); 187 | return r; 188 | } 189 | 190 | // (public) this >> n 191 | function bnShiftRight(n) { 192 | var r = nbi(); 193 | if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); 194 | return r; 195 | } 196 | 197 | // return index of lowest 1-bit in x, x < 2^31 198 | function lbit(x) { 199 | if(x == 0) return -1; 200 | var r = 0; 201 | if((x&0xffff) == 0) { x >>= 16; r += 16; } 202 | if((x&0xff) == 0) { x >>= 8; r += 8; } 203 | if((x&0xf) == 0) { x >>= 4; r += 4; } 204 | if((x&3) == 0) { x >>= 2; r += 2; } 205 | if((x&1) == 0) ++r; 206 | return r; 207 | } 208 | 209 | // (public) returns index of lowest 1-bit (or -1 if none) 210 | function bnGetLowestSetBit() { 211 | for(var i = 0; i < this.t; ++i) 212 | if(this[i] != 0) return i*this.DB+lbit(this[i]); 213 | if(this.s < 0) return this.t*this.DB; 214 | return -1; 215 | } 216 | 217 | // return number of 1 bits in x 218 | function cbit(x) { 219 | var r = 0; 220 | while(x != 0) { x &= x-1; ++r; } 221 | return r; 222 | } 223 | 224 | // (public) return number of set bits 225 | function bnBitCount() { 226 | var r = 0, x = this.s&this.DM; 227 | for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); 228 | return r; 229 | } 230 | 231 | // (public) true iff nth bit is set 232 | function bnTestBit(n) { 233 | var j = Math.floor(n/this.DB); 234 | if(j >= this.t) return(this.s!=0); 235 | return((this[j]&(1<<(n%this.DB)))!=0); 236 | } 237 | 238 | // (protected) this op (1<>= this.DB; 261 | } 262 | if(a.t < this.t) { 263 | c += a.s; 264 | while(i < this.t) { 265 | c += this[i]; 266 | r[i++] = c&this.DM; 267 | c >>= this.DB; 268 | } 269 | c += this.s; 270 | } 271 | else { 272 | c += this.s; 273 | while(i < a.t) { 274 | c += a[i]; 275 | r[i++] = c&this.DM; 276 | c >>= this.DB; 277 | } 278 | c += a.s; 279 | } 280 | r.s = (c<0)?-1:0; 281 | if(c > 0) r[i++] = c; 282 | else if(c < -1) r[i++] = this.DV+c; 283 | r.t = i; 284 | r.clamp(); 285 | } 286 | 287 | // (public) this + a 288 | function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } 289 | 290 | // (public) this - a 291 | function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } 292 | 293 | // (public) this * a 294 | function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } 295 | 296 | // (public) this^2 297 | function bnSquare() { var r = nbi(); this.squareTo(r); return r; } 298 | 299 | // (public) this / a 300 | function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } 301 | 302 | // (public) this % a 303 | function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } 304 | 305 | // (public) [this/a,this%a] 306 | function bnDivideAndRemainder(a) { 307 | var q = nbi(), r = nbi(); 308 | this.divRemTo(a,q,r); 309 | return new Array(q,r); 310 | } 311 | 312 | // (protected) this *= n, this >= 0, 1 < n < DV 313 | function bnpDMultiply(n) { 314 | this[this.t] = this.am(0,n-1,this,0,0,this.t); 315 | ++this.t; 316 | this.clamp(); 317 | } 318 | 319 | // (protected) this += n << w words, this >= 0 320 | function bnpDAddOffset(n,w) { 321 | if(n == 0) return; 322 | while(this.t <= w) this[this.t++] = 0; 323 | this[w] += n; 324 | while(this[w] >= this.DV) { 325 | this[w] -= this.DV; 326 | if(++w >= this.t) this[this.t++] = 0; 327 | ++this[w]; 328 | } 329 | } 330 | 331 | // A "null" reducer 332 | function NullExp() {} 333 | function nNop(x) { return x; } 334 | function nMulTo(x,y,r) { x.multiplyTo(y,r); } 335 | function nSqrTo(x,r) { x.squareTo(r); } 336 | 337 | NullExp.prototype.convert = nNop; 338 | NullExp.prototype.revert = nNop; 339 | NullExp.prototype.mulTo = nMulTo; 340 | NullExp.prototype.sqrTo = nSqrTo; 341 | 342 | // (public) this^e 343 | function bnPow(e) { return this.exp(e,new NullExp()); } 344 | 345 | // (protected) r = lower n words of "this * a", a.t <= n 346 | // "this" should be the larger one if appropriate. 347 | function bnpMultiplyLowerTo(a,n,r) { 348 | var i = Math.min(this.t+a.t,n); 349 | r.s = 0; // assumes a,this >= 0 350 | r.t = i; 351 | while(i > 0) r[--i] = 0; 352 | var j; 353 | for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); 354 | for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); 355 | r.clamp(); 356 | } 357 | 358 | // (protected) r = "this * a" without lower n words, n > 0 359 | // "this" should be the larger one if appropriate. 360 | function bnpMultiplyUpperTo(a,n,r) { 361 | --n; 362 | var i = r.t = this.t+a.t-n; 363 | r.s = 0; // assumes a,this >= 0 364 | while(--i >= 0) r[i] = 0; 365 | for(i = Math.max(n-this.t,0); i < a.t; ++i) 366 | r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); 367 | r.clamp(); 368 | r.drShiftTo(1,r); 369 | } 370 | 371 | // Barrett modular reduction 372 | function Barrett(m) { 373 | // setup Barrett 374 | this.r2 = nbi(); 375 | this.q3 = nbi(); 376 | BigInteger.ONE.dlShiftTo(2*m.t,this.r2); 377 | this.mu = this.r2.divide(m); 378 | this.m = m; 379 | } 380 | 381 | function barrettConvert(x) { 382 | if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); 383 | else if(x.compareTo(this.m) < 0) return x; 384 | else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } 385 | } 386 | 387 | function barrettRevert(x) { return x; } 388 | 389 | // x = x mod m (HAC 14.42) 390 | function barrettReduce(x) { 391 | x.drShiftTo(this.m.t-1,this.r2); 392 | if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } 393 | this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); 394 | this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); 395 | while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); 396 | x.subTo(this.r2,x); 397 | while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 398 | } 399 | 400 | // r = x^2 mod m; x != r 401 | function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 402 | 403 | // r = x*y mod m; x,y != r 404 | function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 405 | 406 | Barrett.prototype.convert = barrettConvert; 407 | Barrett.prototype.revert = barrettRevert; 408 | Barrett.prototype.reduce = barrettReduce; 409 | Barrett.prototype.mulTo = barrettMulTo; 410 | Barrett.prototype.sqrTo = barrettSqrTo; 411 | 412 | // (public) this^e % m (HAC 14.85) 413 | function bnModPow(e,m) { 414 | var i = e.bitLength(), k, r = nbv(1), z; 415 | if(i <= 0) return r; 416 | else if(i < 18) k = 1; 417 | else if(i < 48) k = 3; 418 | else if(i < 144) k = 4; 419 | else if(i < 768) k = 5; 420 | else k = 6; 421 | if(i < 8) 422 | z = new Classic(m); 423 | else if(m.isEven()) 424 | z = new Barrett(m); 425 | else 426 | z = new Montgomery(m); 427 | 428 | // precomputation 429 | var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { 432 | var g2 = nbi(); 433 | z.sqrTo(g[1],g2); 434 | while(n <= km) { 435 | g[n] = nbi(); 436 | z.mulTo(g2,g[n-2],g[n]); 437 | n += 2; 438 | } 439 | } 440 | 441 | var j = e.t-1, w, is1 = true, r2 = nbi(), t; 442 | i = nbits(e[j])-1; 443 | while(j >= 0) { 444 | if(i >= k1) w = (e[j]>>(i-k1))&km; 445 | else { 446 | w = (e[j]&((1<<(i+1))-1))<<(k1-i); 447 | if(j > 0) w |= e[j-1]>>(this.DB+i-k1); 448 | } 449 | 450 | n = k; 451 | while((w&1) == 0) { w >>= 1; --n; } 452 | if((i -= n) < 0) { i += this.DB; --j; } 453 | if(is1) { // ret == 1, don't bother squaring or multiplying it 454 | g[w].copyTo(r); 455 | is1 = false; 456 | } 457 | else { 458 | while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } 459 | if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } 460 | z.mulTo(r2,g[w],r); 461 | } 462 | 463 | while(j >= 0 && (e[j]&(1< 0) { 480 | x.rShiftTo(g,x); 481 | y.rShiftTo(g,y); 482 | } 483 | while(x.signum() > 0) { 484 | if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); 485 | if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); 486 | if(x.compareTo(y) >= 0) { 487 | x.subTo(y,x); 488 | x.rShiftTo(1,x); 489 | } 490 | else { 491 | y.subTo(x,y); 492 | y.rShiftTo(1,y); 493 | } 494 | } 495 | if(g > 0) y.lShiftTo(g,y); 496 | return y; 497 | } 498 | 499 | // (protected) this % n, n < 2^26 500 | function bnpModInt(n) { 501 | if(n <= 0) return 0; 502 | var d = this.DV%n, r = (this.s<0)?n-1:0; 503 | if(this.t > 0) 504 | if(d == 0) r = this[0]%n; 505 | else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; 506 | return r; 507 | } 508 | 509 | // (public) 1/this % m (HAC 14.61) 510 | function bnModInverse(m) { 511 | var ac = m.isEven(); 512 | if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; 513 | var u = m.clone(), v = this.clone(); 514 | var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); 515 | while(u.signum() != 0) { 516 | while(u.isEven()) { 517 | u.rShiftTo(1,u); 518 | if(ac) { 519 | if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } 520 | a.rShiftTo(1,a); 521 | } 522 | else if(!b.isEven()) b.subTo(m,b); 523 | b.rShiftTo(1,b); 524 | } 525 | while(v.isEven()) { 526 | v.rShiftTo(1,v); 527 | if(ac) { 528 | if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } 529 | c.rShiftTo(1,c); 530 | } 531 | else if(!d.isEven()) d.subTo(m,d); 532 | d.rShiftTo(1,d); 533 | } 534 | if(u.compareTo(v) >= 0) { 535 | u.subTo(v,u); 536 | if(ac) a.subTo(c,a); 537 | b.subTo(d,b); 538 | } 539 | else { 540 | v.subTo(u,v); 541 | if(ac) c.subTo(a,c); 542 | d.subTo(b,d); 543 | } 544 | } 545 | if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; 546 | if(d.compareTo(m) >= 0) return d.subtract(m); 547 | if(d.signum() < 0) d.addTo(m,d); else return d; 548 | if(d.signum() < 0) return d.add(m); else return d; 549 | } 550 | 551 | var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; 552 | var lplim = (1<<26)/lowprimes[lowprimes.length-1]; 553 | 554 | // (public) test primality with certainty >= 1-.5^t 555 | function bnIsProbablePrime(t) { 556 | var i, x = this.abs(); 557 | if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { 558 | for(i = 0; i < lowprimes.length; ++i) 559 | if(x[0] == lowprimes[i]) return true; 560 | return false; 561 | } 562 | if(x.isEven()) return false; 563 | i = 1; 564 | while(i < lowprimes.length) { 565 | var m = lowprimes[i], j = i+1; 566 | while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; 567 | m = x.modInt(m); 568 | while(i < j) if(m%lowprimes[i++] == 0) return false; 569 | } 570 | return x.millerRabin(t); 571 | } 572 | 573 | // (protected) true if probably prime (HAC 4.24, Miller-Rabin) 574 | function bnpMillerRabin(t) { 575 | var n1 = this.subtract(BigInteger.ONE); 576 | var k = n1.getLowestSetBit(); 577 | if(k <= 0) return false; 578 | var r = n1.shiftRight(k); 579 | t = (t+1)>>1; 580 | if(t > lowprimes.length) t = lowprimes.length; 581 | var a = nbi(); 582 | for(var i = 0; i < t; ++i) { 583 | //Pick bases at random, instead of starting at 2 584 | a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); 585 | var y = a.modPow(r,this); 586 | if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { 587 | var j = 1; 588 | while(j++ < k && y.compareTo(n1) != 0) { 589 | y = y.modPowInt(2,this); 590 | if(y.compareTo(BigInteger.ONE) == 0) return false; 591 | } 592 | if(y.compareTo(n1) != 0) return false; 593 | } 594 | } 595 | return true; 596 | } 597 | 598 | // protected 599 | BigInteger.prototype.chunkSize = bnpChunkSize; 600 | BigInteger.prototype.toRadix = bnpToRadix; 601 | BigInteger.prototype.fromRadix = bnpFromRadix; 602 | BigInteger.prototype.fromNumber = bnpFromNumber; 603 | BigInteger.prototype.bitwiseTo = bnpBitwiseTo; 604 | BigInteger.prototype.changeBit = bnpChangeBit; 605 | BigInteger.prototype.addTo = bnpAddTo; 606 | BigInteger.prototype.dMultiply = bnpDMultiply; 607 | BigInteger.prototype.dAddOffset = bnpDAddOffset; 608 | BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; 609 | BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; 610 | BigInteger.prototype.modInt = bnpModInt; 611 | BigInteger.prototype.millerRabin = bnpMillerRabin; 612 | 613 | // public 614 | BigInteger.prototype.clone = bnClone; 615 | BigInteger.prototype.intValue = bnIntValue; 616 | BigInteger.prototype.byteValue = bnByteValue; 617 | BigInteger.prototype.shortValue = bnShortValue; 618 | BigInteger.prototype.signum = bnSigNum; 619 | BigInteger.prototype.toByteArray = bnToByteArray; 620 | BigInteger.prototype.equals = bnEquals; 621 | BigInteger.prototype.min = bnMin; 622 | BigInteger.prototype.max = bnMax; 623 | BigInteger.prototype.and = bnAnd; 624 | BigInteger.prototype.or = bnOr; 625 | BigInteger.prototype.xor = bnXor; 626 | BigInteger.prototype.andNot = bnAndNot; 627 | BigInteger.prototype.not = bnNot; 628 | BigInteger.prototype.shiftLeft = bnShiftLeft; 629 | BigInteger.prototype.shiftRight = bnShiftRight; 630 | BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; 631 | BigInteger.prototype.bitCount = bnBitCount; 632 | BigInteger.prototype.testBit = bnTestBit; 633 | BigInteger.prototype.setBit = bnSetBit; 634 | BigInteger.prototype.clearBit = bnClearBit; 635 | BigInteger.prototype.flipBit = bnFlipBit; 636 | BigInteger.prototype.add = bnAdd; 637 | BigInteger.prototype.subtract = bnSubtract; 638 | BigInteger.prototype.multiply = bnMultiply; 639 | BigInteger.prototype.divide = bnDivide; 640 | BigInteger.prototype.remainder = bnRemainder; 641 | BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; 642 | BigInteger.prototype.modPow = bnModPow; 643 | BigInteger.prototype.modInverse = bnModInverse; 644 | BigInteger.prototype.pow = bnPow; 645 | BigInteger.prototype.gcd = bnGCD; 646 | BigInteger.prototype.isProbablePrime = bnIsProbablePrime; 647 | 648 | // JSBN-specific extension 649 | BigInteger.prototype.square = bnSquare; 650 | 651 | // BigInteger interfaces not implemented in jsbn: 652 | 653 | // BigInteger(int signum, byte[] magnitude) 654 | // double doubleValue() 655 | // float floatValue() 656 | // int hashCode() 657 | // long longValue() 658 | // static BigInteger valueOf(long val) 659 | -------------------------------------------------------------------------------- /rsa/crypto-comp-core.gs: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | /** 8 | * CryptoJS core components. 9 | */ 10 | var CryptoJS = CryptoJS || (function (Math, undefined) { 11 | /** 12 | * CryptoJS namespace. 13 | */ 14 | var C = {}; 15 | 16 | /** 17 | * Library namespace. 18 | */ 19 | var C_lib = C.lib = {}; 20 | 21 | /** 22 | * Base object for prototypal inheritance. 23 | */ 24 | var Base = C_lib.Base = (function () { 25 | function F() {} 26 | 27 | return { 28 | /** 29 | * Creates a new object that inherits from this object. 30 | * 31 | * @param {Object} overrides Properties to copy into the new object. 32 | * 33 | * @return {Object} The new object. 34 | * 35 | * @static 36 | * 37 | * @example 38 | * 39 | * var MyType = CryptoJS.lib.Base.extend({ 40 | * field: 'value', 41 | * 42 | * method: function () { 43 | * } 44 | * }); 45 | */ 46 | extend: function (overrides) { 47 | // Spawn 48 | F.prototype = this; 49 | var subtype = new F(); 50 | 51 | // Augment 52 | if (overrides) { 53 | subtype.mixIn(overrides); 54 | } 55 | 56 | // Create default initializer 57 | if (!subtype.hasOwnProperty('init')) { 58 | subtype.init = function () { 59 | subtype.$super.init.apply(this, arguments); 60 | }; 61 | } 62 | 63 | // Initializer's prototype is the subtype object 64 | subtype.init.prototype = subtype; 65 | 66 | // Reference supertype 67 | subtype.$super = this; 68 | 69 | return subtype; 70 | }, 71 | 72 | /** 73 | * Extends this object and runs the init method. 74 | * Arguments to create() will be passed to init(). 75 | * 76 | * @return {Object} The new object. 77 | * 78 | * @static 79 | * 80 | * @example 81 | * 82 | * var instance = MyType.create(); 83 | */ 84 | create: function () { 85 | var instance = this.extend(); 86 | instance.init.apply(instance, arguments); 87 | 88 | return instance; 89 | }, 90 | 91 | /** 92 | * Initializes a newly created object. 93 | * Override this method to add some logic when your objects are created. 94 | * 95 | * @example 96 | * 97 | * var MyType = CryptoJS.lib.Base.extend({ 98 | * init: function () { 99 | * // ... 100 | * } 101 | * }); 102 | */ 103 | init: function () { 104 | }, 105 | 106 | /** 107 | * Copies properties into this object. 108 | * 109 | * @param {Object} properties The properties to mix in. 110 | * 111 | * @example 112 | * 113 | * MyType.mixIn({ 114 | * field: 'value' 115 | * }); 116 | */ 117 | mixIn: function (properties) { 118 | for (var propertyName in properties) { 119 | if (properties.hasOwnProperty(propertyName)) { 120 | this[propertyName] = properties[propertyName]; 121 | } 122 | } 123 | 124 | // IE won't copy toString using the loop above 125 | if (properties.hasOwnProperty('toString')) { 126 | this.toString = properties.toString; 127 | } 128 | }, 129 | 130 | /** 131 | * Creates a copy of this object. 132 | * 133 | * @return {Object} The clone. 134 | * 135 | * @example 136 | * 137 | * var clone = instance.clone(); 138 | */ 139 | clone: function () { 140 | return this.init.prototype.extend(this); 141 | } 142 | }; 143 | }()); 144 | 145 | /** 146 | * An array of 32-bit words. 147 | * 148 | * @property {Array} words The array of 32-bit words. 149 | * @property {number} sigBytes The number of significant bytes in this word array. 150 | */ 151 | var WordArray = C_lib.WordArray = Base.extend({ 152 | /** 153 | * Initializes a newly created word array. 154 | * 155 | * @param {Array} words (Optional) An array of 32-bit words. 156 | * @param {number} sigBytes (Optional) The number of significant bytes in the words. 157 | * 158 | * @example 159 | * 160 | * var wordArray = CryptoJS.lib.WordArray.create(); 161 | * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); 162 | * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); 163 | */ 164 | init: function (words, sigBytes) { 165 | words = this.words = words || []; 166 | 167 | if (sigBytes != undefined) { 168 | this.sigBytes = sigBytes; 169 | } else { 170 | this.sigBytes = words.length * 4; 171 | } 172 | }, 173 | 174 | /** 175 | * Converts this word array to a string. 176 | * 177 | * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex 178 | * 179 | * @return {string} The stringified word array. 180 | * 181 | * @example 182 | * 183 | * var string = wordArray + ''; 184 | * var string = wordArray.toString(); 185 | * var string = wordArray.toString(CryptoJS.enc.Utf8); 186 | */ 187 | toString: function (encoder) { 188 | return (encoder || Hex).stringify(this); 189 | }, 190 | 191 | /** 192 | * Concatenates a word array to this word array. 193 | * 194 | * @param {WordArray} wordArray The word array to append. 195 | * 196 | * @return {WordArray} This word array. 197 | * 198 | * @example 199 | * 200 | * wordArray1.concat(wordArray2); 201 | */ 202 | concat: function (wordArray) { 203 | // Shortcuts 204 | var thisWords = this.words; 205 | var thatWords = wordArray.words; 206 | var thisSigBytes = this.sigBytes; 207 | var thatSigBytes = wordArray.sigBytes; 208 | 209 | // Clamp excess bits 210 | this.clamp(); 211 | 212 | // Concat 213 | if (thisSigBytes % 4) { 214 | // Copy one byte at a time 215 | for (var i = 0; i < thatSigBytes; i++) { 216 | var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 217 | thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); 218 | } 219 | } else if (thatWords.length > 0xffff) { 220 | // Copy one word at a time 221 | for (var i = 0; i < thatSigBytes; i += 4) { 222 | thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; 223 | } 224 | } else { 225 | // Copy all words at once 226 | thisWords.push.apply(thisWords, thatWords); 227 | } 228 | this.sigBytes += thatSigBytes; 229 | 230 | // Chainable 231 | return this; 232 | }, 233 | 234 | /** 235 | * Removes insignificant bits. 236 | * 237 | * @example 238 | * 239 | * wordArray.clamp(); 240 | */ 241 | clamp: function () { 242 | // Shortcuts 243 | var words = this.words; 244 | var sigBytes = this.sigBytes; 245 | 246 | // Clamp 247 | words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); 248 | words.length = Math.ceil(sigBytes / 4); 249 | }, 250 | 251 | /** 252 | * Creates a copy of this word array. 253 | * 254 | * @return {WordArray} The clone. 255 | * 256 | * @example 257 | * 258 | * var clone = wordArray.clone(); 259 | */ 260 | clone: function () { 261 | var clone = Base.clone.call(this); 262 | clone.words = this.words.slice(0); 263 | 264 | return clone; 265 | }, 266 | 267 | /** 268 | * Creates a word array filled with random bytes. 269 | * 270 | * @param {number} nBytes The number of random bytes to generate. 271 | * 272 | * @return {WordArray} The random word array. 273 | * 274 | * @static 275 | * 276 | * @example 277 | * 278 | * var wordArray = CryptoJS.lib.WordArray.random(16); 279 | */ 280 | random: function (nBytes) { 281 | var words = []; 282 | for (var i = 0; i < nBytes; i += 4) { 283 | words.push((Math.random() * 0x100000000) | 0); 284 | } 285 | 286 | return new WordArray.init(words, nBytes); 287 | } 288 | }); 289 | 290 | /** 291 | * Encoder namespace. 292 | */ 293 | var C_enc = C.enc = {}; 294 | 295 | /** 296 | * Hex encoding strategy. 297 | */ 298 | var Hex = C_enc.Hex = { 299 | /** 300 | * Converts a word array to a hex string. 301 | * 302 | * @param {WordArray} wordArray The word array. 303 | * 304 | * @return {string} The hex string. 305 | * 306 | * @static 307 | * 308 | * @example 309 | * 310 | * var hexString = CryptoJS.enc.Hex.stringify(wordArray); 311 | */ 312 | stringify: function (wordArray) { 313 | // Shortcuts 314 | var words = wordArray.words; 315 | var sigBytes = wordArray.sigBytes; 316 | 317 | // Convert 318 | var hexChars = []; 319 | for (var i = 0; i < sigBytes; i++) { 320 | var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 321 | hexChars.push((bite >>> 4).toString(16)); 322 | hexChars.push((bite & 0x0f).toString(16)); 323 | } 324 | 325 | return hexChars.join(''); 326 | }, 327 | 328 | /** 329 | * Converts a hex string to a word array. 330 | * 331 | * @param {string} hexStr The hex string. 332 | * 333 | * @return {WordArray} The word array. 334 | * 335 | * @static 336 | * 337 | * @example 338 | * 339 | * var wordArray = CryptoJS.enc.Hex.parse(hexString); 340 | */ 341 | parse: function (hexStr) { 342 | // Shortcut 343 | var hexStrLength = hexStr.length; 344 | 345 | // Convert 346 | var words = []; 347 | for (var i = 0; i < hexStrLength; i += 2) { 348 | words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); 349 | } 350 | 351 | return new WordArray.init(words, hexStrLength / 2); 352 | } 353 | }; 354 | 355 | /** 356 | * Latin1 encoding strategy. 357 | */ 358 | var Latin1 = C_enc.Latin1 = { 359 | /** 360 | * Converts a word array to a Latin1 string. 361 | * 362 | * @param {WordArray} wordArray The word array. 363 | * 364 | * @return {string} The Latin1 string. 365 | * 366 | * @static 367 | * 368 | * @example 369 | * 370 | * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); 371 | */ 372 | stringify: function (wordArray) { 373 | // Shortcuts 374 | var words = wordArray.words; 375 | var sigBytes = wordArray.sigBytes; 376 | 377 | // Convert 378 | var latin1Chars = []; 379 | for (var i = 0; i < sigBytes; i++) { 380 | var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 381 | latin1Chars.push(String.fromCharCode(bite)); 382 | } 383 | 384 | return latin1Chars.join(''); 385 | }, 386 | 387 | /** 388 | * Converts a Latin1 string to a word array. 389 | * 390 | * @param {string} latin1Str The Latin1 string. 391 | * 392 | * @return {WordArray} The word array. 393 | * 394 | * @static 395 | * 396 | * @example 397 | * 398 | * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); 399 | */ 400 | parse: function (latin1Str) { 401 | // Shortcut 402 | var latin1StrLength = latin1Str.length; 403 | 404 | // Convert 405 | var words = []; 406 | for (var i = 0; i < latin1StrLength; i++) { 407 | words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); 408 | } 409 | 410 | return new WordArray.init(words, latin1StrLength); 411 | } 412 | }; 413 | 414 | /** 415 | * UTF-8 encoding strategy. 416 | */ 417 | var Utf8 = C_enc.Utf8 = { 418 | /** 419 | * Converts a word array to a UTF-8 string. 420 | * 421 | * @param {WordArray} wordArray The word array. 422 | * 423 | * @return {string} The UTF-8 string. 424 | * 425 | * @static 426 | * 427 | * @example 428 | * 429 | * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); 430 | */ 431 | stringify: function (wordArray) { 432 | try { 433 | return decodeURIComponent(escape(Latin1.stringify(wordArray))); 434 | } catch (e) { 435 | throw new Error('Malformed UTF-8 data'); 436 | } 437 | }, 438 | 439 | /** 440 | * Converts a UTF-8 string to a word array. 441 | * 442 | * @param {string} utf8Str The UTF-8 string. 443 | * 444 | * @return {WordArray} The word array. 445 | * 446 | * @static 447 | * 448 | * @example 449 | * 450 | * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); 451 | */ 452 | parse: function (utf8Str) { 453 | return Latin1.parse(unescape(encodeURIComponent(utf8Str))); 454 | } 455 | }; 456 | 457 | /** 458 | * Abstract buffered block algorithm template. 459 | * 460 | * The property blockSize must be implemented in a concrete subtype. 461 | * 462 | * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 463 | */ 464 | var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ 465 | /** 466 | * Resets this block algorithm's data buffer to its initial state. 467 | * 468 | * @example 469 | * 470 | * bufferedBlockAlgorithm.reset(); 471 | */ 472 | reset: function () { 473 | // Initial values 474 | this._data = new WordArray.init(); 475 | this._nDataBytes = 0; 476 | }, 477 | 478 | /** 479 | * Adds new data to this block algorithm's buffer. 480 | * 481 | * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. 482 | * 483 | * @example 484 | * 485 | * bufferedBlockAlgorithm._append('data'); 486 | * bufferedBlockAlgorithm._append(wordArray); 487 | */ 488 | _append: function (data) { 489 | // Convert string to WordArray, else assume WordArray already 490 | if (typeof data == 'string') { 491 | data = Utf8.parse(data); 492 | } 493 | 494 | // Append 495 | this._data.concat(data); 496 | this._nDataBytes += data.sigBytes; 497 | }, 498 | 499 | /** 500 | * Processes available data blocks. 501 | * 502 | * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. 503 | * 504 | * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. 505 | * 506 | * @return {WordArray} The processed data. 507 | * 508 | * @example 509 | * 510 | * var processedData = bufferedBlockAlgorithm._process(); 511 | * var processedData = bufferedBlockAlgorithm._process(!!'flush'); 512 | */ 513 | _process: function (doFlush) { 514 | // Shortcuts 515 | var data = this._data; 516 | var dataWords = data.words; 517 | var dataSigBytes = data.sigBytes; 518 | var blockSize = this.blockSize; 519 | var blockSizeBytes = blockSize * 4; 520 | 521 | // Count blocks ready 522 | var nBlocksReady = dataSigBytes / blockSizeBytes; 523 | if (doFlush) { 524 | // Round up to include partial blocks 525 | nBlocksReady = Math.ceil(nBlocksReady); 526 | } else { 527 | // Round down to include only full blocks, 528 | // less the number of blocks that must remain in the buffer 529 | nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); 530 | } 531 | 532 | // Count words ready 533 | var nWordsReady = nBlocksReady * blockSize; 534 | 535 | // Count bytes ready 536 | var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); 537 | 538 | // Process blocks 539 | if (nWordsReady) { 540 | for (var offset = 0; offset < nWordsReady; offset += blockSize) { 541 | // Perform concrete-algorithm logic 542 | this._doProcessBlock(dataWords, offset); 543 | } 544 | 545 | // Remove processed words 546 | var processedWords = dataWords.splice(0, nWordsReady); 547 | data.sigBytes -= nBytesReady; 548 | } 549 | 550 | // Return processed words 551 | return new WordArray.init(processedWords, nBytesReady); 552 | }, 553 | 554 | /** 555 | * Creates a copy of this object. 556 | * 557 | * @return {Object} The clone. 558 | * 559 | * @example 560 | * 561 | * var clone = bufferedBlockAlgorithm.clone(); 562 | */ 563 | clone: function () { 564 | var clone = Base.clone.call(this); 565 | clone._data = this._data.clone(); 566 | 567 | return clone; 568 | }, 569 | 570 | _minBufferSize: 0 571 | }); 572 | 573 | /** 574 | * Abstract hasher template. 575 | * 576 | * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) 577 | */ 578 | var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ 579 | /** 580 | * Configuration options. 581 | */ 582 | cfg: Base.extend(), 583 | 584 | /** 585 | * Initializes a newly created hasher. 586 | * 587 | * @param {Object} cfg (Optional) The configuration options to use for this hash computation. 588 | * 589 | * @example 590 | * 591 | * var hasher = CryptoJS.algo.SHA256.create(); 592 | */ 593 | init: function (cfg) { 594 | // Apply config defaults 595 | this.cfg = this.cfg.extend(cfg); 596 | 597 | // Set initial values 598 | this.reset(); 599 | }, 600 | 601 | /** 602 | * Resets this hasher to its initial state. 603 | * 604 | * @example 605 | * 606 | * hasher.reset(); 607 | */ 608 | reset: function () { 609 | // Reset data buffer 610 | BufferedBlockAlgorithm.reset.call(this); 611 | 612 | // Perform concrete-hasher logic 613 | this._doReset(); 614 | }, 615 | 616 | /** 617 | * Updates this hasher with a message. 618 | * 619 | * @param {WordArray|string} messageUpdate The message to append. 620 | * 621 | * @return {Hasher} This hasher. 622 | * 623 | * @example 624 | * 625 | * hasher.update('message'); 626 | * hasher.update(wordArray); 627 | */ 628 | update: function (messageUpdate) { 629 | // Append 630 | this._append(messageUpdate); 631 | 632 | // Update the hash 633 | this._process(); 634 | 635 | // Chainable 636 | return this; 637 | }, 638 | 639 | /** 640 | * Finalizes the hash computation. 641 | * Note that the finalize operation is effectively a destructive, read-once operation. 642 | * 643 | * @param {WordArray|string} messageUpdate (Optional) A final message update. 644 | * 645 | * @return {WordArray} The hash. 646 | * 647 | * @example 648 | * 649 | * var hash = hasher.finalize(); 650 | * var hash = hasher.finalize('message'); 651 | * var hash = hasher.finalize(wordArray); 652 | */ 653 | finalize: function (messageUpdate) { 654 | // Final message update 655 | if (messageUpdate) { 656 | this._append(messageUpdate); 657 | } 658 | 659 | // Perform concrete-hasher logic 660 | var hash = this._doFinalize(); 661 | 662 | return hash; 663 | }, 664 | 665 | blockSize: 512/32, 666 | 667 | /** 668 | * Creates a shortcut function to a hasher's object interface. 669 | * 670 | * @param {Hasher} hasher The hasher to create a helper for. 671 | * 672 | * @return {Function} The shortcut function. 673 | * 674 | * @static 675 | * 676 | * @example 677 | * 678 | * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); 679 | */ 680 | _createHelper: function (hasher) { 681 | return function (message, cfg) { 682 | return new hasher.init(cfg).finalize(message); 683 | }; 684 | }, 685 | 686 | /** 687 | * Creates a shortcut function to the HMAC's object interface. 688 | * 689 | * @param {Hasher} hasher The hasher to use in this HMAC helper. 690 | * 691 | * @return {Function} The shortcut function. 692 | * 693 | * @static 694 | * 695 | * @example 696 | * 697 | * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); 698 | */ 699 | _createHmacHelper: function (hasher) { 700 | return function (message, key) { 701 | return new C_algo.HMAC.init(hasher, key).finalize(message); 702 | }; 703 | } 704 | }); 705 | 706 | /** 707 | * Algorithm namespace. 708 | */ 709 | var C_algo = C.algo = {}; 710 | 711 | return C; 712 | }(Math)); 713 | -------------------------------------------------------------------------------- /Code.gs: -------------------------------------------------------------------------------- 1 | var subPanelCSS = { 2 | backgroundColor: '#f7f7f7', 3 | border: '1px solid grey', 4 | padding: '10px' 5 | }; 6 | 7 | function onOpen() { 8 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 9 | var menuEntries = [{name: "Xero Settings", functionName: "xeroSetup"}, //This will be for adding user API settings. 10 | //{name: "Account Type", functionName: "accountType"}, // Select Payable or Receivables 11 | {name: "Manual Upload Invoices", functionName: "uploadInvoices"}, // Push through manaual upload of prices. 12 | {name: "Manual Download Invoices", functionName: "handleInvoicesDownload"}, // Push through manual download of orders. 13 | {name: "Manual Upload Payments", functionName: "uploadPayments"}, // Push through manual upload of order statuses. 14 | {name: "Manual Download Payments", functionName: "handlePaymentsDownload"}, 15 | //{name: "Automation Setup", functionName: "automation"}, // Automation setup for automatic upload and download of Invoices, download of Invoices, upload of Payments, and Download of Payments. If each function could be listed with own trigger setup. 16 | {name: "Clear Data", functionName: "clearData"}, 17 | //{name: "Test", functionName: "downloadInvoiceOptions"} 18 | ]; 19 | ss.addMenu("XERO", menuEntries); 20 | } 21 | 22 | function doGet(e) { 23 | // https://script.google.com/macros/s/AKfycbyoJ09MtcAJFB-v31CH3sA2L5atNr4jtGW3cYWlqqpvGg_kBEY/exec?oauth_token=KNDX9LRNOC0KNAEB4PSVPDINTGSC6F&oauth_verifier=1144716&org=zX0A5sD18KgGYSSotqTKvG 24 | // Step 3. Exchange verified Request token for Access Token 25 | // var p = PropertiesService.getScriptProperties().getProperties(); 26 | Xero.getSettings(); 27 | var payload = 28 | { 29 | "oauth_consumer_key": Xero.getProperty('consumerKey'), 30 | "oauth_token": e.parameter.oauth_token, 31 | "oauth_signature_method": "PLAINTEXT", 32 | "oauth_signature": encodeURIComponent(Xero.getProperty('consumerSecret') + '&' + Xero.getProperty('requestTokenSecret')), 33 | "oauth_timestamp": ((new Date().getTime())/1000).toFixed(0), 34 | "oauth_nonce": generateRandomString(Math.floor(Math.round(25))), 35 | "oauth_version": "1.0", 36 | "oauth_verifier": e.parameter.oauth_verifier 37 | }; 38 | var options = {"method": "post", "payload": payload, muteHttpExceptions: true}; 39 | try { 40 | var response = UrlFetchApp.fetch(accessTokenURL, options); 41 | }catch(e) { 42 | Logger.log(e); 43 | return HtmlService.createHtmlOutput("
"+ response.getContentText() +"
"); 44 | } 45 | 46 | var reoAuthToken = /(oauth_token=)([a-zA-Z0-9]+)/; 47 | var tokenMatch = reoAuthToken.exec(response.getContentText()); 48 | var reTokenSecret = /(oauth_token_secret=)([a-zA-Z0-9]+)/; 49 | var secretMatch = reTokenSecret.exec(response.getContentText()) ; 50 | 51 | if (tokenMatch && tokenMatch[2] != '') { 52 | PropertiesService.getScriptProperties().setProperty('accessToken', tokenMatch[2]); 53 | PropertiesService.getScriptProperties().setProperty('accessTokenSecret', secretMatch[2]); 54 | PropertiesService.getScriptProperties().setProperty('isConnected', true); 55 | ScriptApp.newTrigger('closeConnection').timeBased().after(30000); 56 | return HtmlService.createHtmlOutput("Your spreadsheet is now connected to Xero.com for 30 mins."); 57 | } 58 | } 59 | 60 | function closeConnection() { 61 | PropertiesService.getScriptProperties().deleteProperty('isConnected'); 62 | PropertiesService.getScriptProperties().setProperty('isConnected', false); 63 | } 64 | 65 | function closeApp() { 66 | var app = UiApp.getActiveApplication(); 67 | app.close(); 68 | return app; 69 | } 70 | 71 | function xeroSetup() { 72 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 73 | var app = UiApp.createApplication(); 74 | app.setHeight(400); 75 | var scriptProps = PropertiesService.getScriptProperties(); 76 | 77 | var label1 = app.createLabel('XERO Settings').setStyleAttribute('font-weight', 'bold').setStyleAttribute('padding', '5px').setId('label1'); 78 | var panel1 = app.createVerticalPanel().setId('panel1'); 79 | var grid = app.createGrid(7, 2); 80 | var absPanel = app.createAbsolutePanel(); 81 | 82 | var handler = app.createServerHandler('saveSettings'); 83 | var clientHandler1 = app.createClientHandler(); 84 | var clientHandler2 = app.createClientHandler(); 85 | var clientHandler3 = app.createClientHandler(); 86 | 87 | var btnSave = app.createButton('Save Settings', handler); 88 | var lblAppType = app.createLabel('Application Type: '); 89 | var appTypes = {Private:0, Public:1, Partner:2}; 90 | var listAppType = app.createListBox().setName('appType').addItem('Private').addItem('Public').addItem('Partner').addChangeHandler(clientHandler1). 91 | addChangeHandler(clientHandler2).addChangeHandler(clientHandler3).setSelectedIndex(appTypes[(scriptProps.getProperty('appType') != null ? scriptProps.getProperty('appType'): 'Private')]); 92 | handler.addCallbackElement(listAppType); 93 | 94 | var lblAppName = app.createLabel('Application Name: '); 95 | var txtAppName = app.createTextBox().setName('userAgent').setWidth("350") 96 | .setValue((scriptProps.getProperty('userAgent') != null ? scriptProps.getProperty('userAgent'): "")); 97 | handler.addCallbackElement(txtAppName); 98 | 99 | var lblConsumerKey = app.createLabel('Consumer Key: '); 100 | var txtConsumerKey = app.createTextBox().setName('consumerKey').setWidth("350") 101 | .setValue((scriptProps.getProperty('consumerKey') != null ? scriptProps.getProperty('consumerKey'): "")); 102 | handler.addCallbackElement(txtConsumerKey); 103 | 104 | var lblConsumerSecret = app.createLabel('Consumer Secret: '); 105 | var txtConsumerSecret = app.createTextBox().setName('consumerSecret').setWidth("350") 106 | .setValue((scriptProps.getProperty('consumerSecret') != null ? scriptProps.getProperty('consumerSecret'): "")); 107 | handler.addCallbackElement(txtConsumerSecret); 108 | 109 | var lblcallBack = app.createLabel('Callback URL:'); 110 | var txtcallBack = app.createTextBox().setName('callBack').setWidth("350") 111 | .setValue((scriptProps.getProperty('callbackURL') != null ? scriptProps.getProperty('callbackURL'): "")); 112 | handler.addCallbackElement(txtcallBack); 113 | 114 | var lblRSA = app.createLabel('RSA Private Key:'); 115 | var txtareaRSA = app.createTextArea().setName('RSA').setWidth("350").setHeight("150") 116 | .setValue((scriptProps.getProperty('rsaKey') != null ? scriptProps.getProperty('rsaKey'): "")); 117 | 118 | if (scriptProps.getProperty('appType') == "Private" || scriptProps.getProperty('appType') == null) 119 | txtcallBack.setEnabled(false); 120 | else if (scriptProps.getProperty('appType') == "Public") 121 | txtareaRSA.setEnabled(false); 122 | 123 | handler.addCallbackElement(txtareaRSA); 124 | clientHandler1.validateMatches(listAppType, 'Private').forTargets(txtcallBack).setEnabled(false).forTargets(txtareaRSA).setEnabled(true); 125 | clientHandler2.validateMatches(listAppType, 'Public').forTargets(txtcallBack).setEnabled(true).forTargets(txtareaRSA).setEnabled(false); 126 | clientHandler3.validateMatches(listAppType, 'Partner').forTargets(txtcallBack).setEnabled(true).forTargets(txtareaRSA).setEnabled(true); 127 | 128 | grid.setBorderWidth(0); 129 | grid.setWidget(0, 0, lblAppType); 130 | grid.setWidget(0, 1, listAppType); 131 | grid.setWidget(1, 0, lblAppName); 132 | grid.setWidget(1, 1, txtAppName); 133 | grid.setWidget(2, 0, lblConsumerKey); 134 | grid.setWidget(2, 1, txtConsumerKey); 135 | grid.setWidget(3, 0, lblConsumerSecret); 136 | grid.setWidget(3, 1, txtConsumerSecret); 137 | grid.setWidget(4, 0, lblcallBack); 138 | grid.setWidget(4, 1, txtcallBack); 139 | grid.setWidget(5, 0, lblRSA); 140 | grid.setWidget(5, 1, txtareaRSA); 141 | grid.setWidget(6, 1, btnSave); 142 | panel1.add(grid).setStyleAttributes(subPanelCSS); 143 | app.add(label1); 144 | app.add(panel1); 145 | ss.show(app); 146 | } 147 | 148 | function saveSettings(e){ 149 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 150 | var app = UiApp.getActiveApplication(); 151 | // we have the settings in e.parameter 152 | var sProps = PropertiesService.getScriptProperties(); 153 | var p = e.parameter; 154 | var properties = { appType: p.appType, userAgent: p.userAgent, consumerKey: p.consumerKey, consumerSecret: p.consumerSecret, rsaKey: p.RSA, 155 | callbackURL: p.callBack }; 156 | if (sProps.setProperties(properties)) 157 | ss.toast('Xero Settings saved.', '', 5); 158 | else 159 | ss.toast('Xero settings could not be saved.', '', 5); 160 | if (p.appType != 'Private') { 161 | app.remove(app.getElementById('label1')); 162 | app.remove(app.getElementById('panel1')); 163 | app.setHeight(50);app.setWidth(100); 164 | var link = app.createAnchor('Connect to Xero', true, Xero.connect()); 165 | var handler = app.createServerHandler('closeApp'); 166 | link.addClickHandler(handler); 167 | app.add(link); 168 | } 169 | else 170 | app.close(); 171 | return app; 172 | } 173 | 174 | // Download Functions 175 | function downloadInvoices() { 176 | downloadOptions('Invoices'); 177 | } 178 | 179 | function downloadPayments() { 180 | downloadOptions('Payments'); 181 | } 182 | 183 | function downloadOptions(item) { 184 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 185 | var app = UiApp.createApplication(); 186 | var panel = app.createAbsolutePanel().setStyleAttributes(subPanelCSS).setWidth("475"); 187 | var lblHeader = app.createLabel('Download ' + item).setStyleAttribute('font-weight', 'bold'); 188 | var gridMain = app.createGrid(4, 2); 189 | var optAllHandler = app.createClientHandler(); 190 | var optModifiedAfterHandler = app.createClientHandler(); 191 | var optAll = app.createRadioButton('optAll', 'All').setId('optAll').setValue(true).addClickHandler(optAllHandler); 192 | var optModifiedAfter = app.createRadioButton('optModifiedAfter', 'Modified After').setValue(false).addClickHandler(optModifiedAfterHandler); 193 | var modifiedAfterDate = app.createDatePicker().setVisible(false).setName('modifiedAfterDate').setValue(new Date()); 194 | 195 | optAllHandler.forEventSource().setEnabled(true).forTargets(optModifiedAfter).setValue(false).forTargets(modifiedAfterDate).setVisible(false); 196 | optModifiedAfterHandler.forEventSource().setEnabled(true).forTargets(optAll).setValue(false).forTargets(modifiedAfterDate).setVisible(true); 197 | 198 | switch(item) { 199 | case 'Invoices': 200 | var clickHandler = app.createServerHandler('handleInvoicesDownload'); 201 | break; 202 | case 'Payments': 203 | var clickHandler = app.createServerHandler('handlePaymentsDownload'); 204 | break; 205 | } 206 | var btnHandler = app.createClientHandler().forEventSource().setEnabled(false).forTargets(optAll).setEnabled(false).forTargets(optModifiedAfter).setEnabled(false).forTargets(modifiedAfterDate).setEnabled(false); 207 | var btnDownload = app.createButton('Download', clickHandler).addClickHandler(btnHandler); 208 | 209 | // callback elements 210 | clickHandler.addCallbackElement(optAll).addCallbackElement(optModifiedAfter).addCallbackElement(modifiedAfterDate); 211 | gridMain.setWidget(0, 0, optAll); 212 | gridMain.setWidget(1, 0, optModifiedAfter); 213 | gridMain.setWidget(2, 1, modifiedAfterDate); 214 | gridMain.setWidget(3, 0, btnDownload); 215 | 216 | panel.add(gridMain); 217 | app.add(lblHeader); 218 | app.add(panel); 219 | 220 | ss.show(app); 221 | } 222 | 223 | function handleInvoicesDownload(){ 224 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 225 | var sheet = ss.getSheetByName("Invoices Download"); 226 | var li_sheet = ss.getSheetByName("Download - Invoices Line Items"); 227 | 228 | if (!sheet) ss.insertSheet("Invoices Download"); 229 | if (!li_sheet) ss.insertSheet("Download - Invoices Line Items"); 230 | 231 | // Add headers 232 | if (sheet.getLastRow() == 0) { 233 | var arrHeaders = ['Type', 'Contact Name', 'Date', 'Due Date', 'Status', 'Line Amount Types', 'Sub Total', 'Total Tax', 'Total', 'Updated Date (UTC)', 234 | 'Currency Code', 'Invoice ID', 'Invoice Number', 'Amount Due', 'Amount Paid', 'Amount Credited']; 235 | sheet.appendRow(arrHeaders).getRange(1, 1, 1, 16).setFontWeight('bold'); 236 | } 237 | if (li_sheet.getLastRow() == 0) { 238 | var liHeaders = ['Invoice ID', 'Invoice Number', 'Description', 'Quantity', 'Unit Amount', 'Tax Type', 'Tax Amount', 'Line Amount', 'Account Code']; 239 | li_sheet.appendRow(liHeaders).getRange(1, 1, 1, 9).setFontWeight('bold'); 240 | } 241 | 242 | var pageNo = 1; 243 | var moreData = true; 244 | 245 | try { 246 | // Connect to Xero 247 | Xero.connect(); 248 | 249 | while (moreData) { 250 | ss.toast('Downloading page ' + pageNo + ' ...'); 251 | // API call 252 | var invoice_info = Xero.fetchData('Invoices', pageNo); 253 | Logger.log(invoice_info); 254 | 255 | if (invoice_info) var invoices = invoice_info.Invoices; 256 | else return false; 257 | 258 | // If less than 100 records returned, there are no more records 259 | if (invoices.length < 100) moreData = false; 260 | else pageNo++; 261 | 262 | // Processing the result and update Invoices data in the sheet 263 | for(var i=0; i < invoices.length; i++) { 264 | var invoice = invoices[i]; 265 | var accType = (invoice.Type != null) ? invoice.Type : ""; 266 | var contactName = (invoice.Contact != null) ? invoice.Contact.Name : ""; 267 | var date = (invoice.DateString != null) ? invoice.DateString : ""; 268 | var dueDate = (invoice.DueDateString != null) ? invoice.DueDateString : ""; 269 | var status = (invoice.Status != null) ? invoice.Status : ""; 270 | var lineAmountTypes = (invoice.LineAmountTypes != null) ? invoice.LineAmountTypes : ""; 271 | 272 | // Line Items present in the Middle 273 | var subTotal = (invoice.SubTotal != null) ? invoice.SubTotal : ""; 274 | var totalTax = (invoice.TotalTax != null) ? invoice.TotalTax : ""; 275 | var total = (invoice.Total != null) ? invoice.Total : ""; 276 | var updatedDateUTC = (invoice.UpdatedDateUTC != null) ? eval('new ' + invoice.UpdatedDateUTC.substr(1, invoice.UpdatedDateUTC.length - 2) + '.toISOString()') : "" ; 277 | var currencyCode = (invoice.CurrencyCode != null) ? invoice.CurrencyCode : ""; 278 | var invoiceID = (invoice.InvoiceID != null) ? invoice.InvoiceID : ""; 279 | var invoiceNumber = (invoice.InvoiceNumber != null) ? invoice.InvoiceNumber : ""; 280 | 281 | // Payments present in the Middle 282 | var amountDue = (invoice.AmountDue != null) ? invoice.AmountDue : ""; 283 | var amountPaid = (invoice.AmountPaid != null) ? invoice.AmountPaid : ""; 284 | var AmountCredited = (invoice.AmountCredited != null) ? invoice.AmountCredited : 0; 285 | 286 | // Add Line Items to the Invoice in the end, because all other fields for invoice will be common 287 | var arr_line_items = []; 288 | 289 | var lineItems = (invoice.LineItems != null) ? invoice.LineItems : []; 290 | for(var k=0; k < lineItems.length; k++){ 291 | var item = lineItems[k]; 292 | var description = (item.Description != null ) ? item.Description : ""; 293 | var quantity = (item.Quantity != null) ? item.Quantity : ""; 294 | var unitAmount = (item.UnitAmount != null) ? item.UnitAmount : ""; 295 | var taxType = (item.TaxType != null) ? item.TaxType : ""; 296 | var taxAmount = (item.TaxAmount != null) ? item.TaxAmount : ""; 297 | var lineAmount = (item.LineAmount != null) ? item.LineAmount : ""; 298 | var accountCode = (item.AccountCode != null) ? item.AccountCode : ""; 299 | 300 | var tmp = [invoiceID,invoiceNumber,description,quantity,unitAmount,taxType,taxAmount,lineAmount,accountCode]; 301 | li_sheet.appendRow(tmp); 302 | } 303 | // Add data in the Spreadsheet 304 | var arr =[accType, contactName, date, dueDate, status, lineAmountTypes, subTotal, totalTax, total, updatedDateUTC, currencyCode, invoiceID, invoiceNumber,amountDue, amountPaid, AmountCredited ]; 305 | sheet.appendRow(arr); 306 | } 307 | } 308 | } 309 | catch(e) { 310 | Browser.msgBox('handleInvoicesDownload: ' + e.message); 311 | } 312 | } 313 | 314 | function handlePaymentsDownload(){ 315 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 316 | var sheet = ss.getSheetByName("Payments Download"); 317 | 318 | if (!sheet) { 319 | ss.insertSheet("Payments Download"); 320 | sheet = ss.getActiveSheet(); 321 | } 322 | sheet.activate(); 323 | 324 | // Add headers 325 | if (sheet.getLastRow() == 0) { 326 | var arrHeaders = ['Payment ID', 'Date', 'Amount', 'Currency Rate', 'Payment Type', 'Status', 'Update Date (UTC)', 'Account ID', 'Contact ID', 'Contact Name', 327 | 'Invoice Type', 'Invoice ID', 'Invoice Number']; 328 | sheet.appendRow(arrHeaders).getRange(1, 1, 1, 13).setFontWeight('bold'); 329 | } 330 | 331 | var pageNo = 1; 332 | var moreData = true; 333 | 334 | try { 335 | // Connect to Xero 336 | Xero.connect(); 337 | 338 | ss.toast("Downloading page " + pageNo + "..."); 339 | while (moreData) { 340 | // API call 341 | var payments_info = Xero.fetchData('Payments', pageNo); // Data should retrieve in JSON format 342 | 343 | if (payments_info) var payments = payments_info.Payments; 344 | else return app; 345 | 346 | // Processing the result and update Invoices data in the sheet 347 | for(var i=0; i < payments.length; i++){ 348 | var payment = payments[i]; 349 | var paymentID = (payment.PaymentID != null) ? payment.PaymentID : ""; 350 | var date = (payment.Date != null) ? eval('new ' + payment.Date.substr(1, payment.Date.length - 2) + '.toISOString()') : "" ; 351 | var amount = (payment.Amount != null) ? payment.Amount : ""; 352 | var currencyRate = (payment.CurrencyRate != null) ? payment.CurrencyRate : ""; 353 | var paymentType = (payment.PaymentType != null) ? payment.PaymentType : ""; 354 | var status = (payment.Status != null) ? payment.Status : ""; 355 | var updatedDateUTC = (payment.UpdatedDateUTC != null) ? eval('new ' + payment.UpdatedDateUTC.substr(1, payment.UpdatedDateUTC.length - 2) + '.toISOString()') : "" ; 356 | 357 | var accountId = ""; 358 | var account = payment.Account; 359 | if (account != null && account != undefined && account != ""){ 360 | accountId = ( account.AccountID != null ) ? account.AccountID : ""; 361 | } 362 | 363 | var contactID = "", inv_type = "", inv_id = "", inv_number ="", contactName=""; 364 | var invoice = payment.Invoice; 365 | if (invoice != undefined && invoice != null && invoice != "") { 366 | inv_type = (invoice.Type != null) ? invoice.Type : ""; 367 | inv_id = (invoice.InvoiceID != null) ? invoice.InvoiceID : ""; 368 | inv_number = (invoice.InvoiceNumber != null) ? invoice.InvoiceNumber : ""; 369 | var contact = invoice.Contact ; 370 | 371 | if(contact != null && contact != undefined && contact != ""){ 372 | contactID = (contact.ContactID != null) ? contact.ContactID : ""; 373 | contactName = (contact.Name != null) ? contact.Name : ""; 374 | } 375 | } 376 | // Add data in the Spreadsheet 377 | var arr = [paymentID, date, amount, currencyRate, paymentType, status, updatedDateUTC, accountId, contactID, contactName, inv_type, inv_id, inv_number]; 378 | sheet.appendRow(arr); 379 | } 380 | 381 | // More data? 382 | if (payments.length < 100) moreData = false; 383 | else pageNo++; 384 | } 385 | } 386 | catch(e) { 387 | Browser.msgBox(e.message); 388 | } 389 | } 390 | 391 | 392 | // Upload Functions 393 | function uploadInvoices(){ 394 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 395 | var sheet = ss.getSheetByName("Invoices Upload"); 396 | var l_sheet = ss.getSheetByName("Upload - Invoice Line Items"); 397 | 398 | if (!sheet) { 399 | ss.insertSheet('Invoices Upload'); 400 | sheet = ss.getSheetByName("Invoices Upload"); 401 | } 402 | 403 | if (!l_sheet) { 404 | ss.insertSheet('Upload - Invoice Line Items'); 405 | l_sheet = ss.getSheetByName("Upload - Invoice Line Items"); 406 | } 407 | 408 | if (sheet.getLastRow() == 0) { 409 | var arrHeaders = ['Invoice Number', 'Invoice Type', 'Contact Name', 'Contact Number', 'Issue Date', 'Due Date', 'Line Amount Types', 'Reference', 'Bid', 'URL', 410 | 'Currency Code', 'Status', 'Sent to Contact', 'Sub Total', 'Total Tax', 'Total']; 411 | sheet.appendRow(arrHeaders).getRange(1, 1, 1, 16).setFontWeight('bold'); 412 | } 413 | if (l_sheet.getLastRow() == 0) { 414 | var liHeaders = ['Invoice Number', 'Description', 'Quantity', 'Unit Amount', 'Tax Type', 'Tax Amount', 'Line Amount', 'Account Code', 'Region']; 415 | l_sheet.appendRow(liHeaders).getRange(1, 1, 1, 9).setFontWeight('bold'); 416 | } 417 | 418 | sheet.activate(); 419 | 420 | if (sheet.getLastRow() <= 1 || l_sheet.getLastRow() <= 1) { 421 | Browser.msgBox('Error: No data to upload'); 422 | return false; 423 | } 424 | 425 | var l_lrow = l_sheet.getDataRange().getLastRow(); 426 | var l_data = l_sheet.getDataRange().getValues(); 427 | 428 | var lrow = sheet.getDataRange().getLastRow(); 429 | var lcol = 17; 430 | 431 | var data = sheet.getRange(2, 1, lrow-1, lcol).getValues(); 432 | var payload = ""; 433 | for(var i=0; i < data.length; i++) { 434 | 435 | var inv_number = data[i][0]; 436 | var inv_type = data[i][1]; 437 | var contact_name = data[i][2]; 438 | var contact_number = data[i][3]; 439 | var issue_date = data[i][4]; 440 | var due_date = data[i][5]; 441 | var line_amount_types = data[i][6]; 442 | var reference = data[i][7]; 443 | var bid = data[i][8]; 444 | var url = data[i][9]; 445 | var currency_code = data[i][10]; 446 | var status = data[i][11]; 447 | var sent_to_contact = data[i][12]; 448 | var sub_total = data[i][13]; 449 | var total_tax = data[i][14]; 450 | var total = data[i][15]; 451 | 452 | payload += ""; 453 | payload += "" + inv_number + ""; 454 | payload += "" + inv_type + ""; 455 | payload += ""; 456 | payload += "" + contact_name + ""; 457 | payload += "" + contact_number + ""; 458 | payload += ""; 459 | 460 | // Removed the Address related fields in Contact. Add them as required 461 | payload += "" + Utilities.formatDate(issue_date, "GMT", "yyyy-MM-dd") + ""; 462 | payload += "" + Utilities.formatDate(due_date, "GMT", "yyyy-MM-dd") + ""; 463 | payload += "" + line_amount_types + ""; 464 | if(inv_type == "ACCREC"){ 465 | payload += "" + reference + ""; 466 | } 467 | payload += "" + url + ""; 468 | payload += "" + currency_code + ""; 469 | payload += "" + status + ""; 470 | payload += "" + sent_to_contact + ""; 471 | payload += "" + sub_total + ""; 472 | payload += "" + total_tax + ""; 473 | payload += "" + total + ""; 474 | 475 | payload += ""; 476 | for(var j=2; j < l_data.length; j++){ 477 | var l_inv_number = l_data[j][0]; 478 | if(l_inv_number != undefined && l_inv_number != null && l_inv_number != "" && l_inv_number == inv_number){ 479 | payload += ""; 480 | 481 | var l_desc = l_data[j][1]; 482 | var l_quantity = l_data[j][2]; 483 | var l_unitamount = l_data[j][3]; 484 | var l_taxtype = l_data[j][4]; 485 | var l_taxamount = l_data[j][5]; 486 | var l_lineamount = l_data[j][6]; 487 | var l_accountcode = l_data[j][7]; 488 | var l_region = l_data[j][8]; 489 | 490 | payload += "" + l_desc + ""; 491 | payload += "" + l_quantity + ""; 492 | payload += "" + l_unitamount + ""; 493 | payload += "" + l_taxtype + ""; 494 | payload += "" + l_taxamount + ""; 495 | payload += "" + l_lineamount + ""; 496 | payload += "" + l_accountcode + ""; 497 | payload += "" + l_region + ""; 498 | 499 | payload += ""; 500 | } 501 | } 502 | payload += ""; 503 | payload += ""; 504 | } 505 | payload += ""; 506 | 507 | try { 508 | Xero.connect(); 509 | var response = Xero.uploadData('Invoices', payload); 510 | if (response) { 511 | // request successful, Log failed invoices 512 | var invoices = response.Invoices; 513 | logFailedInvoices(invoices); 514 | } 515 | } 516 | catch(e) { 517 | Browser.msgBox(e.message); 518 | } 519 | } 520 | 521 | function logFailedInvoices(invoices){ 522 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 523 | var errorSheet = ss.getSheetByName("Error Invoices Upload"); 524 | var errorLISheet = ss.getSheetByName("Error Upload - Invoice Line Items"); 525 | 526 | if (!errorSheet) ss.insertSheet("Error Invoices Upload"); 527 | if (!errorLISheet) ss.insertSheet("Error Upload - Invoice Line Items"); 528 | 529 | // Add headers 530 | if (errorSheet.getLastRow() == 0) { 531 | var arrHeaders = ['Type', 'Contact Name', 'Date', 'Due Date', 'Status', 'Line Amount Types', 'Sub Total', 'Total Tax', 'Total', 'Updated Date (UTC)', 532 | 'Currency Code', 'Invoice ID', 'Invoice Number', 'Amount Due', 'Amount Paid', 'Amount Credited']; 533 | errorSheet.appendRow(arrHeaders).getRange(1, 1, 1, 16).setFontWeight('bold'); 534 | } 535 | if (errorLISheet.getLastRow() == 0) { 536 | var liHeaders = ['Invoice ID', 'Description', 'Quantity', 'Unit Amount', 'Tax Type', 'Tax Amount', 'Line Amount', 'Account Code']; 537 | errorLISheet.appendRow(liHeaders).getRange(1, 1, 1, 8).setFontWeight('bold'); 538 | } 539 | 540 | // Processing the result and update Invoices data in the sheet 541 | var failedInvoices =0; 542 | for(var i=0; i < invoices.length; i++){ 543 | var invoice = invoices[i]; 544 | var StatusAttributeString = (invoice.StatusAttributeString != null) ? invoice.StatusAttributeString : ""; 545 | if (StatusAttributeString == "ERROR") { // an error occured for this invoice 546 | failedInvoices++; 547 | var accType = (invoice.Type != null) ? invoice.Type : ""; 548 | var contactName = (invoice.Contact.Name != null) ? invoice.Contact.Name : ""; 549 | var date = (invoice.DateString != null) ? invoice.DateString : ""; 550 | var dueDate = (invoice.DueDateString != null) ? invoice.DueDateString : ""; 551 | var status = (invoice.Status != null) ? invoice.Status : ""; 552 | var lineAmountTypes = (invoice.LineAmountTypes != null) ? invoice.LineAmountTypes : ""; 553 | 554 | // Line Items present in the Middle 555 | var subTotal = (invoice.SubTotal != null) ? invoice.SubTotal : ""; 556 | var totalTax = (invoice.TotalTax != null) ? invoice.TotalTax : ""; 557 | var total = (invoice.Total != null) ? invoice.Total : ""; 558 | var updatedDateUTC = (invoice.UpdatedDateUTC != null) ? eval('new ' + invoice.UpdatedDateUTC.substr(1, invoice.UpdatedDateUTC.length - 2) + '.toISOString()') : "" ; 559 | var currencyCode = (invoice.CurrencyCode != null) ? invoice.CurrencyCode : ""; 560 | var invoiceID = (invoice.InvoiceID != null) ? invoice.InvoiceID : ""; 561 | var invoiceNumber = (invoice.InvoiceNumber != null) ? invoice.InvoiceNumber : ""; 562 | 563 | // Payments present in the Middle 564 | var amountDue = (invoice.AmountDue != null) ? invoice.AmountDue : ""; 565 | var amountPaid = (invoice.AmountPaid != null) ? invoice.AmountPaid : ""; 566 | var AmountCredited = (invoice.AmountCredited != null) ? invoice.AmountCredited : 0; 567 | 568 | // Add Line Items to the Invoice in the end, because all other fields for invoice will be common 569 | var arr_line_items = []; 570 | 571 | var lineItems = (invoice.LineItems != null) ? invoice.LineItems : []; 572 | for(var k=0; k < lineItems.length; k++){ 573 | var item = lineItems[k]; 574 | var description = (item.Description != null ) ? item.Description : ""; 575 | var quantity = (item.Quantity != null) ? item.Quantity : ""; 576 | var unitAmount = (item.UnitAmount != null) ? item.UnitAmount : ""; 577 | var taxType = (item.TaxType != null) ? item.TaxType : ""; 578 | var taxAmount = (item.TaxAmount != null) ? item.TaxAmount : ""; 579 | var lineAmount = (item.LineAmount != null) ? item.LineAmount : ""; 580 | var accountCode = (item.AccountCode != null) ? item.AccountCode : ""; 581 | 582 | var tmp = [invoiceID,description,quantity,unitAmount,taxType,taxAmount,lineAmount,accountCode]; 583 | errorLISheet.appendRow(tmp); 584 | } 585 | // Add data in the Spreadsheet 586 | var arr =[accType, contactName, date, dueDate, status, lineAmountTypes, subTotal, totalTax, total, updatedDateUTC, currencyCode, invoiceID, invoiceNumber,amountDue, amountPaid, AmountCredited ]; 587 | errorSheet.appendRow(arr); 588 | } 589 | } 590 | if (failedInvoices > 0) 591 | Browser.msgBox(failedInvoices + " invoices could not be saved to Xero due to a validation error. See \"Error Invoices Upload\" & \"Error Invoices Upload Line Items\" sheets."); 592 | } 593 | 594 | function uploadPayments(){ 595 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 596 | var sheet = ss.getSheetByName("Payments Upload"); 597 | 598 | if (!sheet) { 599 | Browser.msgBox('Error: "Payments Upload" sheet not found.'); 600 | return false; 601 | } 602 | else if (sheet.getLastRow() == 0) { 603 | Browser.msgBox('Error: "Payments Upload" sheet has no data.'); 604 | var headers = ['Invoice ID', 'Invoice Number', 'Account ID', 'Account Code', 'Date', 'Currency Rate', 'Amount', 'Reference']; 605 | sheet.getRange(1, 1, 1, 8).setValues(headers).setFontWeight('bold'); 606 | return false; 607 | } 608 | var lrow = sheet.getDataRange().getLastRow(); 609 | var lcol = 8; 610 | 611 | var data = sheet.getRange(3, 1, lrow-2, lcol).getValues(); 612 | 613 | var payload = ""; 614 | for(var i=0; i < data.length; i++){ 615 | var invoiceID = data[i][0]; 616 | var invNumber = data[i][1]; 617 | var accID = data[i][2]; 618 | var accCode = data[i][3]; 619 | var date = data[i][4]; 620 | var cur_rate = data[i][5]; 621 | var amount = data[i][6]; 622 | var reference = data[i][7]; 623 | payload += ""; 624 | 625 | if (invoiceID != null && invoiceID != undefined && invoiceID != "" ) 626 | payload += "" + invoiceID + ""; 627 | else if (invNumber != null && invNumber != undefined && invNumber != "") { 628 | payload += "" + invNumber + ""; 629 | } else { 630 | Browser.msgBox("Either InvoiceID or Invoice Number should be present. Please see Row:" + i+3); 631 | return; 632 | } 633 | 634 | if (accID != null && accID != undefined && accID != "") 635 | payload += "" + accID + ""; 636 | else if (accCode != null && accCode != undefined && accCode != "") 637 | payload += "" + accCode + ""; 638 | else { 639 | Browser.msgBox("Either AccountID or Account Code should be present. Please see Row:" + i+3); 640 | return; 641 | } 642 | 643 | payload += "" + Utilities.formatDate(date, "GMT", "yyyy-MM-dd") + ""; 644 | payload += "" + cur_rate + ""; 645 | payload += "" + amount + ""; 646 | payload += "" + reference + ""; 647 | payload += ""; 648 | 649 | } 650 | payload += ""; 651 | 652 | try { 653 | Xero.connect(); 654 | var method = 'PUT'; 655 | var response = XERO.uploadData('Payments', payload, method); 656 | if (response.code != 200) { 657 | // Log failed items if any 658 | if (response.code == 400) { 659 | Browser.msgBox(response.Message + ' . See "Error Payments Upload" sheet for details. Response Code: ' + response.code); 660 | logFailedPayments(response); 661 | } 662 | else 663 | Browser.msgBox('Error occured. Response Code: ' + response.code); 664 | } 665 | } 666 | catch(e) { 667 | Browser.msgBox(e.message); 668 | } 669 | } 670 | 671 | function logFailedPayments(response) { 672 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 673 | var errorPayments = ss.getSheetByName('Error Payments Upload'); 674 | if (!errorPayments) 675 | ss.insertSheet('Error Payments Upload'); 676 | if (errorPayments.getLastRow() == 0) { 677 | // insert column headers 678 | var colHeaders = ['Invoice ID', 'Invoice Number', 'Account ID', 'Account Code', 'Date', 'Currency Rate', 'Amount', 'Reference', 'Error Message']; 679 | var range = errorPayments.getRange(1, 1, 1, 9).setValues([colHeaders]).setFontWeight('bold'); 680 | } 681 | // read response elements 682 | var elements = response.Elements; 683 | for (var i = 0; i < elements.length; i++) { 684 | var elem = elements[i]; 685 | var invoiceID = elem.Invoice.InvoiceID ? elem.Invoice.InvoiceID : ""; 686 | var invoiceNum = elem.Invoice.InvoiceNumber ? elem.Invoice.InvoiceNumber : ""; 687 | var accID = elem.Account.AccountID ? elem.Account.AccountID : ""; 688 | var accCode = elem.Account.Code ? elem.Account.Code : ""; 689 | var date = elem.Date ? eval("new " + elem.Date.substr(2, elem.Date.length - 2) + ".toISOString()") : ""; 690 | var curRate = elem.Invoice.CurrencyRate ? elem.Invoice.CurrencyRate : ""; 691 | var amount = elem.Amount ? elem.Amount : ""; 692 | var ref = elem.Invoice.Reference ? elem.Invoice.Reference : ""; 693 | var errors = elem.ValidationErrors; 694 | var errorMsg = ""; 695 | for (var j = 0; j < errors.length; j++) 696 | errorMsg += errors[j].Message ? errors[j].Message + ". " : ""; 697 | 698 | var row = [invoiceID, invoiceNum, accID, accCode, date, curRate, amount, ref, errorMsg]; 699 | errorPayments.appendRow(row); 700 | } 701 | } 702 | 703 | // Utilities 704 | function generateRandomString(n) { 705 | var chars = ['a', 'b','c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; 706 | chars.push('A', 'B', 'C', 'D', 'E', 'F','G','H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'); 707 | var randomString = ''; 708 | for (i=0; i < n; i++) { 709 | r = Math.random(); 710 | r = r * 61; 711 | r = Math.round(r); 712 | randomString = randomString + chars[r]; 713 | } 714 | return randomString; 715 | } 716 | 717 | function convertToUTC(input) { 718 | var inter = new Date(Date.parse(input)); 719 | var y = inter.getFullYear(); 720 | var m = inter.getMonth() + 1; 721 | var d = inter.getDate(); 722 | mString = m; dString = d; yString = y; 723 | if (m < 10) 724 | var mString = '0' + m; 725 | if (d < 10) 726 | var dString = '0' + d; 727 | 728 | var output = yString + '-' + mString + '-' + dString + 'T00:00:00'; 729 | Browser.msgBox('convertToUTC: ' + output); 730 | return output; 731 | } 732 | 733 | /* 734 | function download_line_items_for_invoices(){ 735 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 736 | var sheet = ss.getSheetByName("Xero Invoices Download"); 737 | var osheet = ss.getSheetByName("Download - Invoices Line Items"); 738 | 739 | var line_items_status_col = 17; 740 | var lrow = sheet.getDataRange().getLastRow(); 741 | var data = sheet.getRange(3, line_items_status_col, lrow-2, 1).getValues(); 742 | 743 | var invoices = []; 744 | for(var i=0; i < data.length; i++){ 745 | if(data[i][0] != undefined && data[i][0] != null && data[i][0] != "YES"){ 746 | var invoice_id = sheet.getRange(i+3, 12).getValue(); 747 | 748 | var responses = XERO.download_invoice_for_an_id(invoice_id); 749 | var x = 1; 750 | 751 | var invoice = responses.Response.Invoices.Invoice; 752 | var line_items = invoice.LineItems.LineItem; 753 | 754 | if(line_items[0] != undefined && line_items[0] != null && line_items[0] != ""){ 755 | 756 | for(var z=0; z < line_items.length; z++){ 757 | var line_item = line_items[z]; 758 | 759 | var description = (line_item.Description != null) ? line_item.Description : ""; 760 | var quantity = (line_item.Quantity != null) ? line_item.Quantity : ""; 761 | var unitAmount = (line_item.UnitAmount != null) ? line_item.UnitAmount : ""; 762 | var taxType = (line_item.TaxType != null) ? line_item.TaxType : ""; 763 | var taxAmount = (line_item.TaxAmount != null) ? line_item.TaxAmount : ""; 764 | var lineAmount = (line_item.LineAmount != null) ? line_item.LineAmount : ""; 765 | var accountCode = (line_item.AccountCode != null) ? line_item.AccountCode : ""; 766 | 767 | var row = [invoice_id, description, quantity, unitAmount, taxType, taxAmount, lineAmount, accountCode]; 768 | osheet.appendRow(row).activate(); 769 | } 770 | 771 | }else{ 772 | var description = (line_items.Description != null) ? line_items.Description : ""; 773 | var quantity = (line_items.Quantity != null) ? line_items.Quantity : ""; 774 | var unitAmount = (line_items.UnitAmount != null) ? line_items.UnitAmount : ""; 775 | var taxType = (line_items.TaxType != null) ? line_items.TaxType : ""; 776 | var taxAmount = (line_items.TaxAmount != null) ? line_items.TaxAmount : ""; 777 | var lineAmount = (line_items.LineAmount != null) ? line_items.LineAmount : ""; 778 | var accountCode = (line_items.AccountCode != null) ? line_items.AccountCode : ""; 779 | 780 | var row = [invoice_id, description, quantity, unitAmount, taxType, taxAmount, lineAmount, accountCode]; 781 | osheet.appendRow(row).activate(); 782 | } 783 | 784 | sheet.getRange(i+3, line_items_status_col).setValue("YES"); 785 | } 786 | } 787 | 788 | } 789 | */ 790 | -------------------------------------------------------------------------------- /rsa/crypto-1.1.gs: -------------------------------------------------------------------------------- 1 | /*! crypto-1.1.5.js (c) 2013 Kenji Urushima | kjur.github.com/jsrsasign/license 2 | */ 3 | /* 4 | * crypto.js - Cryptographic Algorithm Provider class 5 | * 6 | * Copyright (c) 2013 Kenji Urushima (kenji.urushima@gmail.com) 7 | * 8 | * This software is licensed under the terms of the MIT License. 9 | * http://kjur.github.com/jsrsasign/license 10 | * 11 | * The above copyright and license notice shall be 12 | * included in all copies or substantial portions of the Software. 13 | */ 14 | 15 | /** 16 | * @fileOverview 17 | * @name crypto-1.1.js 18 | * @author Kenji Urushima kenji.urushima@gmail.com 19 | * @version 1.1.5 (2013-Oct-06) 20 | * @since jsrsasign 2.2 21 | * @license MIT License 22 | */ 23 | 24 | /** 25 | * kjur's class library name space 26 | * @name KJUR 27 | * @namespace kjur's class library name space 28 | */ 29 | if (typeof KJUR == "undefined" || !KJUR) KJUR = {}; 30 | /** 31 | * kjur's cryptographic algorithm provider library name space 32 | *

33 | * This namespace privides following crytpgrahic classes. 34 | *

    35 | *
  • {@link KJUR.crypto.MessageDigest} - Java JCE(cryptograhic extension) style MessageDigest class
  • 36 | *
  • {@link KJUR.crypto.Signature} - Java JCE(cryptograhic extension) style Signature class
  • 37 | *
  • {@link KJUR.crypto.Util} - cryptographic utility functions and properties
  • 38 | *
39 | * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2. 40 | *

41 | * @name KJUR.crypto 42 | * @namespace 43 | */ 44 | if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {}; 45 | 46 | /** 47 | * static object for cryptographic function utilities 48 | * @name KJUR.crypto.Util 49 | * @class static object for cryptographic function utilities 50 | * @property {Array} DIGESTINFOHEAD PKCS#1 DigestInfo heading hexadecimal bytes for each hash algorithms 51 | * @property {Array} DEFAULTPROVIDER associative array of default provider name for each hash and signature algorithms 52 | * @description 53 | */ 54 | KJUR.crypto.Util = new function() { 55 | this.DIGESTINFOHEAD = { 56 | 'sha1': "3021300906052b0e03021a05000414", 57 | 'sha224': "302d300d06096086480165030402040500041c", 58 | 'sha256': "3031300d060960864801650304020105000420", 59 | 'sha384': "3041300d060960864801650304020205000430", 60 | 'sha512': "3051300d060960864801650304020305000440", 61 | 'md2': "3020300c06082a864886f70d020205000410", 62 | 'md5': "3020300c06082a864886f70d020505000410", 63 | 'ripemd160': "3021300906052b2403020105000414", 64 | }; 65 | 66 | /* 67 | * @since crypto 1.1.1 68 | */ 69 | this.DEFAULTPROVIDER = { 70 | 'md5': 'cryptojs', 71 | 'sha1': 'cryptojs', 72 | 'sha224': 'cryptojs', 73 | 'sha256': 'cryptojs', 74 | 'sha384': 'cryptojs', 75 | 'sha512': 'cryptojs', 76 | 'ripemd160': 'cryptojs', 77 | 'hmacmd5': 'cryptojs', 78 | 'hmacsha1': 'cryptojs', 79 | 'hmacsha224': 'cryptojs', 80 | 'hmacsha256': 'cryptojs', 81 | 'hmacsha384': 'cryptojs', 82 | 'hmacsha512': 'cryptojs', 83 | 'hmacripemd160': 'cryptojs', 84 | 85 | 'MD5withRSA': 'cryptojs/jsrsa', 86 | 'SHA1withRSA': 'cryptojs/jsrsa', 87 | 'SHA224withRSA': 'cryptojs/jsrsa', 88 | 'SHA256withRSA': 'cryptojs/jsrsa', 89 | 'SHA384withRSA': 'cryptojs/jsrsa', 90 | 'SHA512withRSA': 'cryptojs/jsrsa', 91 | 'RIPEMD160withRSA': 'cryptojs/jsrsa', 92 | 93 | 'MD5withECDSA': 'cryptojs/jsrsa', 94 | 'SHA1withECDSA': 'cryptojs/jsrsa', 95 | 'SHA224withECDSA': 'cryptojs/jsrsa', 96 | 'SHA256withECDSA': 'cryptojs/jsrsa', 97 | 'SHA384withECDSA': 'cryptojs/jsrsa', 98 | 'SHA512withECDSA': 'cryptojs/jsrsa', 99 | 'RIPEMD160withECDSA': 'cryptojs/jsrsa', 100 | 101 | 'SHA1withDSA': 'cryptojs/jsrsa', 102 | 'SHA224withDSA': 'cryptojs/jsrsa', 103 | 'SHA256withDSA': 'cryptojs/jsrsa', 104 | 105 | 'MD5withRSAandMGF1': 'cryptojs/jsrsa', 106 | 'SHA1withRSAandMGF1': 'cryptojs/jsrsa', 107 | 'SHA224withRSAandMGF1': 'cryptojs/jsrsa', 108 | 'SHA256withRSAandMGF1': 'cryptojs/jsrsa', 109 | 'SHA384withRSAandMGF1': 'cryptojs/jsrsa', 110 | 'SHA512withRSAandMGF1': 'cryptojs/jsrsa', 111 | 'RIPEMD160withRSAandMGF1': 'cryptojs/jsrsa', 112 | }; 113 | 114 | /* 115 | * @since crypto 1.1.2 116 | */ 117 | this.CRYPTOJSMESSAGEDIGESTNAME = { 118 | 'md5': 'CryptoJS.algo.MD5', 119 | 'sha1': 'CryptoJS.algo.SHA1', 120 | 'sha224': 'CryptoJS.algo.SHA224', 121 | 'sha256': 'CryptoJS.algo.SHA256', 122 | 'sha384': 'CryptoJS.algo.SHA384', 123 | 'sha512': 'CryptoJS.algo.SHA512', 124 | 'ripemd160': 'CryptoJS.algo.RIPEMD160' 125 | }; 126 | 127 | /** 128 | * get hexadecimal DigestInfo 129 | * @name getDigestInfoHex 130 | * @memberOf KJUR.crypto.Util 131 | * @function 132 | * @param {String} hHash hexadecimal hash value 133 | * @param {String} alg hash algorithm name (ex. 'sha1') 134 | * @return {String} hexadecimal string DigestInfo ASN.1 structure 135 | */ 136 | this.getDigestInfoHex = function(hHash, alg) { 137 | if (typeof this.DIGESTINFOHEAD[alg] == "undefined") 138 | throw "alg not supported in Util.DIGESTINFOHEAD: " + alg; 139 | return this.DIGESTINFOHEAD[alg] + hHash; 140 | }; 141 | 142 | /** 143 | * get PKCS#1 padded hexadecimal DigestInfo 144 | * @name getPaddedDigestInfoHex 145 | * @memberOf KJUR.crypto.Util 146 | * @function 147 | * @param {String} hHash hexadecimal hash value of message to be signed 148 | * @param {String} alg hash algorithm name (ex. 'sha1') 149 | * @param {Integer} keySize key bit length (ex. 1024) 150 | * @return {String} hexadecimal string of PKCS#1 padded DigestInfo 151 | */ 152 | this.getPaddedDigestInfoHex = function(hHash, alg, keySize) { 153 | var hDigestInfo = this.getDigestInfoHex(hHash, alg); 154 | var pmStrLen = keySize / 4; // minimum PM length 155 | 156 | if (hDigestInfo.length + 22 > pmStrLen) // len(0001+ff(*8)+00+hDigestInfo)=22 157 | throw "key is too short for SigAlg: keylen=" + keySize + "," + alg; 158 | 159 | var hHead = "0001"; 160 | var hTail = "00" + hDigestInfo; 161 | var hMid = ""; 162 | var fLen = pmStrLen - hHead.length - hTail.length; 163 | for (var i = 0; i < fLen; i += 2) { 164 | hMid += "ff"; 165 | } 166 | var hPaddedMessage = hHead + hMid + hTail; 167 | return hPaddedMessage; 168 | }; 169 | 170 | /** 171 | * get hexadecimal hash of string with specified algorithm 172 | * @name hashString 173 | * @memberOf KJUR.crypto.Util 174 | * @function 175 | * @param {String} s input string to be hashed 176 | * @param {String} alg hash algorithm name 177 | * @return {String} hexadecimal string of hash value 178 | * @since 1.1.1 179 | */ 180 | this.hashString = function(s, alg) { 181 | var md = new KJUR.crypto.MessageDigest({'alg': alg}); 182 | return md.digestString(s); 183 | }; 184 | 185 | /** 186 | * get hexadecimal hash of hexadecimal string with specified algorithm 187 | * @name hashHex 188 | * @memberOf KJUR.crypto.Util 189 | * @function 190 | * @param {String} sHex input hexadecimal string to be hashed 191 | * @param {String} alg hash algorithm name 192 | * @return {String} hexadecimal string of hash value 193 | * @since 1.1.1 194 | */ 195 | this.hashHex = function(sHex, alg) { 196 | var md = new KJUR.crypto.MessageDigest({'alg': alg}); 197 | return md.digestHex(sHex); 198 | }; 199 | 200 | /** 201 | * get hexadecimal SHA1 hash of string 202 | * @name sha1 203 | * @memberOf KJUR.crypto.Util 204 | * @function 205 | * @param {String} s input string to be hashed 206 | * @return {String} hexadecimal string of hash value 207 | * @since 1.0.3 208 | */ 209 | this.sha1 = function(s) { 210 | var md = new KJUR.crypto.MessageDigest({'alg':'sha1', 'prov':'cryptojs'}); 211 | return md.digestString(s); 212 | }; 213 | 214 | /** 215 | * get hexadecimal SHA256 hash of string 216 | * @name sha256 217 | * @memberOf KJUR.crypto.Util 218 | * @function 219 | * @param {String} s input string to be hashed 220 | * @return {String} hexadecimal string of hash value 221 | * @since 1.0.3 222 | */ 223 | this.sha256 = function(s) { 224 | var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'}); 225 | return md.digestString(s); 226 | }; 227 | 228 | this.sha256Hex = function(s) { 229 | var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'}); 230 | return md.digestHex(s); 231 | }; 232 | 233 | /** 234 | * get hexadecimal SHA512 hash of string 235 | * @name sha512 236 | * @memberOf KJUR.crypto.Util 237 | * @function 238 | * @param {String} s input string to be hashed 239 | * @return {String} hexadecimal string of hash value 240 | * @since 1.0.3 241 | */ 242 | this.sha512 = function(s) { 243 | var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'}); 244 | return md.digestString(s); 245 | }; 246 | 247 | this.sha512Hex = function(s) { 248 | var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'}); 249 | return md.digestHex(s); 250 | }; 251 | 252 | /** 253 | * get hexadecimal MD5 hash of string 254 | * @name md5 255 | * @memberOf KJUR.crypto.Util 256 | * @function 257 | * @param {String} s input string to be hashed 258 | * @return {String} hexadecimal string of hash value 259 | * @since 1.0.3 260 | */ 261 | this.md5 = function(s) { 262 | var md = new KJUR.crypto.MessageDigest({'alg':'md5', 'prov':'cryptojs'}); 263 | return md.digestString(s); 264 | }; 265 | 266 | /** 267 | * get hexadecimal RIPEMD160 hash of string 268 | * @name ripemd160 269 | * @memberOf KJUR.crypto.Util 270 | * @function 271 | * @param {String} s input string to be hashed 272 | * @return {String} hexadecimal string of hash value 273 | * @since 1.0.3 274 | */ 275 | this.ripemd160 = function(s) { 276 | var md = new KJUR.crypto.MessageDigest({'alg':'ripemd160', 'prov':'cryptojs'}); 277 | return md.digestString(s); 278 | }; 279 | 280 | /* 281 | * @since 1.1.2 282 | */ 283 | this.getCryptoJSMDByName = function(s) { 284 | 285 | }; 286 | }; 287 | 288 | /** 289 | * MessageDigest class which is very similar to java.security.MessageDigest class 290 | * @name KJUR.crypto.MessageDigest 291 | * @class MessageDigest class which is very similar to java.security.MessageDigest class 292 | * @param {Array} params parameters for constructor 293 | * @description 294 | *
295 | * Currently this supports following algorithm and providers combination: 296 | *
    297 | *
  • md5 - cryptojs
  • 298 | *
  • sha1 - cryptojs
  • 299 | *
  • sha224 - cryptojs
  • 300 | *
  • sha256 - cryptojs
  • 301 | *
  • sha384 - cryptojs
  • 302 | *
  • sha512 - cryptojs
  • 303 | *
  • ripemd160 - cryptojs
  • 304 | *
  • sha256 - sjcl (NEW from crypto.js 1.0.4)
  • 305 | *
306 | * @example 307 | * // CryptoJS provider sample 308 | * <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/core.js"></script> 309 | * <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/sha1.js"></script> 310 | * <script src="crypto-1.0.js"></script> 311 | * var md = new KJUR.crypto.MessageDigest({alg: "sha1", prov: "cryptojs"}); 312 | * md.updateString('aaa') 313 | * var mdHex = md.digest() 314 | * 315 | * // SJCL(Stanford JavaScript Crypto Library) provider sample 316 | * <script src="http://bitwiseshiftleft.github.io/sjcl/sjcl.js"></script> 317 | * <script src="crypto-1.0.js"></script> 318 | * var md = new KJUR.crypto.MessageDigest({alg: "sha256", prov: "sjcl"}); // sjcl supports sha256 only 319 | * md.updateString('aaa') 320 | * var mdHex = md.digest() 321 | */ 322 | KJUR.crypto.MessageDigest = function(params) { 323 | var md = null; 324 | var algName = null; 325 | var provName = null; 326 | 327 | /** 328 | * set hash algorithm and provider 329 | * @name setAlgAndProvider 330 | * @memberOf KJUR.crypto.MessageDigest 331 | * @function 332 | * @param {String} alg hash algorithm name 333 | * @param {String} prov provider name 334 | * @description 335 | * @example 336 | * // for SHA1 337 | * md.setAlgAndProvider('sha1', 'cryptojs'); 338 | * // for RIPEMD160 339 | * md.setAlgAndProvider('ripemd160', 'cryptojs'); 340 | */ 341 | this.setAlgAndProvider = function(alg, prov) { 342 | if (alg != null && prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg]; 343 | 344 | // for cryptojs 345 | if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(alg) != -1 && 346 | prov == 'cryptojs') { 347 | try { 348 | this.md = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[alg]).create(); 349 | } catch (ex) { 350 | throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex; 351 | } 352 | this.updateString = function(str) { 353 | this.md.update(str); 354 | }; 355 | this.updateHex = function(hex) { 356 | var wHex = CryptoJS.enc.Hex.parse(hex); 357 | this.md.update(wHex); 358 | }; 359 | this.digest = function() { 360 | var hash = this.md.finalize(); 361 | return hash.toString(CryptoJS.enc.Hex); 362 | }; 363 | this.digestString = function(str) { 364 | this.updateString(str); 365 | return this.digest(); 366 | }; 367 | this.digestHex = function(hex) { 368 | this.updateHex(hex); 369 | return this.digest(); 370 | }; 371 | } 372 | if (':sha256:'.indexOf(alg) != -1 && 373 | prov == 'sjcl') { 374 | try { 375 | this.md = new sjcl.hash.sha256(); 376 | } catch (ex) { 377 | throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex; 378 | } 379 | this.updateString = function(str) { 380 | this.md.update(str); 381 | }; 382 | this.updateHex = function(hex) { 383 | var baHex = sjcl.codec.hex.toBits(hex); 384 | this.md.update(baHex); 385 | }; 386 | this.digest = function() { 387 | var hash = this.md.finalize(); 388 | return sjcl.codec.hex.fromBits(hash); 389 | }; 390 | this.digestString = function(str) { 391 | this.updateString(str); 392 | return this.digest(); 393 | }; 394 | this.digestHex = function(hex) { 395 | this.updateHex(hex); 396 | return this.digest(); 397 | }; 398 | } 399 | }; 400 | 401 | /** 402 | * update digest by specified string 403 | * @name updateString 404 | * @memberOf KJUR.crypto.MessageDigest 405 | * @function 406 | * @param {String} str string to update 407 | * @description 408 | * @example 409 | * md.updateString('New York'); 410 | */ 411 | this.updateString = function(str) { 412 | throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName; 413 | }; 414 | 415 | /** 416 | * update digest by specified hexadecimal string 417 | * @name updateHex 418 | * @memberOf KJUR.crypto.MessageDigest 419 | * @function 420 | * @param {String} hex hexadecimal string to update 421 | * @description 422 | * @example 423 | * md.updateHex('0afe36'); 424 | */ 425 | this.updateHex = function(hex) { 426 | throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName; 427 | }; 428 | 429 | /** 430 | * completes hash calculation and returns hash result 431 | * @name digest 432 | * @memberOf KJUR.crypto.MessageDigest 433 | * @function 434 | * @description 435 | * @example 436 | * md.digest() 437 | */ 438 | this.digest = function() { 439 | throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName; 440 | }; 441 | 442 | /** 443 | * performs final update on the digest using string, then completes the digest computation 444 | * @name digestString 445 | * @memberOf KJUR.crypto.MessageDigest 446 | * @function 447 | * @param {String} str string to final update 448 | * @description 449 | * @example 450 | * md.digestString('aaa') 451 | */ 452 | this.digestString = function(str) { 453 | throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName; 454 | }; 455 | 456 | /** 457 | * performs final update on the digest using hexadecimal string, then completes the digest computation 458 | * @name digestHex 459 | * @memberOf KJUR.crypto.MessageDigest 460 | * @function 461 | * @param {String} hex hexadecimal string to final update 462 | * @description 463 | * @example 464 | * md.digestHex('0f2abd') 465 | */ 466 | this.digestHex = function(hex) { 467 | throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName; 468 | }; 469 | 470 | if (params !== undefined) { 471 | if (params['alg'] !== undefined) { 472 | this.algName = params['alg']; 473 | if (params['prov'] === undefined) 474 | this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]; 475 | this.setAlgAndProvider(this.algName, this.provName); 476 | } 477 | } 478 | }; 479 | 480 | /** 481 | * Mac(Message Authentication Code) class which is very similar to java.security.Mac class 482 | * @name KJUR.crypto.Mac 483 | * @class Mac class which is very similar to java.security.Mac class 484 | * @param {Array} params parameters for constructor 485 | * @description 486 | *
487 | * Currently this supports following algorithm and providers combination: 488 | *
    489 | *
  • hmacmd5 - cryptojs
  • 490 | *
  • hmacsha1 - cryptojs
  • 491 | *
  • hmacsha224 - cryptojs
  • 492 | *
  • hmacsha256 - cryptojs
  • 493 | *
  • hmacsha384 - cryptojs
  • 494 | *
  • hmacsha512 - cryptojs
  • 495 | *
496 | * NOTE: HmacSHA224 and HmacSHA384 issue was fixed since jsrsasign 4.1.4. 497 | * Please use 'ext/cryptojs-312-core-fix*.js' instead of 'core.js' of original CryptoJS 498 | * to avoid those issue. 499 | * @example 500 | * var mac = new KJUR.crypto.Mac({alg: "HmacSHA1", prov: "cryptojs", "pass": "pass"}); 501 | * mac.updateString('aaa') 502 | * var macHex = md.doFinal() 503 | */ 504 | KJUR.crypto.Mac = function(params) { 505 | var mac = null; 506 | var pass = null; 507 | var algName = null; 508 | var provName = null; 509 | var algProv = null; 510 | 511 | this.setAlgAndProvider = function(alg, prov) { 512 | if (alg == null) alg = "hmacsha1"; 513 | 514 | alg = alg.toLowerCase(); 515 | if (alg.substr(0, 4) != "hmac") { 516 | throw "setAlgAndProvider unsupported HMAC alg: " + alg; 517 | } 518 | 519 | if (prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg]; 520 | this.algProv = alg + "/" + prov; 521 | 522 | var hashAlg = alg.substr(4); 523 | 524 | // for cryptojs 525 | if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(hashAlg) != -1 && 526 | prov == 'cryptojs') { 527 | try { 528 | var mdObj = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[hashAlg]); 529 | this.mac = CryptoJS.algo.HMAC.create(mdObj, this.pass); 530 | } catch (ex) { 531 | throw "setAlgAndProvider hash alg set fail hashAlg=" + hashAlg + "/" + ex; 532 | } 533 | this.updateString = function(str) { 534 | this.mac.update(str); 535 | }; 536 | this.updateHex = function(hex) { 537 | var wHex = CryptoJS.enc.Hex.parse(hex); 538 | this.mac.update(wHex); 539 | }; 540 | this.doFinal = function() { 541 | var hash = this.mac.finalize(); 542 | return hash.toString(CryptoJS.enc.Hex); 543 | }; 544 | this.doFinalString = function(str) { 545 | this.updateString(str); 546 | return this.doFinal(); 547 | }; 548 | this.doFinalHex = function(hex) { 549 | this.updateHex(hex); 550 | return this.doFinal(); 551 | }; 552 | } 553 | }; 554 | 555 | /** 556 | * update digest by specified string 557 | * @name updateString 558 | * @memberOf KJUR.crypto.Mac 559 | * @function 560 | * @param {String} str string to update 561 | * @description 562 | * @example 563 | * md.updateString('New York'); 564 | */ 565 | this.updateString = function(str) { 566 | throw "updateString(str) not supported for this alg/prov: " + this.algProv; 567 | }; 568 | 569 | /** 570 | * update digest by specified hexadecimal string 571 | * @name updateHex 572 | * @memberOf KJUR.crypto.Mac 573 | * @function 574 | * @param {String} hex hexadecimal string to update 575 | * @description 576 | * @example 577 | * md.updateHex('0afe36'); 578 | */ 579 | this.updateHex = function(hex) { 580 | throw "updateHex(hex) not supported for this alg/prov: " + this.algProv; 581 | }; 582 | 583 | /** 584 | * completes hash calculation and returns hash result 585 | * @name doFinal 586 | * @memberOf KJUR.crypto.Mac 587 | * @function 588 | * @description 589 | * @example 590 | * md.digest() 591 | */ 592 | this.doFinal = function() { 593 | throw "digest() not supported for this alg/prov: " + this.algProv; 594 | }; 595 | 596 | /** 597 | * performs final update on the digest using string, then completes the digest computation 598 | * @name doFinalString 599 | * @memberOf KJUR.crypto.Mac 600 | * @function 601 | * @param {String} str string to final update 602 | * @description 603 | * @example 604 | * md.digestString('aaa') 605 | */ 606 | this.doFinalString = function(str) { 607 | throw "digestString(str) not supported for this alg/prov: " + this.algProv; 608 | }; 609 | 610 | /** 611 | * performs final update on the digest using hexadecimal string, 612 | * then completes the digest computation 613 | * @name doFinalHex 614 | * @memberOf KJUR.crypto.Mac 615 | * @function 616 | * @param {String} hex hexadecimal string to final update 617 | * @description 618 | * @example 619 | * md.digestHex('0f2abd') 620 | */ 621 | this.doFinalHex = function(hex) { 622 | throw "digestHex(hex) not supported for this alg/prov: " + this.algProv; 623 | }; 624 | 625 | if (params !== undefined) { 626 | if (params['pass'] !== undefined) { 627 | this.pass = params['pass']; 628 | } 629 | if (params['alg'] !== undefined) { 630 | this.algName = params['alg']; 631 | if (params['prov'] === undefined) 632 | this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]; 633 | this.setAlgAndProvider(this.algName, this.provName); 634 | } 635 | } 636 | }; 637 | 638 | /** 639 | * Signature class which is very similar to java.security.Signature class 640 | * @name KJUR.crypto.Signature 641 | * @class Signature class which is very similar to java.security.Signature class 642 | * @param {Array} params parameters for constructor 643 | * @property {String} state Current state of this signature object whether 'SIGN', 'VERIFY' or null 644 | * @description 645 | *
646 | * As for params of constructor's argument, it can be specify following attributes: 647 | *
    648 | *
  • alg - signature algorithm name (ex. {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160}with{RSA,ECDSA,DSA})
  • 649 | *
  • provider - currently 'cryptojs/jsrsa' only
  • 650 | *
651 | *

SUPPORTED ALGORITHMS AND PROVIDERS

652 | * This Signature class supports following signature algorithm and provider names: 653 | *
    654 | *
  • MD5withRSA - cryptojs/jsrsa
  • 655 | *
  • SHA1withRSA - cryptojs/jsrsa
  • 656 | *
  • SHA224withRSA - cryptojs/jsrsa
  • 657 | *
  • SHA256withRSA - cryptojs/jsrsa
  • 658 | *
  • SHA384withRSA - cryptojs/jsrsa
  • 659 | *
  • SHA512withRSA - cryptojs/jsrsa
  • 660 | *
  • RIPEMD160withRSA - cryptojs/jsrsa
  • 661 | *
  • MD5withECDSA - cryptojs/jsrsa
  • 662 | *
  • SHA1withECDSA - cryptojs/jsrsa
  • 663 | *
  • SHA224withECDSA - cryptojs/jsrsa
  • 664 | *
  • SHA256withECDSA - cryptojs/jsrsa
  • 665 | *
  • SHA384withECDSA - cryptojs/jsrsa
  • 666 | *
  • SHA512withECDSA - cryptojs/jsrsa
  • 667 | *
  • RIPEMD160withECDSA - cryptojs/jsrsa
  • 668 | *
  • MD5withRSAandMGF1 - cryptojs/jsrsa
  • 669 | *
  • SHA1withRSAandMGF1 - cryptojs/jsrsa
  • 670 | *
  • SHA224withRSAandMGF1 - cryptojs/jsrsa
  • 671 | *
  • SHA256withRSAandMGF1 - cryptojs/jsrsa
  • 672 | *
  • SHA384withRSAandMGF1 - cryptojs/jsrsa
  • 673 | *
  • SHA512withRSAandMGF1 - cryptojs/jsrsa
  • 674 | *
  • RIPEMD160withRSAandMGF1 - cryptojs/jsrsa
  • 675 | *
  • SHA1withDSA - cryptojs/jsrsa
  • 676 | *
  • SHA224withDSA - cryptojs/jsrsa
  • 677 | *
  • SHA256withDSA - cryptojs/jsrsa
  • 678 | *
679 | * Here are supported elliptic cryptographic curve names and their aliases for ECDSA: 680 | *
    681 | *
  • secp256k1
  • 682 | *
  • secp256r1, NIST P-256, P-256, prime256v1
  • 683 | *
  • secp384r1, NIST P-384, P-384
  • 684 | *
685 | * NOTE1: DSA signing algorithm is also supported since crypto 1.1.5. 686 | *

EXAMPLES

687 | * @example 688 | * // RSA signature generation 689 | * var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA"}); 690 | * sig.init(prvKeyPEM); 691 | * sig.updateString('aaa'); 692 | * var hSigVal = sig.sign(); 693 | * 694 | * // DSA signature validation 695 | * var sig2 = new KJUR.crypto.Signature({"alg": "SHA1withDSA"}); 696 | * sig2.init(certPEM); 697 | * sig.updateString('aaa'); 698 | * var isValid = sig2.verify(hSigVal); 699 | * 700 | * // ECDSA signing 701 | * var sig = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'}); 702 | * sig.init(prvKeyPEM); 703 | * sig.updateString('aaa'); 704 | * var sigValueHex = sig.sign(); 705 | * 706 | * // ECDSA verifying 707 | * var sig2 = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'}); 708 | * sig.init(certPEM); 709 | * sig.updateString('aaa'); 710 | * var isValid = sig.verify(sigValueHex); 711 | */ 712 | KJUR.crypto.Signature = function(params) { 713 | var prvKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for signing 714 | var pubKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for verifying 715 | 716 | var md = null; // KJUR.crypto.MessageDigest object 717 | var sig = null; 718 | var algName = null; 719 | var provName = null; 720 | var algProvName = null; 721 | var mdAlgName = null; 722 | var pubkeyAlgName = null; // rsa,ecdsa,rsaandmgf1(=rsapss) 723 | var state = null; 724 | var pssSaltLen = -1; 725 | var initParams = null; 726 | 727 | var sHashHex = null; // hex hash value for hex 728 | var hDigestInfo = null; 729 | var hPaddedDigestInfo = null; 730 | var hSign = null; 731 | 732 | this._setAlgNames = function() { 733 | if (this.algName.match(/^(.+)with(.+)$/)) { 734 | this.mdAlgName = RegExp.$1.toLowerCase(); 735 | this.pubkeyAlgName = RegExp.$2.toLowerCase(); 736 | } 737 | }; 738 | 739 | this._zeroPaddingOfSignature = function(hex, bitLength) { 740 | var s = ""; 741 | var nZero = bitLength / 4 - hex.length; 742 | for (var i = 0; i < nZero; i++) { 743 | s = s + "0"; 744 | } 745 | return s + hex; 746 | }; 747 | 748 | /** 749 | * set signature algorithm and provider 750 | * @name setAlgAndProvider 751 | * @memberOf KJUR.crypto.Signature 752 | * @function 753 | * @param {String} alg signature algorithm name 754 | * @param {String} prov provider name 755 | * @description 756 | * @example 757 | * md.setAlgAndProvider('SHA1withRSA', 'cryptojs/jsrsa'); 758 | */ 759 | this.setAlgAndProvider = function(alg, prov) { 760 | this._setAlgNames(); 761 | if (prov != 'cryptojs/jsrsa') 762 | throw "provider not supported: " + prov; 763 | 764 | if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(this.mdAlgName) != -1) { 765 | try { 766 | this.md = new KJUR.crypto.MessageDigest({'alg':this.mdAlgName}); 767 | } catch (ex) { 768 | throw "setAlgAndProvider hash alg set fail alg=" + 769 | this.mdAlgName + "/" + ex; 770 | } 771 | 772 | this.init = function(keyparam, pass) { 773 | var keyObj = null; 774 | try { 775 | if (pass === undefined) { 776 | keyObj = KEYUTIL.getKey(keyparam); 777 | } else { 778 | keyObj = KEYUTIL.getKey(keyparam, pass); 779 | } 780 | } catch (ex) { 781 | throw "init failed:" + ex; 782 | } 783 | 784 | if (keyObj.isPrivate === true) { 785 | this.prvKey = keyObj; 786 | this.state = "SIGN"; 787 | } else if (keyObj.isPublic === true) { 788 | this.pubKey = keyObj; 789 | this.state = "VERIFY"; 790 | } else { 791 | throw "init failed.:" + keyObj; 792 | } 793 | }; 794 | 795 | this.initSign = function(params) { 796 | if (typeof params['ecprvhex'] == 'string' && 797 | typeof params['eccurvename'] == 'string') { 798 | this.ecprvhex = params['ecprvhex']; 799 | this.eccurvename = params['eccurvename']; 800 | } else { 801 | this.prvKey = params; 802 | } 803 | this.state = "SIGN"; 804 | }; 805 | 806 | this.initVerifyByPublicKey = function(params) { 807 | if (typeof params['ecpubhex'] == 'string' && 808 | typeof params['eccurvename'] == 'string') { 809 | this.ecpubhex = params['ecpubhex']; 810 | this.eccurvename = params['eccurvename']; 811 | } else if (params instanceof KJUR.crypto.ECDSA) { 812 | this.pubKey = params; 813 | } else if (params instanceof RSAKey) { 814 | this.pubKey = params; 815 | } 816 | this.state = "VERIFY"; 817 | }; 818 | 819 | this.initVerifyByCertificatePEM = function(certPEM) { 820 | var x509 = new X509(); 821 | x509.readCertPEM(certPEM); 822 | this.pubKey = x509.subjectPublicKeyRSA; 823 | this.state = "VERIFY"; 824 | }; 825 | 826 | this.updateString = function(str) { 827 | this.md.updateString(str); 828 | }; 829 | this.updateHex = function(hex) { 830 | this.md.updateHex(hex); 831 | }; 832 | 833 | this.sign = function() { 834 | this.sHashHex = this.md.digest(); 835 | if (typeof this.ecprvhex != "undefined" && 836 | typeof this.eccurvename != "undefined") { 837 | var ec = new KJUR.crypto.ECDSA({'curve': this.eccurvename}); 838 | this.hSign = ec.signHex(this.sHashHex, this.ecprvhex); 839 | } else if (this.pubkeyAlgName == "rsaandmgf1") { 840 | this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex, 841 | this.mdAlgName, 842 | this.pssSaltLen); 843 | } else if (this.pubkeyAlgName == "rsa") { 844 | this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, 845 | this.mdAlgName); 846 | } else if (this.prvKey instanceof KJUR.crypto.ECDSA) { 847 | this.hSign = this.prvKey.signWithMessageHash(this.sHashHex); 848 | } else if (this.prvKey instanceof KJUR.crypto.DSA) { 849 | this.hSign = this.prvKey.signWithMessageHash(this.sHashHex); 850 | } else { 851 | throw "Signature: unsupported public key alg: " + this.pubkeyAlgName; 852 | } 853 | return this.hSign; 854 | }; 855 | this.signString = function(str) { 856 | this.updateString(str); 857 | return this.sign(); 858 | }; 859 | this.signHex = function(hex) { 860 | this.updateHex(hex); 861 | return this.sign(); 862 | }; 863 | this.verify = function(hSigVal) { 864 | this.sHashHex = this.md.digest(); 865 | if (typeof this.ecpubhex != "undefined" && 866 | typeof this.eccurvename != "undefined") { 867 | var ec = new KJUR.crypto.ECDSA({curve: this.eccurvename}); 868 | return ec.verifyHex(this.sHashHex, hSigVal, this.ecpubhex); 869 | } else if (this.pubkeyAlgName == "rsaandmgf1") { 870 | return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, hSigVal, 871 | this.mdAlgName, 872 | this.pssSaltLen); 873 | } else if (this.pubkeyAlgName == "rsa") { 874 | return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal); 875 | } else if (this.pubKey instanceof KJUR.crypto.ECDSA) { 876 | return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal); 877 | } else if (this.pubKey instanceof KJUR.crypto.DSA) { 878 | return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal); 879 | } else { 880 | throw "Signature: unsupported public key alg: " + this.pubkeyAlgName; 881 | } 882 | }; 883 | } 884 | }; 885 | 886 | /** 887 | * Initialize this object for signing or verifying depends on key 888 | * @name init 889 | * @memberOf KJUR.crypto.Signature 890 | * @function 891 | * @param {Object} key specifying public or private key as plain/encrypted PKCS#5/8 PEM file, certificate PEM or {@link RSAKey}, {@link KJUR.crypto.DSA} or {@link KJUR.crypto.ECDSA} object 892 | * @param {String} pass (OPTION) passcode for encrypted private key 893 | * @since crypto 1.1.3 894 | * @description 895 | * This method is very useful initialize method for Signature class since 896 | * you just specify key then this method will automatically initialize it 897 | * using {@link KEYUTIL.getKey} method. 898 | * As for 'key', following argument type are supported: 899 | *
signing
900 | *
    901 | *
  • PEM formatted PKCS#8 encrypted RSA/ECDSA private key concluding "BEGIN ENCRYPTED PRIVATE KEY"
  • 902 | *
  • PEM formatted PKCS#5 encrypted RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" and ",ENCRYPTED"
  • 903 | *
  • PEM formatted PKCS#8 plain RSA/ECDSA private key concluding "BEGIN PRIVATE KEY"
  • 904 | *
  • PEM formatted PKCS#5 plain RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" without ",ENCRYPTED"
  • 905 | *
  • RSAKey object of private key
  • 906 | *
  • KJUR.crypto.ECDSA object of private key
  • 907 | *
  • KJUR.crypto.DSA object of private key
  • 908 | *
909 | *
verification
910 | *
    911 | *
  • PEM formatted PKCS#8 RSA/EC/DSA public key concluding "BEGIN PUBLIC KEY"
  • 912 | *
  • PEM formatted X.509 certificate with RSA/EC/DSA public key concluding 913 | * "BEGIN CERTIFICATE", "BEGIN X509 CERTIFICATE" or "BEGIN TRUSTED CERTIFICATE".
  • 914 | *
  • RSAKey object of public key
  • 915 | *
  • KJUR.crypto.ECDSA object of public key
  • 916 | *
  • KJUR.crypto.DSA object of public key
  • 917 | *
918 | * @example 919 | * sig.init(sCertPEM) 920 | */ 921 | this.init = function(key, pass) { 922 | throw "init(key, pass) not supported for this alg:prov=" + 923 | this.algProvName; 924 | }; 925 | 926 | /** 927 | * Initialize this object for verifying with a public key 928 | * @name initVerifyByPublicKey 929 | * @memberOf KJUR.crypto.Signature 930 | * @function 931 | * @param {Object} param RSAKey object of public key or associative array for ECDSA 932 | * @since 1.0.2 933 | * @deprecated from crypto 1.1.5. please use init() method instead. 934 | * @description 935 | * Public key information will be provided as 'param' parameter and the value will be 936 | * following: 937 | *
    938 | *
  • {@link RSAKey} object for RSA verification
  • 939 | *
  • associative array for ECDSA verification 940 | * (ex. {'ecpubhex': '041f..', 'eccurvename': 'secp256r1'}) 941 | *
  • 942 | *
943 | * @example 944 | * sig.initVerifyByPublicKey(rsaPrvKey) 945 | */ 946 | this.initVerifyByPublicKey = function(rsaPubKey) { 947 | throw "initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov=" + 948 | this.algProvName; 949 | }; 950 | 951 | /** 952 | * Initialize this object for verifying with a certficate 953 | * @name initVerifyByCertificatePEM 954 | * @memberOf KJUR.crypto.Signature 955 | * @function 956 | * @param {String} certPEM PEM formatted string of certificate 957 | * @since 1.0.2 958 | * @deprecated from crypto 1.1.5. please use init() method instead. 959 | * @description 960 | * @example 961 | * sig.initVerifyByCertificatePEM(certPEM) 962 | */ 963 | this.initVerifyByCertificatePEM = function(certPEM) { 964 | throw "initVerifyByCertificatePEM(certPEM) not supported for this alg:prov=" + 965 | this.algProvName; 966 | }; 967 | 968 | /** 969 | * Initialize this object for signing 970 | * @name initSign 971 | * @memberOf KJUR.crypto.Signature 972 | * @function 973 | * @param {Object} param RSAKey object of public key or associative array for ECDSA 974 | * @deprecated from crypto 1.1.5. please use init() method instead. 975 | * @description 976 | * Private key information will be provided as 'param' parameter and the value will be 977 | * following: 978 | *
    979 | *
  • {@link RSAKey} object for RSA signing
  • 980 | *
  • associative array for ECDSA signing 981 | * (ex. {'ecprvhex': '1d3f..', 'eccurvename': 'secp256r1'})
  • 982 | *
983 | * @example 984 | * sig.initSign(prvKey) 985 | */ 986 | this.initSign = function(prvKey) { 987 | throw "initSign(prvKey) not supported for this alg:prov=" + this.algProvName; 988 | }; 989 | 990 | /** 991 | * Updates the data to be signed or verified by a string 992 | * @name updateString 993 | * @memberOf KJUR.crypto.Signature 994 | * @function 995 | * @param {String} str string to use for the update 996 | * @description 997 | * @example 998 | * sig.updateString('aaa') 999 | */ 1000 | this.updateString = function(str) { 1001 | throw "updateString(str) not supported for this alg:prov=" + this.algProvName; 1002 | }; 1003 | 1004 | /** 1005 | * Updates the data to be signed or verified by a hexadecimal string 1006 | * @name updateHex 1007 | * @memberOf KJUR.crypto.Signature 1008 | * @function 1009 | * @param {String} hex hexadecimal string to use for the update 1010 | * @description 1011 | * @example 1012 | * sig.updateHex('1f2f3f') 1013 | */ 1014 | this.updateHex = function(hex) { 1015 | throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName; 1016 | }; 1017 | 1018 | /** 1019 | * Returns the signature bytes of all data updates as a hexadecimal string 1020 | * @name sign 1021 | * @memberOf KJUR.crypto.Signature 1022 | * @function 1023 | * @return the signature bytes as a hexadecimal string 1024 | * @description 1025 | * @example 1026 | * var hSigValue = sig.sign() 1027 | */ 1028 | this.sign = function() { 1029 | throw "sign() not supported for this alg:prov=" + this.algProvName; 1030 | }; 1031 | 1032 | /** 1033 | * performs final update on the sign using string, then returns the signature bytes of all data updates as a hexadecimal string 1034 | * @name signString 1035 | * @memberOf KJUR.crypto.Signature 1036 | * @function 1037 | * @param {String} str string to final update 1038 | * @return the signature bytes of a hexadecimal string 1039 | * @description 1040 | * @example 1041 | * var hSigValue = sig.signString('aaa') 1042 | */ 1043 | this.signString = function(str) { 1044 | throw "digestString(str) not supported for this alg:prov=" + this.algProvName; 1045 | }; 1046 | 1047 | /** 1048 | * performs final update on the sign using hexadecimal string, then returns the signature bytes of all data updates as a hexadecimal string 1049 | * @name signHex 1050 | * @memberOf KJUR.crypto.Signature 1051 | * @function 1052 | * @param {String} hex hexadecimal string to final update 1053 | * @return the signature bytes of a hexadecimal string 1054 | * @description 1055 | * @example 1056 | * var hSigValue = sig.signHex('1fdc33') 1057 | */ 1058 | this.signHex = function(hex) { 1059 | throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName; 1060 | }; 1061 | 1062 | /** 1063 | * verifies the passed-in signature. 1064 | * @name verify 1065 | * @memberOf KJUR.crypto.Signature 1066 | * @function 1067 | * @param {String} str string to final update 1068 | * @return {Boolean} true if the signature was verified, otherwise false 1069 | * @description 1070 | * @example 1071 | * var isValid = sig.verify('1fbcefdca4823a7(snip)') 1072 | */ 1073 | this.verify = function(hSigVal) { 1074 | throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName; 1075 | }; 1076 | 1077 | this.initParams = params; 1078 | 1079 | if (params !== undefined) { 1080 | if (params['alg'] !== undefined) { 1081 | this.algName = params['alg']; 1082 | if (params['prov'] === undefined) { 1083 | this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]; 1084 | } else { 1085 | this.provName = params['prov']; 1086 | } 1087 | this.algProvName = this.algName + ":" + this.provName; 1088 | this.setAlgAndProvider(this.algName, this.provName); 1089 | this._setAlgNames(); 1090 | } 1091 | 1092 | if (params['psssaltlen'] !== undefined) this.pssSaltLen = params['psssaltlen']; 1093 | 1094 | if (params['prvkeypem'] !== undefined) { 1095 | if (params['prvkeypas'] !== undefined) { 1096 | throw "both prvkeypem and prvkeypas parameters not supported"; 1097 | } else { 1098 | try { 1099 | var prvKey = new RSAKey(); 1100 | prvKey.readPrivateKeyFromPEMString(params['prvkeypem']); 1101 | this.initSign(prvKey); 1102 | } catch (ex) { 1103 | throw "fatal error to load pem private key: " + ex; 1104 | } 1105 | } 1106 | } 1107 | } 1108 | }; 1109 | 1110 | /** 1111 | * static object for cryptographic function utilities 1112 | * @name KJUR.crypto.OID 1113 | * @class static object for cryptography related OIDs 1114 | * @property {Array} oidhex2name key value of hexadecimal OID and its name 1115 | * (ex. '2a8648ce3d030107' and 'secp256r1') 1116 | * @since crypto 1.1.3 1117 | * @description 1118 | */ 1119 | 1120 | 1121 | KJUR.crypto.OID = new function() { 1122 | this.oidhex2name = { 1123 | '2a864886f70d010101': 'rsaEncryption', 1124 | '2a8648ce3d0201': 'ecPublicKey', 1125 | '2a8648ce380401': 'dsa', 1126 | '2a8648ce3d030107': 'secp256r1', 1127 | '2b8104001f': 'secp192k1', 1128 | '2b81040021': 'secp224r1', 1129 | '2b8104000a': 'secp256k1', 1130 | '2b81040023': 'secp521r1', 1131 | '2b81040022': 'secp384r1', 1132 | '2a8648ce380403': 'SHA1withDSA', // 1.2.840.10040.4.3 1133 | '608648016503040301': 'SHA224withDSA', // 2.16.840.1.101.3.4.3.1 1134 | '608648016503040302': 'SHA256withDSA', // 2.16.840.1.101.3.4.3.2 1135 | }; 1136 | }; 1137 | --------------------------------------------------------------------------------