diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f602998 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ + + + +TWU_RSA_LIBS = lib/twu-rsa/base64.js lib/twu-rsa/jsbn.js lib/twu-rsa/jsbn2.js lib/twu-rsa/rsa.js lib/twu-rsa/rsa2.js +SANZENREITER_HASH_LIBS = lib/sanzenreiter-hash/sha1.js lib/sanzenreiter-hash/sha256.js +KURUSHIMA_JSRSA_LIBS = lib/kurushima-jsrsa/asn1hex.js lib/kurushima-jsrsa/rsa-pem.js lib/kurushima-jsrsa/rsa-sign.js lib/kurushima-jsrsa/x509.js + +LIBS = $(TWU_RSA_LIBS) $(SANZENREITER_HASH_LIBS) $(KURUSHIMA_JSRSA_LIBS) + +dist: + mkdir -p build + cat $(LIBS) src/jwt-token.js > build/jwt.js \ No newline at end of file diff --git a/lib/kurushima-jsrsa/asn1hex.js b/lib/kurushima-jsrsa/asn1hex.js new file mode 100644 index 0000000..15987bc --- /dev/null +++ b/lib/kurushima-jsrsa/asn1hex.js @@ -0,0 +1,106 @@ +// +// asn1hex.js - Hexadecimal represented ASN.1 string library +// +// +// version: 1.0 (2010-Jun-03) +// +// Copyright (c) 2010 Kenji Urushima (kenji.urushima@gmail.com) +// +// This software is licensed under the terms of the MIT License. +// http://www.opensource.org/licenses/mit-license.php +// +// The above copyright and license notice shall be +// included in all copies or substantial portions of the Software. +// +// +// Depends on: +// + +// MEMO: +// f('3082025b02...', 2) ... 82025b ... 3bytes +// f('020100', 2) ... 01 ... 1byte +// f('0203001...', 2) ... 03 ... 1byte +// f('02818003...', 2) ... 8180 ... 2bytes +// f('3080....0000', 2) ... 80 ... -1 +// +// Requirements: +// - ASN.1 type octet length MUST be 1. +// (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...) +// - +function _asnhex_getByteLengthOfL_AtObj(s, pos) { + if (s.substring(pos + 2, pos + 3) != '8') return 1; + var i = parseInt(s.substring(pos + 3, pos + 4)); + if (i == 0) return -1; // length octet '80' indefinite length + if (0 < i && i < 10) return i + 1; // including '8?' octet; + return -2; // malformed format +} + +function _asnhex_getHexOfL_AtObj(s, pos) { + var len = _asnhex_getByteLengthOfL_AtObj(s, pos); + if (len < 1) return ''; + return s.substring(pos + 2, pos + 2 + len * 2); +} + +// +// getting ASN.1 length value at the position 'idx' of +// hexa decimal string 's'. +// +// f('3082025b02...', 0) ... 82025b ... ??? +// f('020100', 0) ... 01 ... 1 +// f('0203001...', 0) ... 03 ... 3 +// f('02818003...', 0) ... 8180 ... 128 +function _asnhex_getIntOfL_AtObj(s, pos) { + var hLength = _asnhex_getHexOfL_AtObj(s, pos); + if (hLength == '') return -1; + var bi; + if (parseInt(hLength.substring(0, 1)) < 8) { + bi = parseBigInt(hLength, 16); + } else { + bi = parseBigInt(hLength.substring(2), 16); + } + return bi.intValue(); +} + +// +// get ASN.1 value starting string position +// for ASN.1 object refered by index 'idx'. +// +function _asnhex_getStartPosOfV_AtObj(s, pos) { + var l_len = _asnhex_getByteLengthOfL_AtObj(s, pos); + if (l_len < 0) return l_len; + return pos + (l_len + 1) * 2; +} + +function _asnhex_getHexOfV_AtObj(s, pos) { + var pos1 = _asnhex_getStartPosOfV_AtObj(s, pos); + var len = _asnhex_getIntOfL_AtObj(s, pos); + return s.substring(pos1, pos1 + len * 2); +} + +function _asnhex_getPosOfNextSibling_AtObj(s, pos) { + var pos1 = _asnhex_getStartPosOfV_AtObj(s, pos); + var len = _asnhex_getIntOfL_AtObj(s, pos); + return pos1 + len * 2; +} + +function _asnhex_getPosArrayOfChildren_AtObj(h, pos) { + var a = new Array(); + var p0 = _asnhex_getStartPosOfV_AtObj(h, pos); + a.push(p0); + + var len = _asnhex_getIntOfL_AtObj(h, pos); + var p = p0; + var k = 0; + while (1) { + var pNext = _asnhex_getPosOfNextSibling_AtObj(h, p); + if (pNext == null || (pNext - p0 >= (len * 2))) break; + if (k >= 200) break; + + a.push(pNext); + p = pNext; + + k++; + } + + return a; +} diff --git a/lib/kurushima-jsrsa/rsa-pem.js b/lib/kurushima-jsrsa/rsa-pem.js new file mode 100644 index 0000000..8236068 --- /dev/null +++ b/lib/kurushima-jsrsa/rsa-pem.js @@ -0,0 +1,71 @@ +// +// rsa-pem.js - adding function for reading/writing PKCS#1 PEM private key +// to RSAKey class. +// +// version: 1.0 (2010-Jun-03) +// +// Copyright (c) 2010 Kenji Urushima (kenji.urushima@gmail.com) +// +// This software is licensed under the terms of the MIT License. +// http://www.opensource.org/licenses/mit-license.php +// +// The above copyright and license notice shall be +// included in all copies or substantial portions of the Software. +// +// +// Depends on: +// +// +// +// _RSApem_pemToBase64(sPEM) +// +// removing PEM header, PEM footer and space characters including +// new lines from PEM formatted RSA private key string. +// +function _rsapem_pemToBase64(sPEMPrivateKey) { + var s = sPEMPrivateKey; + s = s.replace("-----BEGIN RSA PRIVATE KEY-----", ""); + s = s.replace("-----END RSA PRIVATE KEY-----", ""); + s = s.replace(/[ \n]+/g, ""); + return s; +} + +function _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey) { + var a = new Array(); + var v1 = _asnhex_getStartPosOfV_AtObj(hPrivateKey, 0); + var n1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, v1); + var e1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, n1); + var d1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, e1); + var p1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, d1); + var q1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, p1); + var dp1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, q1); + var dq1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, dp1); + var co1 = _asnhex_getPosOfNextSibling_AtObj(hPrivateKey, dq1); + a.push(v1, n1, e1, d1, p1, q1, dp1, dq1, co1); + return a; +} + +function _rsapem_getHexValueArrayOfChildrenFromHex(hPrivateKey) { + var posArray = _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey); + var v = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[0]); + var n = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[1]); + var e = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[2]); + var d = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[3]); + var p = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[4]); + var q = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[5]); + var dp = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[6]); + var dq = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[7]); + var co = _asnhex_getHexOfV_AtObj(hPrivateKey, posArray[8]); + var a = new Array(); + a.push(v, n, e, d, p, q, dp, dq, co); + return a; +} + +function _rsapem_readPrivateKeyFromPEMString(keyPEM) { + var keyB64 = _rsapem_pemToBase64(keyPEM); + var keyHex = b64tohex(keyB64) // depends base64.js + var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex); + this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]); +} + +RSAKey.prototype.readPrivateKeyFromPEMString = _rsapem_readPrivateKeyFromPEMString; diff --git a/lib/kurushima-jsrsa/rsa-sign.js b/lib/kurushima-jsrsa/rsa-sign.js new file mode 100644 index 0000000..62de97d --- /dev/null +++ b/lib/kurushima-jsrsa/rsa-sign.js @@ -0,0 +1,155 @@ +// +// rsa-sign.js - adding signing functions to RSAKey class. +// +// +// version: 1.0 (2010-Jun-03) +// +// Copyright (c) 2010 Kenji Urushima (kenji.urushima@gmail.com) +// +// This software is licensed under the terms of the MIT License. +// http://www.opensource.org/licenses/mit-license.php +// +// The above copyright and license notice shall be +// included in all copies or substantial portions of the Software. + +// +// Depends on: +// function sha1.hex(s) of sha1.js +// jsbn.js +// jsbn2.js +// rsa.js +// rsa2.js +// + +// keysize / pmstrlen +// 512 / 128 +// 1024 / 256 +// 2048 / 512 +// 4096 / 1024 + +// As for _RSASGIN_DIHEAD values for each hash algorithm, see PKCS#1 v2.1 spec (p38). +var _RSASIGN_DIHEAD = []; +_RSASIGN_DIHEAD['sha1'] = "3021300906052b0e03021a05000414"; +_RSASIGN_DIHEAD['sha256'] = "3031300d060960864801650304020105000420"; +//_RSASIGN_DIHEAD['md2'] = "3020300c06082a864886f70d020205000410"; +//_RSASIGN_DIHEAD['md5'] = "3020300c06082a864886f70d020505000410"; +//_RSASIGN_DIHEAD['sha384'] = "3041300d060960864801650304020205000430"; +//_RSASIGN_DIHEAD['sha512'] = "3051300d060960864801650304020305000440"; +var _RSASIGN_HASHHEXFUNC = []; +_RSASIGN_HASHHEXFUNC['sha1'] = sha1.hex; +_RSASIGN_HASHHEXFUNC['sha256'] = sha256.hex; + +// ======================================================================== +// Signature Generation +// ======================================================================== + +function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) { + var pmStrLen = keySize / 4; + var hashFunc = _RSASIGN_HASHHEXFUNC[hashAlg]; + var sHashHex = hashFunc(s); + + var sHead = "0001"; + var sTail = "00" + _RSASIGN_DIHEAD[hashAlg] + sHashHex; + var sMid = ""; + var fLen = pmStrLen - sHead.length - sTail.length; + for (var i = 0; i < fLen; i += 2) { + sMid += "ff"; + } + sPaddedMessageHex = sHead + sMid + sTail; + return sPaddedMessageHex; +} + +function _rsasign_signString(s, hashAlg) { + var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), hashAlg); + var biPaddedMessage = parseBigInt(hPM, 16); + var biSign = this.doPrivate(biPaddedMessage); + var hexSign = biSign.toString(16); + return hexSign; +} + +function _rsasign_signStringWithSHA1(s) { + var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), 'sha1'); + var biPaddedMessage = parseBigInt(hPM, 16); + var biSign = this.doPrivate(biPaddedMessage); + var hexSign = biSign.toString(16); + return hexSign; +} + +function _rsasign_signStringWithSHA256(s) { + var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), 'sha256'); + var biPaddedMessage = parseBigInt(hPM, 16); + var biSign = this.doPrivate(biPaddedMessage); + var hexSign = biSign.toString(16); + return hexSign; +} + +// ======================================================================== +// Signature Verification +// ======================================================================== + +function _rsasign_getDecryptSignatureBI(biSig, hN, hE) { + var rsa = new RSAKey(); + rsa.setPublic(hN, hE); + var biDecryptedSig = rsa.doPublic(biSig); + return biDecryptedSig; +} + +function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) { + var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE); + var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, ''); + return hDigestInfo; +} + +function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) { + for (var algName in _RSASIGN_DIHEAD) { + var head = _RSASIGN_DIHEAD[algName]; + var len = head.length; + if (hDigestInfo.substring(0, len) == head) { + var a = [algName, hDigestInfo.substring(len)]; + return a; + } + } + return []; +} + +function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) { + var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE); + var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo); + if (digestInfoAry.length == 0) return false; + var algName = digestInfoAry[0]; + var diHashValue = digestInfoAry[1]; + var ff = _RSASIGN_HASHHEXFUNC[algName]; + var msgHashValue = ff(sMsg); + return (diHashValue == msgHashValue); +} + +function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) { + var biSig = parseBigInt(hSig, 16); + var result = _rsasign_verifySignatureWithArgs(sMsg, biSig, + this.n.toString(16), + this.e.toString(16)); + return result; +} + +function _rsasign_verifyString(sMsg, hSig) { + hSig = hSig.replace(/[ \n]+/g, ""); + var biSig = parseBigInt(hSig, 16); + var biDecryptedSig = this.doPublic(biSig); + var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, ''); + var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo); + + if (digestInfoAry.length == 0) return false; + var algName = digestInfoAry[0]; + var diHashValue = digestInfoAry[1]; + var ff = _RSASIGN_HASHHEXFUNC[algName]; + var msgHashValue = ff(sMsg); + return (diHashValue == msgHashValue); +} + +RSAKey.prototype.signString = _rsasign_signString; +RSAKey.prototype.signStringWithSHA1 = _rsasign_signStringWithSHA1; +RSAKey.prototype.signStringWithSHA256 = _rsasign_signStringWithSHA256; + +RSAKey.prototype.verifyString = _rsasign_verifyString; +RSAKey.prototype.verifyHexSignatureForMessage = _rsasign_verifyHexSignatureForMessage; + diff --git a/lib/kurushima-jsrsa/x509.js b/lib/kurushima-jsrsa/x509.js new file mode 100644 index 0000000..3356f9e --- /dev/null +++ b/lib/kurushima-jsrsa/x509.js @@ -0,0 +1,111 @@ +// +// x509.js - X509 class to read subject public key from certificate. +// +// version: 1.0 (2010-Jun-03) +// +// Copyright (c) 2010 Kenji Urushima (kenji.urushima@gmail.com) +// +// This software is licensed under the terms of the MIT License. +// http://www.opensource.org/licenses/mit-license.php +// +// The above copyright and license notice shall be +// included in all copies or substantial portions of the Software. +// + +// Depends: +// base64.js +// rsa.js + +function _x509_pemToBase64(sCertPEM) { + var s = sCertPEM; + s = s.replace("-----BEGIN CERTIFICATE-----", ""); + s = s.replace("-----END CERTIFICATE-----", ""); + s = s.replace(/[ \n]+/g, ""); + return s; +} + +function _x509_pemToHex(sCertPEM) { + var b64Cert = _x509_pemToBase64(sCertPEM); + var hCert = b64tohex(b64Cert); + return hCert; +} + +function _x509_getHexTbsCertificateFromCert(hCert) { + var pTbsCert = _asnhex_getStartPosOfV_AtObj(hCert, 0); + return pTbsCert; +} + +// NOTE: privateKeyUsagePeriod field of X509v2 not supported. +// NOTE: v1 and v3 supported +function _x509_getSubjectPublicKeyInfoPosFromCertHex(hCert) { + var pTbsCert = _asnhex_getStartPosOfV_AtObj(hCert, 0); + var a = _asnhex_getPosArrayOfChildren_AtObj(hCert, pTbsCert); + if (a.length < 1) return -1; + if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3 + if (a.length < 6) return -1; + return a[6]; + } else { + if (a.length < 5) return -1; + return a[5]; + } +} + +// NOTE: Without BITSTRING encapsulation. +function _x509_getSubjectPublicKeyPosFromCertHex(hCert) { + var pInfo = _x509_getSubjectPublicKeyInfoPosFromCertHex(hCert); + if (pInfo == -1) return -1; + var a = _asnhex_getPosArrayOfChildren_AtObj(hCert, pInfo); + if (a.length != 2) return -1; + var pBitString = a[1]; + if (hCert.substring(pBitString, pBitString + 2) != '03') return -1; + var pBitStringV = _asnhex_getStartPosOfV_AtObj(hCert, pBitString); + + if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1; + return pBitStringV + 2; +} + +function _x509_getPublicKeyHexArrayFromCertHex(hCert) { + var p = _x509_getSubjectPublicKeyPosFromCertHex(hCert); + var a = _asnhex_getPosArrayOfChildren_AtObj(hCert, p); + if (a.length != 2) return []; + var hN = _asnhex_getHexOfV_AtObj(hCert, a[0]); + var hE = _asnhex_getHexOfV_AtObj(hCert, a[1]); + if (hN != null && hE != null) { + return [hN, hE]; + } else { + return []; + } +} + +function _x509_getPublicKeyHexArrayFromCertPEM(sCertPEM) { + var hCert = _x509_pemToHex(sCertPEM); + var a = _x509_getPublicKeyHexArrayFromCertHex(hCert); + return a; +} + +function _x509_readCertPEM(sCertPEM) { + var hCert = _x509_pemToHex(sCertPEM); + var a = _x509_getPublicKeyHexArrayFromCertHex(hCert); + var rsa = new RSAKey(); + rsa.setPublic(a[0], a[1]); + this.subjectPublicKeyRSA = rsa; + this.subjectPublicKeyRSA_hN = a[0]; + this.subjectPublicKeyRSA_hE = a[1]; +} + +function _x509_readCertPEMWithoutRSAInit(sCertPEM) { + var hCert = _x509_pemToHex(sCertPEM); + var a = _x509_getPublicKeyHexArrayFromCertHex(hCert); + this.subjectPublicKeyRSA.setPublic(a[0], a[1]); + this.subjectPublicKeyRSA_hN = a[0]; + this.subjectPublicKeyRSA_hE = a[1]; +} + +function X509() { + this.subjectPublicKeyRSA = null; + this.subjectPublicKeyRSA_hN = null; + this.subjectPublicKeyRSA_hE = null; +} + +X509.prototype.readCertPEM = _x509_readCertPEM; +X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit; diff --git a/lib/sanzenreiter-hash/sha1.js b/lib/sanzenreiter-hash/sha1.js new file mode 100644 index 0000000..11ff8bc --- /dev/null +++ b/lib/sanzenreiter-hash/sha1.js @@ -0,0 +1,191 @@ +/* /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ + charset= shift_jis + + [RFC 3174] US Secure Hash Algorithm 1 (SHA1) + ftp://ftp.isi.edu/in-notes/rfc3174.txt + + LastModified : 2006-11/14 + + Written by kerry + http://user1.matsumoto.ne.jp/~goma/ + + “®ìƒuƒ‰ƒEƒU :: IE4+ , NN4.06+ , Gecko , Opera6 + + ---------------------------------------------------------------- + + Usage + + // •Ô‚è’l‚ð 16i”‚Å“¾‚é + sha1hash = sha1.hex( data ); + + // •Ô‚è’l‚ðƒoƒCƒiƒŠ‚Å“¾‚é + sha1bin = sha1.bin( data ); + + // •Ô‚è’l‚ð10i”‚Ì”z—ñ‚Å“¾‚é + sha1decs = sha1.dec( data ); + + + * data -> ƒnƒbƒVƒ…’l‚𓾂½‚¢ƒf[ƒ^ + data ‚̓Aƒ“ƒpƒbƒNÏ‚Ý‚Ì”z—ñ‚Å‚à‰Â”\ + + // e.g. + + var data_1 = "abc"; + var hash_1 = sha1.hex( data_1 ); + var data_2 = sha1 Array(data_1.charCodeAt(0), data_1.charCodeAt(1), data_1.charCodeAt(2)); + var hash_2 = sha1.hex( data_2 ); + + alert( hash_1 === hash_2 ); // true + +/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */ + + +sha1 = new function() +{ + var blockLen = 64; + var state = [ 0x67452301 , 0xefcdab89 , 0x98badcfe , 0x10325476 , 0xc3d2e1f0 ]; + var sttLen = state.length; + + this.hex = function(_data) + { + return toHex( getMD(_data) ); + } + + this.dec = function(_data) + { + return getMD(_data); + } + + this.bin = function(_data) + { + return pack( getMD(_data) ); + } + + var getMD = function(_data) + { + var datz = []; + if (isAry(_data)) datz = _data; + else if (isStr(_data)) datz = unpack(_data); + else "unknown type"; + datz = paddingData(datz); + return round(datz); + } + + var isAry = function(_ary) + { + return _ary && _ary.constructor === [].constructor; + } + var isStr = function(_str) + { + return typeof(_str) == typeof("string"); + } + + var rotl = function(_v, _s) { return (_v << _s) | (_v >>> (32 - _s)) }; + + var round = function(_blk) + { + var stt = []; + var tmpS= []; + var i, j, tmp, x = []; + for (j=0; j0xf?"":"0")+ _decz[i].toString(16); + return hex; + } + + var fromBigEndian32 = function(_blk) + { + var tmp = []; + for (n=i=0; i<_blk.length; i++) + { + tmp[n++] = (_blk[i] >>> 24) & 0xff; + tmp[n++] = (_blk[i] >>> 16) & 0xff; + tmp[n++] = (_blk[i] >>> 8) & 0xff; + tmp[n++] = _blk[i] & 0xff; + } + return tmp; + } + + var toBigEndian32 = function(_blk) + { + var tmp = []; + var i, n; + for (n=i=0; i<_blk.length; i+=4, n++) + tmp[n] = (_blk[i]<<24) | (_blk[i+ 1]<<16) | (_blk[i+ 2]<<8) | _blk[i+ 3]; + return tmp; + } + + var unpack = function(_dat) + { + var i, n, c, tmp = []; + + for (n=i=0; i<_dat.length; i++) + { + c = _dat.charCodeAt(i); + if (c <= 0xff) tmp[n++] = c; + else { + tmp[n++] = c >>> 8; + tmp[n++] = c & 0xff; + } + } + return tmp; + } + + var pack = function(_ary) + { + var i, tmp = ""; + for (i in _ary) tmp += String.fromCharCode(_ary[i]); + return tmp; + } + + var K = [ 0x5a827999 , 0x6ed9eba1 , 0x8f1bbcdc , 0xca62c1d6 ]; + +} + + diff --git a/lib/sanzenreiter-hash/sha256.js b/lib/sanzenreiter-hash/sha256.js new file mode 100644 index 0000000..f87ec2f --- /dev/null +++ b/lib/sanzenreiter-hash/sha256.js @@ -0,0 +1,217 @@ +/* /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ + charset= shift_jis + + SHA-256 + FIPS 180-2 + http://csrc.nist.gov/cryptval/shs.html + + LastModified : 2006-11/14 + + Written by kerry + http://user1.matsumoto.ne.jp/~goma/ + + “®ìƒuƒ‰ƒEƒU :: IE4+ , NN4.06+ , Gecko , Opera6 + + ---------------------------------------------------------------- + + Usage + + // •Ô‚è’l‚ð 16i”‚Å“¾‚é + sha256hash = sha256.hex( data ); + + // •Ô‚è’l‚ðƒoƒCƒiƒŠ‚Å“¾‚é + sha256bin = sha256.bin( data ); + + // •Ô‚è’l‚ð10i”‚Ì”z—ñ‚Å“¾‚é + sha256decs = sha256.dec( data ); + + + * data -> ƒnƒbƒVƒ…’l‚𓾂½‚¢ƒf[ƒ^ + data ‚̓Aƒ“ƒpƒbƒNÏ‚Ý‚Ì”z—ñ‚Å‚à‰Â”\ + + // e.g. + + var data_1 = "abc"; + var hash_1 = sha256.hex( data_1 ); + var data_2 = sha256 Array(data_1.charCodeAt(0), data_1.charCodeAt(1), data_1.charCodeAt(2)); + var hash_2 = sha256.hex( data_2 ); + + alert( hash_1 === hash_2 ); // true + +/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */ + + +sha256 = new function() +{ + var blockLen = 64; + var state = [ 0x6a09e667 , 0xbb67ae85 , 0x3c6ef372 , 0xa54ff53a , + 0x510e527f , 0x9b05688c , 0x1f83d9ab , 0x5be0cd19 ]; + var sttLen = state.length; + + this.hex = function(_data) + { + return toHex( getMD(_data) ); + } + + this.dec = function(_data) + { + return getMD(_data); + } + + this.bin = function(_data) + { + return pack( getMD(_data) ); + } + + var getMD = function(_data) + { + var datz = []; + if (isAry(_data)) datz = _data; + else if (isStr(_data)) datz = unpack(_data); + else "unknown type"; + datz = paddingData(datz); + return round(datz); + } + + var isAry = function(_ary) + { + return _ary && _ary.constructor === [].constructor; + } + var isStr = function(_str) + { + return typeof(_str) == typeof("string"); + } + + var rotr = function(_v, _s) { return (_v >>> _s) | (_v << (32 - _s)) }; + + var S0 = function(_v) { return rotr(_v, 2) ^ rotr(_v, 13) ^ rotr(_v, 22) }; + var S1 = function(_v) { return rotr(_v, 6) ^ rotr(_v, 11) ^ rotr(_v, 25) }; + var s0 = function(_v) { return rotr(_v, 7) ^ rotr(_v, 18) ^ (_v >>> 3) }; + var s1 = function(_v) { return rotr(_v, 17) ^ rotr(_v, 19) ^ (_v >>> 10) }; + + var ch = function(_b, _c, _d) { return (_b & _c) ^ (~_b & _d) }; + var maj = function(_b, _c, _d) { return (_b & _c) ^ (_b & _d) ^ (_c & _d) }; + + var round = function(_blk) + { + var stt = []; + var tmpS= []; + var i, j, tmp1, tmp2, x = []; + for (j=0; j0xf?"":"0")+ _decz[i].toString(16); + return hex; + } + + var fromBigEndian32 = function(_blk) + { + var tmp = []; + for (n=i=0; i<_blk.length; i++) + { + tmp[n++] = (_blk[i] >>> 24) & 0xff; + tmp[n++] = (_blk[i] >>> 16) & 0xff; + tmp[n++] = (_blk[i] >>> 8) & 0xff; + tmp[n++] = _blk[i] & 0xff; + } + return tmp; + } + + var toBigEndian32 = function(_blk) + { + var tmp = []; + var i, n; + for (n=i=0; i<_blk.length; i+=4, n++) + tmp[n] = (_blk[i]<<24) | (_blk[i+ 1]<<16) | (_blk[i+ 2]<<8) | _blk[i+ 3]; + return tmp; + } + + var unpack = function(_dat) + { + var i, n, c, tmp = []; + + for (n=i=0; i<_dat.length; i++) + { + c = _dat.charCodeAt(i); + if (c <= 0xff) tmp[n++] = c; + else { + tmp[n++] = c >>> 8; + tmp[n++] = c & 0xff; + } + } + return tmp; + } + + var pack = function(_ary) + { + var i, tmp = ""; + for (i in _ary) tmp += String.fromCharCode(_ary[i]); + return tmp; + } + + + var K = [ + 0x428a2f98 , 0x71374491 , 0xb5c0fbcf , 0xe9b5dba5 , + 0x3956c25b , 0x59f111f1 , 0x923f82a4 , 0xab1c5ed5 , + 0xd807aa98 , 0x12835b01 , 0x243185be , 0x550c7dc3 , + 0x72be5d74 , 0x80deb1fe , 0x9bdc06a7 , 0xc19bf174 , + + 0xe49b69c1 , 0xefbe4786 , 0x0fc19dc6 , 0x240ca1cc , + 0x2de92c6f , 0x4a7484aa , 0x5cb0a9dc , 0x76f988da , + 0x983e5152 , 0xa831c66d , 0xb00327c8 , 0xbf597fc7 , + 0xc6e00bf3 , 0xd5a79147 , 0x06ca6351 , 0x14292967 , + + 0x27b70a85 , 0x2e1b2138 , 0x4d2c6dfc , 0x53380d13 , + 0x650a7354 , 0x766a0abb , 0x81c2c92e , 0x92722c85 , + 0xa2bfe8a1 , 0xa81a664b , 0xc24b8b70 , 0xc76c51a3 , + 0xd192e819 , 0xd6990624 , 0xf40e3585 , 0x106aa070 , + + 0x19a4c116 , 0x1e376c08 , 0x2748774c , 0x34b0bcb5 , + 0x391c0cb3 , 0x4ed8aa4a , 0x5b9cca4f , 0x682e6ff3 , + 0x748f82ee , 0x78a5636f , 0x84c87814 , 0x8cc70208 , + 0x90befffa , 0xa4506ceb , 0xbef9a3f7 , 0xc67178f2 + ]; +} + + diff --git a/lib/twu-rsa/base64.js b/lib/twu-rsa/base64.js new file mode 100644 index 0000000..f5b168c --- /dev/null +++ b/lib/twu-rsa/base64.js @@ -0,0 +1,71 @@ +var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +var b64pad="="; + +function hex2b64(h) { + var i; + var c; + var ret = ""; + for(i = 0; i+3 <= h.length; i+=3) { + c = parseInt(h.substring(i,i+3),16); + ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63); + } + if(i+1 == h.length) { + c = parseInt(h.substring(i,i+1),16); + ret += b64map.charAt(c << 2); + } + else if(i+2 == h.length) { + c = parseInt(h.substring(i,i+2),16); + ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4); + } + while((ret.length & 3) > 0) ret += b64pad; + return ret; +} + +// convert a base64 string to hex +function b64tohex(s) { + var ret = "" + var i; + var k = 0; // b64 state, 0-3 + var slop; + for(i = 0; i < s.length; ++i) { + if(s.charAt(i) == b64pad) break; + v = b64map.indexOf(s.charAt(i)); + if(v < 0) continue; + if(k == 0) { + ret += int2char(v >> 2); + slop = v & 3; + k = 1; + } + else if(k == 1) { + ret += int2char((slop << 2) | (v >> 4)); + slop = v & 0xf; + k = 2; + } + else if(k == 2) { + ret += int2char(slop); + ret += int2char(v >> 2); + slop = v & 3; + k = 3; + } + else { + ret += int2char((slop << 2) | (v >> 4)); + ret += int2char(v & 0xf); + k = 0; + } + } + if(k == 1) + ret += int2char(slop << 2); + return ret; +} + +// convert a base64 string to a byte/number array +function b64toBA(s) { + //piggyback on b64tohex for now, optimize later + var h = b64tohex(s); + var i; + var a = new Array(); + for(i = 0; 2*i < h.length; ++i) { + a[i] = parseInt(h.substring(2*i,2*i+2),16); + } + return a; +} diff --git a/lib/twu-rsa/jsbn.js b/lib/twu-rsa/jsbn.js new file mode 100644 index 0000000..928cc4f --- /dev/null +++ b/lib/twu-rsa/jsbn.js @@ -0,0 +1,559 @@ +// Copyright (c) 2005 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); diff --git a/lib/twu-rsa/jsbn2.js b/lib/twu-rsa/jsbn2.js new file mode 100644 index 0000000..e53e00b --- /dev/null +++ b/lib/twu-rsa/jsbn2.js @@ -0,0 +1,648 @@ +// Copyright (c) 2005-2009 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +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]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + a.fromInt(lowprimes[i]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) diff --git a/lib/twu-rsa/rsa.js b/lib/twu-rsa/rsa.js new file mode 100644 index 0000000..9f86640 --- /dev/null +++ b/lib/twu-rsa/rsa.js @@ -0,0 +1,112 @@ +// Depends on jsbn.js and rng.js + +// Version 1.1: support utf-8 encoding in pkcs1pad2 + +// convert a (hex) string to a bignum object +function parseBigInt(str,r) { + return new BigInteger(str,r); +} + +function linebrk(s,n) { + var ret = ""; + var i = 0; + while(i + n < s.length) { + ret += s.substring(i,i+n) + "\n"; + i += n; + } + return ret + s.substring(i,s.length); +} + +function byte2Hex(b) { + if(b < 0x10) + return "0" + b.toString(16); + else + return b.toString(16); +} + +// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint +function pkcs1pad2(s,n) { + if(n < s.length + 11) { // TODO: fix for utf-8 + alert("Message too long for RSA"); + return null; + } + var ba = new Array(); + var i = s.length - 1; + while(i >= 0 && n > 0) { + var c = s.charCodeAt(i--); + if(c < 128) { // encode using utf-8 + ba[--n] = c; + } + else if((c > 127) && (c < 2048)) { + ba[--n] = (c & 63) | 128; + ba[--n] = (c >> 6) | 192; + } + else { + ba[--n] = (c & 63) | 128; + ba[--n] = ((c >> 6) & 63) | 128; + ba[--n] = (c >> 12) | 224; + } + } + ba[--n] = 0; + var rng = new SecureRandom(); + var x = new Array(); + while(n > 2) { // random non-zero pad + x[0] = 0; + while(x[0] == 0) rng.nextBytes(x); + ba[--n] = x[0]; + } + ba[--n] = 2; + ba[--n] = 0; + return new BigInteger(ba); +} + +// "empty" RSA key constructor +function RSAKey() { + this.n = null; + this.e = 0; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.coeff = null; +} + +// Set the public key fields N and e from hex strings +function RSASetPublic(N,E) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + } + else + alert("Invalid RSA public key"); +} + +// Perform raw public operation on "x": return x^e (mod n) +function RSADoPublic(x) { + return x.modPowInt(this.e, this.n); +} + +// Return the PKCS#1 RSA encryption of "text" as an even-length hex string +function RSAEncrypt(text) { + var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); + if(m == null) return null; + var c = this.doPublic(m); + if(c == null) return null; + var h = c.toString(16); + if((h.length & 1) == 0) return h; else return "0" + h; +} + +// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string +//function RSAEncryptB64(text) { +// var h = this.encrypt(text); +// if(h) return hex2b64(h); else return null; +//} + +// protected +RSAKey.prototype.doPublic = RSADoPublic; + +// public +RSAKey.prototype.setPublic = RSASetPublic; +RSAKey.prototype.encrypt = RSAEncrypt; +//RSAKey.prototype.encrypt_b64 = RSAEncryptB64; diff --git a/lib/twu-rsa/rsa2.js b/lib/twu-rsa/rsa2.js new file mode 100644 index 0000000..1dfdb70 --- /dev/null +++ b/lib/twu-rsa/rsa2.js @@ -0,0 +1,132 @@ +// Depends on rsa.js and jsbn2.js + +// Version 1.1: support utf-8 decoding in pkcs1unpad2 + +// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext +function pkcs1unpad2(d,n) { + var b = d.toByteArray(); + var i = 0; + while(i < b.length && b[i] == 0) ++i; + if(b.length-i != n-1 || b[i] != 2) + return null; + ++i; + while(b[i] != 0) + if(++i >= b.length) return null; + var ret = ""; + while(++i < b.length) { + var c = b[i] & 255; + if(c < 128) { // utf-8 decode + ret += String.fromCharCode(c); + } + else if((c > 191) && (c < 224)) { + ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63)); + ++i; + } + else { + ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63)); + i += 2; + } + } + return ret; +} + +// Set the private key fields N, e, and d from hex strings +function RSASetPrivate(N,E,D) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + } + else + alert("Invalid RSA private key"); +} + +// Set the private key fields N, e, d and CRT params from hex strings +function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + this.p = parseBigInt(P,16); + this.q = parseBigInt(Q,16); + this.dmp1 = parseBigInt(DP,16); + this.dmq1 = parseBigInt(DQ,16); + this.coeff = parseBigInt(C,16); + } + else + alert("Invalid RSA private key"); +} + +// Generate a new random private key B bits long, using public expt E +function RSAGenerate(B,E) { + var rng = new SecureRandom(); + var qs = B>>1; + this.e = parseInt(E,16); + var ee = new BigInteger(E,16); + for(;;) { + for(;;) { + this.p = new BigInteger(B-qs,1,rng); + if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; + } + for(;;) { + this.q = new BigInteger(qs,1,rng); + if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; + } + if(this.p.compareTo(this.q) <= 0) { + var t = this.p; + this.p = this.q; + this.q = t; + } + var p1 = this.p.subtract(BigInteger.ONE); + var q1 = this.q.subtract(BigInteger.ONE); + var phi = p1.multiply(q1); + if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { + this.n = this.p.multiply(this.q); + this.d = ee.modInverse(phi); + this.dmp1 = this.d.mod(p1); + this.dmq1 = this.d.mod(q1); + this.coeff = this.q.modInverse(this.p); + break; + } + } +} + +// Perform raw private operation on "x": return x^d (mod n) +function RSADoPrivate(x) { + if(this.p == null || this.q == null) + return x.modPow(this.d, this.n); + + // TODO: re-calculate any missing CRT params + var xp = x.mod(this.p).modPow(this.dmp1, this.p); + var xq = x.mod(this.q).modPow(this.dmq1, this.q); + + while(xp.compareTo(xq) < 0) + xp = xp.add(this.p); + return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is an even-length hex string and the output is a plain string. +function RSADecrypt(ctext) { + var c = parseBigInt(ctext, 16); + var m = this.doPrivate(c); + if(m == null) return null; + return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is a Base64-encoded string and the output is a plain string. +//function RSAB64Decrypt(ctext) { +// var h = b64tohex(ctext); +// if(h) return this.decrypt(h); else return null; +//} + +// protected +RSAKey.prototype.doPrivate = RSADoPrivate; + +// public +RSAKey.prototype.setPrivate = RSASetPrivate; +RSAKey.prototype.setPrivateEx = RSASetPrivateEx; +RSAKey.prototype.generate = RSAGenerate; +RSAKey.prototype.decrypt = RSADecrypt; +//RSAKey.prototype.b64_decrypt = RSAB64Decrypt; diff --git a/src/jwt-token.js b/src/jwt-token.js new file mode 100644 index 0000000..23425ad --- /dev/null +++ b/src/jwt-token.js @@ -0,0 +1,108 @@ +function HMACAlgorithm(hash, key) +{ + this.hash = hash; + this.key = key; +} + +HMACAlgorithm.prototype = +{ + update: function _update(data) + { + this.data = data; + }, + + finalize: function _finalize() + { + }, + + sign: function _sign() + { + return window.btoa(Crypto.HMAC(this.hash, this.data, this.key, {asString:true})); + } +} + +function RSASHAAlgorithm(hash, keyPEM) +{ + if (hash == "sha1") { + this.hash = "sha1"; + } else if (hash == "sha256") { + this.hash = "sha256"; + } else { + throw NoSuchAlgorithmException("JWT algorithm: " + jwtAlgStr); + } + this.keyPEM = keyPEM; +} +RSASHAAlgorithm.prototype = +{ + update: function _update(data) + { + this.data = data; + }, + finalize: function _finalize() + { + + }, + sign: function _sign() + { + var rsa = new RSAKey(); + rsa.readPrivateKeyFromPEMString(this.keyPEM); + var hSig = rsa.signString(this.data, this.hash); + return hex2b64(hSig); + } +} + +function WebToken(objectStr, algorithm) +{ + this.jsonStr = objectStr; + this.pkAlgorithm = algorithm; +} + +function jsonObj(strOrObject) +{ + if (typeof strOrObject == "string") { + return JSON.parse(strOrObject); + } + return strOrObject; +} + +WebToken.prototype = +{ + serialize: function _serialize(key) + { + var header = jsonObj(this.pkAlgorithm); + var jwtAlgStr = header.alg; + var algorithm; + + if ("ES256" === jwtAlgStr) { + //oid = SECObjectIdentifiers.secp256r1; + algorithm = new SHA256Digest(); + } else if ("ES384" === jwtAlgStr) { + //oid = SECObjectIdentifiers.secp384r1; + algorithm = new SHA384Digest(); + } else if ("ES512" === jwtAlgStr) { + //oid = SECObjectIdentifiers.secp521r1; + algorithm = new SHA512Digest(); + } else if ("HS256" === jwtAlgStr) { + algorithm = new HMACAlgorithm(Crypto.SHA256, key); + } else if ("RS256" === jwtAlgStr) { + algorithm = new RSASHAAlgorithm("sha256", key); + } else { + throw NoSuchAlgorithmException("JWT algorithm: " + jwtAlgStr); + } + + var algBytes = window.btoa(this.pkAlgorithm); + var jsonBytes = window.btoa(this.jsonStr); + var stringToSign = algBytes + "." + jsonBytes; + algorithm.update(stringToSign); // or something? + var digestValue = algorithm.finalize(); + var signatureValue = algorithm.sign(); + + dump(signatureValue+"\n"); + return stringToSign + "." + signatureValue; + }, + + verify: function _verify() + { + } +} + diff --git a/tests/doctest/doctest.css b/tests/doctest/doctest.css new file mode 100644 index 0000000..7403feb --- /dev/null +++ b/tests/doctest/doctest.css @@ -0,0 +1,69 @@ +pre { + overflow: auto; +} + +pre.doctest { + border-left: 3px solid #99f; + padding-left: 1em; +} + +pre.output { + border-left: 3px solid #9f9; + padding-left: 1em; +} + +.doctest-example-prompt { + color: #333; +} + +.doctest-success { + color: #060; +} + +.doctest-failure { + color: #600; +} + +.doctest-example-detail { + color: #060; + font-weight: bold; +} + +a.doctest-failure-link { + text-decoration: none; +} + +a.doctest-failure-link:hover { + text-decoration: underline; +} + +.doctest-example:target { + border-left: 3px solid #f00; +} + +div.test:target { + border: 3px solid #ff0; +} + +div.test { + border: 1px solid #999; + margin-bottom: 1em; +} + +div.test .test-id { + position: relative; + float: right; + background-color: #000; + color: #bbb; + padding: 3px; +} + +div.test .test-id a:link, +div.test .test-id a:visited { + color: #bbb; + text-decoration: none; +} + +div.test .test-id a:hover { + text-decoration: underline; +} diff --git a/tests/doctest/doctest.js b/tests/doctest/doctest.js new file mode 100644 index 0000000..c3c23dc --- /dev/null +++ b/tests/doctest/doctest.js @@ -0,0 +1,1397 @@ +/* + +Javascript doctest runner +Copyright 2006-2010 Ian Bicking + +This program is free software; you can redistribute it and/or modify it under +the terms of the MIT License. + +*/ + + +function doctest(verbosity/*default=0*/, elements/*optional*/, + outputId/*optional*/) { + var output = document.getElementById(outputId || 'doctestOutput'); + var reporter = new doctest.Reporter(output, verbosity || 0); + if (elements) { + if (typeof elements == 'string') { + // Treat it as an id + elements = [document.getElementById(elementId)]; + } + if (! elements.length) { + throw('No elements'); + } + var suite = new doctest.TestSuite(elements, reporter); + } else { + var els = doctest.getElementsByTagAndClassName('pre', 'doctest'); + var suite = new doctest.TestSuite(els, reporter); + } + suite.run(); +} + +doctest.runDoctest = function (el, reporter) { + logDebug('Testing element', el); + reporter.startElement(el); + if (el === null) { + throw('runDoctest() with a null element'); + } + var parsed = new doctest.Parser(el); + var runner = new doctest.JSRunner(reporter); + runner.runParsed(parsed); +}; + +doctest.TestSuite = function (els, reporter) { + if (this === window) { + throw('you forgot new!'); + } + this.els = els; + this.parsers = []; + for (var i=0; i= this.testSuite.parsers.length) { + logInfo('All examples from all sections tested'); + this.runner.reporter.finish(); + return; + } + logInfo('Testing example ' + (parserIndex+1) + ' of ' + + this.testSuite.parsers.length); + var runNext = function () { + self.run(parserIndex+1); + }; + this.runner.runParsed(this.testSuite.parsers[parserIndex], 0, runNext); +}; + +doctest.Parser = function (el) { + if (this === window) { + throw('you forgot new!'); + } + if (! el) { + throw('Bad call to doctest.Parser'); + } + if (el.getAttribute('parsed-id')) { + var examplesID = el.getAttribute('parsed-id'); + if (doctest._allExamples[examplesID]) { + this.examples = doctest._allExamples[examplesID]; + return; + } + } + var newHTML = document.createElement('span'); + newHTML.className = 'doctest-example-set'; + var examplesID = doctest.genID('example-set'); + newHTML.setAttribute('id', examplesID); + el.setAttribute('parsed-id', examplesID); + var text = doctest.getText(el); + var lines = text.split(/(?:\r\n|\r|\n)/); + this.examples = []; + var example_lines = []; + var output_lines = []; + for (var i=0; i/.test(line)) { + if (! example_lines.length) { + throw('Bad example: '+doctest.repr(line)+'\n' + +'> line not preceded by $'); + } + line = line.substr(1).replace(/ *$/, '').replace(/^ /, ''); + example_lines.push(line); + } else { + output_lines.push(line); + } + } + if (example_lines.length) { + var ex = new doctest.Example(example_lines, output_lines); + this.examples.push(ex); + newHTML.appendChild(ex.createSpan()); + } + el.innerHTML = ''; + el.appendChild(newHTML); + doctest._allExamples[examplesID] = this.examples; +}; + +doctest._allExamples = {}; + +doctest.Example = function (example, output) { + if (this === window) { + throw('you forgot new!'); + } + this.example = example.join('\n'); + this.output = output.join('\n'); + this.htmlID = null; + this.detailID = null; +}; + +doctest.Example.prototype.createSpan = function () { + var id = doctest.genID('example'); + var span = document.createElement('span'); + span.className = 'doctest-example'; + span.setAttribute('id', id); + this.htmlID = id; + var exampleSpan = document.createElement('span'); + exampleSpan.className = 'doctest-example-code'; + var exampleLines = this.example.split(/\n/); + for (var i=0; i 0) { + if (this.verbosity > 1) { + this.write('Trying:\n'); + this.write(this.formatOutput(example.example)); + this.write('Expecting:\n'); + this.write(this.formatOutput(example.output)); + this.write('ok\n'); + } else { + this.writeln(example.example + ' ... passed!'); + } + } + this.success += 1; + if ((example.output.indexOf('...') >= 0 + || example.output.indexOf('?') >= 0) + && output) { + example.markExample('doctest-success', 'Output:\n' + output); + } else { + example.markExample('doctest-success'); + } +}; + +doctest.Reporter.prototype.reportFailure = function (example, output) { + this.write('Failed example:\n'); + this.write('' + + this.formatOutput(example.example) + +''); + this.write('Expected:\n'); + this.write(this.formatOutput(example.output)); + this.write('Got:\n'); + this.write(this.formatOutput(output)); + this.failure += 1; + example.markExample('doctest-failure', 'Actual output:\n' + output); +}; + +doctest.Reporter.prototype.finish = function () { + this.writeln((this.success+this.failure) + + ' tests in ' + this.elements + ' items.'); + if (this.failure) { + var color = '#f00'; + } else { + var color = '#0f0'; + } + this.writeln('' + this.success + ' tests of ' + + '' + (this.success+this.failure) + ' passed, ' + + '' + + this.failure + ' failed.'); +}; + +doctest.Reporter.prototype.writeln = function (text) { + this.write(text + '\n'); +}; + +doctest.Reporter.prototype.write = function (text) { + var leading = /^[ ]*/.exec(text)[0]; + text = text.substr(leading.length); + for (var i=0; i'); + this.container.innerHTML += text; +}; + +doctest.Reporter.prototype.formatOutput = function (text) { + if (! text) { + return ' (nothing)\n'; + } + var lines = text.split(/\n/); + var output = ''; + for (var i=0; i= parsed.examples.length) { + if (finishedCallback) { + finishedCallback(); + } + return; + } + var example = parsed.examples[index]; + if (typeof example == 'undefined') { + throw('Undefined example (' + (index+1) + ' of ' + parsed.examples.length + ')'); + } + doctest._waitCond = null; + this.run(example); + var finishThisRun = function () { + self.finishRun(example); + if (doctest._AbortCalled) { + // FIXME: I need to find a way to make this more visible: + logWarn('Abort() called'); + return; + } + self.runParsed(parsed, index+1, finishedCallback); + }; + if (doctest._waitCond !== null) { + if (typeof doctest._waitCond == 'number') { + var condition = null; + var time = doctest._waitCond; + var maxTime = null; + } else { + var condition = doctest._waitCond; + // FIXME: shouldn't be hard-coded + var time = 100; + var maxTime = doctest._waitTimeout || doctest.defaultTimeout; + } + var start = (new Date()).getTime(); + var timeoutFunc = function () { + if (condition === null + || condition()) { + finishThisRun(); + } else { + // Condition not met, try again soon... + if ((new Date()).getTime() - start > maxTime) { + // Time has run out + var msg = 'Error: wait(' + repr(condition) + ') has timed out'; + writeln(msg); + logDebug(msg); + logDebug('Timeout after ' + ((new Date()).getTime() - start) + + ' milliseconds'); + finishThisRun(); + return; + } + setTimeout(timeoutFunc, time); + } + }; + setTimeout(timeoutFunc, time); + } else { + finishThisRun(); + } +}; + +doctest.formatTraceback = function (e, skipFrames) { + skipFrames = skipFrames || 0; + var lines = []; + if (typeof e == 'undefined' || !e) { + var caughtErr = null; + try { + (null).foo; + } catch (caughtErr) { + e = caughtErr; + } + skipFrames++; + } + if (e.stack) { + var stack = e.stack.split('\n'); + for (var i=skipFrames; i ' + filename + ':' + lineno); + } + } + } + if (lines.length) { + return lines; + } else { + return null; + } +}; + +doctest.logTraceback = function (e, skipFrames) { + var tracebackLines = doctest.formatTraceback(e, skipFrames); + if (! tracebackLines) { + return; + } + for (var i=0; i 1) { + // If it's only one line it's not worth showing this + var check = this.showCheckDifference(got, expected); + logWarn('Mismatch of output (line-by-line comparison follows)'); + for (var i=0; i gotLines.length ? + expectedLines.length : gotLines.length; + function displayExpectedLine(line) { + return line; + line = line.replace(/\[a-zA-Z0-9_.\]\+/g, '?'); + line = line.replace(/ \+/g, ' '); + line = line.replace(/\(\?:\.\|\[\\r\\n\]\)\*/g, '...'); + // FIXME: also unescape values? e.g., * became \* + return line; + } + for (var i=0; i= expectedLines.length) { + result.push('got extra line: ' + repr(gotLines[i])); + continue; + } else if (i >= gotLines.length) { + result.push('expected extra line: ' + displayExpectedLine(expectedLines[i])); + continue; + } + var gotLine = gotLines[i]; + try { + var expectRE = new RegExp('^' + expectedLines[i] + '$'); + } catch (e) { + result.push('regex match failed: ' + repr(gotLine) + ' (' + + expectedLines[i] + ')'); + continue; + } + if (gotLine.search(expectRE) != -1) { + result.push('match: ' + repr(gotLine)); + } else { + result.push('no match: ' + repr(gotLine) + ' (' + + displayExpectedLine(expectedLines[i]) + ')'); + } + } + return result; +}; + +// Should I really be setting this on RegExp? +RegExp.escape = function (text) { + if (!arguments.callee.sRE) { + var specials = [ + '/', '.', '*', '+', '?', '|', + '(', ')', '[', ']', '{', '}', '\\' + ]; + arguments.callee.sRE = new RegExp( + '(\\' + specials.join('|\\') + ')', 'g' + ); + } + return text.replace(arguments.callee.sRE, '\\$1'); +}; + +doctest.OutputCapturer = function () { + if (this === window) { + throw('you forgot new!'); + } + this.output = ''; +}; + +doctest._output = null; + +doctest.OutputCapturer.prototype.capture = function () { + doctest._output = this; +}; + +doctest.OutputCapturer.prototype.stopCapture = function () { + doctest._output = null; +}; + +doctest.OutputCapturer.prototype.write = function (text) { + if (typeof text == 'string') { + this.output += text; + } else { + this.output += repr(text); + } +}; + +// Used to create unique IDs: +doctest._idGen = 0; + +doctest.genID = function (prefix) { + prefix = prefix || 'generic-doctest'; + var id = doctest._idGen++; + return prefix + '-' + doctest._idGen; +}; + +doctest.writeln = function () { + for (var i=0; i (maxLen - indentString.length)) { + doctest._reprTrackRestore(restorer); + return doctest.multilineObjRepr(obj, indentString, maxLen); + } + return ostring; +}; + +doctest.multilineObjRepr = function (obj, indentString, maxLen) { + var keys = doctest._sortedKeys(obj); + var ostring = '{\n'; + for (var i=0; i (maxLen + indentString.length)) { + doctest._reprTrackRestore(restorer); + return doctest.multilineArrayRepr(obj, indentString, maxLen); + } + return s; +}; + +doctest.multilineArrayRepr = function (obj, indentString, maxLen) { + var s = "[\n"; + for (var i=0; i'; + return s; +}; + +doctest.repr.registry = [ + [function (o) { + return typeof o == 'string';}, + function (o) { + o = '"' + o.replace(/([\"\\])/g, '\\$1') + '"'; + o = o.replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r"); + return o; + }], + [function (o) { + return typeof o == 'number';}, + function (o) { + return o + ""; + }], + [function (o) { + return (typeof o == 'object' && o.xmlVersion); + }, + doctest.xmlRepr], + [function (o) { + var typ = typeof o; + if ((typ != 'object' && ! (type == 'function' && typeof o.item == 'function')) || + o === null || + typeof o.length != 'number' || + o.nodeType === 3) { + return false; + } + return true; + }, + doctest.arrayRepr + ]]; + +doctest.objDiff = function (orig, current) { + var result = { + added: {}, + removed: {}, + changed: {}, + same: {} + }; + for (var i in orig) { + if (! (i in current)) { + result.removed[i] = orig[i]; + } else if (orig[i] !== current[i]) { + result.changed[i] = [orig[i], current[i]]; + } else { + result.same[i] = orig[i]; + } + } + for (i in current) { + if (! (i in orig)) { + result.added[i] = current[i]; + } + } + return result; +}; + +doctest.writeDiff = function (orig, current, indentString) { + if (typeof orig != 'object' || typeof current != 'object') { + writeln(indentString + repr(orig, indentString) + ' -> ' + repr(current, indentString)); + return; + } + indentString = indentString || ''; + var diff = doctest.objDiff(orig, current); + var i, keys; + var any = false; + keys = doctest._sortedKeys(diff.added); + for (i=0; i ' + + repr(diff.changed[keys[i]][1], indentString)); + } + if (! any) { + writeln(indentString + '(no changes)'); + } +}; + +doctest.objectsEqual = function (ob1, ob2) { + var i; + if (typeof ob1 != 'object' || typeof ob2 != 'object') { + return ob1 === ob2; + } + for (i in ob1) { + if (ob1[i] !== ob2[i]) { + return false; + } + } + for (i in ob2) { + if (! (i in ob1)) { + return false; + } + } + return true; +}; + +doctest.getElementsByTagAndClassName = function (tagName, className, parent/*optional*/) { + parent = parent || document; + var els = parent.getElementsByTagName(tagName); + var result = []; + var regexes = []; + if (typeof className == 'string') { + className = [className]; + } + for (var i=0; i/g, '>'); +}; + +doctest.escapeSpaces = function (s) { + return s.replace(/ /g, '  '); +}; + +doctest.extend = function (obj, extendWith) { + for (i in extendWith) { + obj[i] = extendWith[i]; + } + return obj; +}; + +doctest.extendDefault = function (obj, extendWith) { + for (i in extendWith) { + if (typeof obj[i] == 'undefined') { + obj[i] = extendWith[i]; + } + } + return obj; +}; + +if (typeof repr == 'undefined') { + repr = doctest.repr; +} + +doctest._consoleFunc = function (attr) { + if (typeof window.console != 'undefined' + && typeof window.console[attr] != 'undefined') { + if (typeof console[attr].apply === 'function') { + result = function() { + console[attr].apply(console, arguments); + }; + } else { + result = console[attr]; + } + } else { + result = function () { + // FIXME: do something + }; + } + return result; +}; + +if (typeof log == 'undefined') { + log = doctest._consoleFunc('log'); +} + +if (typeof logDebug == 'undefined') { + logDebug = doctest._consoleFunc('log'); +} + +if (typeof logInfo == 'undefined') { + logInfo = doctest._consoleFunc('info'); +} + +if (typeof logWarn == 'undefined') { + logWarn = doctest._consoleFunc('warn'); +} + +doctest.eval = function () { + return window.eval.apply(window, arguments); +}; + +doctest.useCoffeeScript = function (options) { + options = options || {}; + options.bare = true; + options.globals = true; + if (! options.fileName) { + options.fileName = 'repl'; + } + if (typeof CoffeeScript == 'undefined') { + doctest.logWarn('coffee-script.js is not included'); + throw 'coffee-script.js is not included'; + } + doctest.eval = function (code) { + var src = CoffeeScript.compile(code, options); + logDebug('Compiled code to:', src); + return window.eval(src); + }; +}; + +doctest.autoSetup = function (parent) { + var tags = doctest.getElementsByTagAndClassName('div', 'test', parent); + // First we'll make sure everything has an ID + var tagsById = {}; + for (var i=0; i 1) { + // This makes the :target CSS work, since if the hash points to an + // element whose id has just been added, it won't be noticed + location.hash = location.hash; + } + var output = document.getElementById('doctestOutput'); + if (! tags.length) { + tags = document.getElementsByTagName('body'); + } + if (! output) { + output = document.createElement('pre'); + output.setAttribute('id', 'doctestOutput'); + output.className = 'output'; + tags[0].parentNode.insertBefore(output, tags[0]); + } + var reloader = document.getElementById('doctestReload'); + if (! reloader) { + reloader = document.createElement('button'); + reloader.setAttribute('type', 'button'); + reloader.setAttribute('id', 'doctest-testall'); + reloader.innerHTML = 'test all'; + reloader.onclick = function () { + location.hash = '#doctest-testall'; + location.reload(); + }; + output.parentNode.insertBefore(reloader, output); + } +}; + +doctest.autoSetup._idCount = 0; + +doctest.Spy = function (name, options, extraOptions) { + var self; + if (doctest.spies[name]) { + self = doctest.spies[name]; + if (! options && ! extraOptions) { + return self; + } + } else { + self = function () { + return self.func.apply(this, arguments); + }; + } + name = name || 'spy'; + options = options || {}; + if (typeof options == 'function') { + options = {applies: options}; + } + if (extraOptions) { + doctest.extendDefault(options, extraOptions); + } + doctest.extendDefault(options, doctest.defaultSpyOptions); + self._name = name; + self.options = options; + self.called = false; + self.calledWait = false; + self.args = null; + self.self = null; + self.argList = []; + self.selfList = []; + self.writes = options.writes || false; + self.returns = options.returns || null; + self.applies = options.applies || null; + self.binds = options.binds || null; + self.throwError = options.throwError || null; + self.ignoreThis = options.ignoreThis || false; + self.wrapArgs = options.wrapArgs || false; + self.func = function () { + self.called = true; + self.calledWait = true; + self.args = doctest._argsToArray(arguments); + self.self = this; + self.argList.push(self.args); + self.selfList.push(this); + // It might be possible to get the caller? + if (self.writes) { + writeln(self.formatCall()); + } + if (self.throwError) { + throw self.throwError; + } + if (self.applies) { + return self.applies.apply(this, arguments); + } + return self.returns; + }; + self.func.toString = function () { + return "Spy('" + self._name + "').func"; + }; + + // Method definitions: + self.formatCall = function () { + var s = ''; + if ((! self.ignoreThis) && self.self !== window && self.self !== self) { + s += doctest.repr(self.self) + '.'; + } + s += self._name; + if (self.args === null) { + return s + ':never called'; + } + s += '('; + for (var i=0; i'); + } + var loc = window.location.search.substring(1); + if (auto || (/doctestRun/).exec(loc)) { + var elements = null; + // FIXME: we need to put the output near the specific test being tested: + if (location.hash) { + var el = document.getElementById(location.hash.substr(1)); + if (el) { + if (/\btest\b/.exec(el.className)) { + var testEls = doctest.getElementsByTagAndClassName('pre', 'doctest', el); + elements = doctest.getElementsByTagAndClassName('pre', ['doctest', 'setup']); + for (var i=0; i + + + +JSON Web Token Tests + + + + + + + + + + + + + +

Test Encodings

+
+A simple JSON object is correctly converted to Base64. +
+$ window.btoa(joeStr); // to test: does this properly handle high-byte characters?
+"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ=="
+
+
+ +
+An HS256 algorithm element is correctly converted to Base64. +
+$ window.btoa(hs256);
+"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
+
+
+ +
+An ES256 algorithm element is correctly converted to Base64. +
+$ window.btoa(es256);
+"eyJhbGciOiJFUzI1NiJ9"
+
+
+ +
+An RS256 algorithm element is correctly converted to Base64. +
+$ window.btoa(rs256);
+"eyJhbGciOiJSUzI1NiJ9"
+
+
+ +
+Test creation of an HMAC-SHA256 signature. +
+$ var jwt = new WebToken(joeStr, hs256);
+$ var signed = jwt.serialize(hsKey)
+$ var split = signed.split("\.")
+$ split.length
+3
+$ split[0]
+"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
+$ split[1]
+"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ=="
+$ split[2]
+"Biflo4Rnc3YqNjOSpWEYCx3j+63vf4EjDYtbzQIft3A="
+
+
+ + +
+Test creation of an RSA-SHA256 signature. +
+$ var jwt = new WebToken(joeStr, rs256);
+$ var signed = jwt.serialize(rsKeyPEM)
+$ var split = signed.split("\.")
+$ split.length
+3
+$ split[0]
+"eyJhbGciOiJSUzI1NiJ9"
+$ split[1]
+"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ=="
+$ split[2]
+"hgod3zm0Qt1U5owxPrtk/L05ETgC5Wt8qmar5CIQGD07HyMYlvKxsDv65tBCixxCEWcuBT/AZ5NuoGZ3bAnTt0gBKmhyxxv/PlmY7ss51m8lbpGXn3hmEqzEHZMnFhdPFPK0+mZaTo/NhWoiwVH6y0PB8yblY+gGsn/Sfjz/i2MbmPWb63lu/iNRPmjzoEZds5RFa/4P5HpzlabsjpEfbileTsjUHUIRRSBOeObRVKuk3FS9N/YncODOeeYnfvdZInkKrD42871jxksYPITUXSJqW5fzmbmkhp+9WyotQe1i4vHFAsSYlJrwkdxSDEj3vIlUasy52tXCseoWpZES7g=="
+
+
+ + + +
+Test verification of an EDSA Signature according to Draft 01. +
+$ var signature = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q";
+$ var x = [127, 205, 206, 39, 112, 246, 196, 93, 65, 131, 203, 238, 111, 219, 75, 123, 88, 7, 51, 53, 123, 233, 239, 19, 186, 207, 110, 60, 123, 209, 84, 69];
+$ var y = [199, 241, 68, 205, 27, 189, 155, 126, 135, 44, 223, 237, 185, 238, 185, 244, 179, 105, 93, 110, 169, 11, 36, 173, 138, 70, 35, 40, 133, 136, 229, 173];
+$ WebToken.verify(signature, x, y);
+true
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/tests/key.pem b/tests/key.pem new file mode 100644 index 0000000..7dba987 --- /dev/null +++ b/tests/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxEymjlmPMUsvE5PU+ATSNwg4vLmKkD31SG28ZDn7Dh6c1OAh +YX/yEp+5BXq8ybBkRpfC/7srRQ4+TxnnGspPt/eQyKfNF80lwy4TRA/SbRCjYf+N +Tq6e8v3QH8M9SciKMG0OLmvMnOgV/mTRUwZSuLOetZLD16gCgd1GG0sMAKBXbJZg +2oz1tJulPOtaCYmQxu53cVHkfDvF/4K0mK122jo1FxxPkAn3NqMCod3Gxi8UHhNb +0s8xlW9zbyosdftbj9b5TdBMpRS9fEiesZlAZq8kOkYTRrUTrYv25wjXhHBHcNyK +THCn5Xdivu7sdPLePDj8nHRnLj4rkSjKpiFGawIDAQABAoIBAQCd5bojzs5rrDpV +2Rf52IbvVWGuDOD0XaIrfHmJdUoIdX9ZjF/NeYlSib/eNHgfFA/U6MYlxnxrs6VT +I1bOKeYt6KlBfhhvCMlTUoCUwtVUfYmujk0v+S5J7ve2VOm7q9/cTByYIoYXwGfQ +lW/7BJ8Niw4ip8d4dOBvbXmzAo7JAMdDAMwCfSOLPpEZk1wlsT9b0Ghvg0vlUNhO +HHdfJ63FTrclYPVDMsn6e5hFNItBzuPx9oR3vxCfB42fF9MZXFzHjlt5GuCHj9Ct +BxRRqtCloRKGuujOCRA33SZKzwJwv+93fLYVHtjpX8k/s8cbJEpUst9qtlYNj1HZ +2g6CKtBBAoGBAPDCJ8Gm0b46cAb/671s1xRIbtCn6qHKpTqp7TIOaqj9NAbjx1kb +3MBu1G89Vs0To9iYz0v3UYqC2gI3NwvHKYe0QxCZTO8ci/iTqkvlmvMlmW+fpByQ +hsk//Ms3BW/3EMEXbRIK5p7jALkfrzejcJHxnlq93vRhoiReK+1ejLEjAoGBANC5 ++MG5NpfrciDtjHPzZybTTwKvoL3TJo1cDHrZhNgYcT98+JC5o70Y2/IzkpvORanj +bPB+pHE3fXgGGTqDHcmo+vFt82FoDeLvdQoCInWn32mMIoAKatO33z+yAfr86yFN +23vSP/BgupkTF/gImct/+keYMvX0z+8lQTzyJL4ZAoGAVWaZkaCp/89c067OIWhN +Nr2myU729KMcTx3drIbeoMkIPnVnzh0LBhuKMVdRxfb0hK1XwvucqgRWbrjFRuFD +wwZXT7kBSEQZBnjizH9Konsw3R6EqTk/Bn4zHqaKwDekcsnrf53sRmoBZKlvjs3j +7XEGmeuF/avwRvQ8oruKLn8CgYA34oMrAzcNx/lfvZqMdRAaQht2gaWNDZrV4F5r +ChBad3ji4cf/n+SqPZyuJYbMdpcKXJ0PaykGMzBB0YgxtWdlVfwSZjjyzJRjPW/d +xSKK0+6qaN3X4HIny6RXfoavN8aQvTLV3T5HUu7DC2yweUUMSnCteJ+0YN7HjfcA +pWiXCQKBgCyH5DM1VoSMh+UIrzYnoUUJcVC/q2XjtfGhwraUBaM6z9SWhX6jfLHi +5ojsfcHNoSxykHIBYW6j/CVUJjhccQkJgGkGFj1fHvq5iXfOi1c1izv41CDDXdP+ +Jq3Mls3h5WNryE7aM49KrZEjco+sj5QswL2y0NMpxwGDOijZjKIr +-----END RSA PRIVATE KEY----- diff --git a/tests/pubkey.pem b/tests/pubkey.pem new file mode 100644 index 0000000..73dd491 --- /dev/null +++ b/tests/pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxEymjlmPMUsvE5PU+ATS +Nwg4vLmKkD31SG28ZDn7Dh6c1OAhYX/yEp+5BXq8ybBkRpfC/7srRQ4+TxnnGspP +t/eQyKfNF80lwy4TRA/SbRCjYf+NTq6e8v3QH8M9SciKMG0OLmvMnOgV/mTRUwZS +uLOetZLD16gCgd1GG0sMAKBXbJZg2oz1tJulPOtaCYmQxu53cVHkfDvF/4K0mK12 +2jo1FxxPkAn3NqMCod3Gxi8UHhNb0s8xlW9zbyosdftbj9b5TdBMpRS9fEiesZlA +Zq8kOkYTRrUTrYv25wjXhHBHcNyKTHCn5Xdivu7sdPLePDj8nHRnLj4rkSjKpiFG +awIDAQAB +-----END PUBLIC KEY-----