3 var JWTInternals = (function() {
5 // convert a base64url string to hex
6 var b64urlmap="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
7 function b64urltohex(s) {
10 var k = 0; // b64 state, 0-3
12 for(i = 0; i < s.length; ++i) {
13 var v = b64urlmap.indexOf(s.charAt(i));
16 ret += int2char(v >> 2);
21 ret += int2char((slop << 2) | (v >> 4));
26 ret += int2char(slop);
27 ret += int2char(v >> 2);
32 ret += int2char((slop << 2) | (v >> 4));
33 ret += int2char(v & 0xf);
38 ret += int2char(slop << 2);
44 function base64urlencode(arg)
46 var s = window.btoa(arg); // Standard base64 encoder
47 s = s.split('=')[0]; // Remove any trailing '='s
48 s = s.replace(/\+/g, '-'); // 62nd char of encoding
49 s = s.replace(/\//g, '_'); // 63rd char of encoding
50 // TODO optimize this; we can do much better
54 function base64urldecode(arg)
57 s = s.replace(/-/g, '+'); // 62nd char of encoding
58 s = s.replace(/_/g, '/'); // 63rd char of encoding
59 switch (s.length % 4) // Pad with trailing '='s
61 case 0: break; // No pad chars in this case
62 case 2: s += "=="; break; // Two pad chars
63 case 3: s += "="; break; // One pad char
64 default: throw new InputException("Illegal base64url string!");
66 return window.atob(s); // Standard base64 decoder
69 function NoSuchAlgorithmException(message) {
70 this.message = message;
71 this.toString = function() { return "No such algorithm: "+this.message; };
73 function NotImplementedException(message) {
74 this.message = message;
75 this.toString = function() { return "Not implemented: "+this.message; };
77 function InputException(message) {
78 this.message = message;
79 this.toString = function() { return "Malformed input: "+this.message; };
82 function HMACAlgorithm(hash, key)
84 if (hash == "sha256") {
85 this.hash = sjcl.hash.sha256;
87 throw new NoSuchAlgorithmException("HMAC does not support hash " + hash);
89 this.key = sjcl.codec.utf8String.toBits(key);
92 HMACAlgorithm.prototype =
94 update: function _update(data)
99 finalize: function _finalize()
103 sign: function _sign()
105 var hmac = new sjcl.misc.hmac(this.key, this.hash);
106 var result = hmac.encrypt(this.data);
107 return base64urlencode(window.atob(sjcl.codec.base64.fromBits(result)));
110 verify: function _verify(sig)
112 var hmac = new sjcl.misc.hmac(this.key, this.hash);
113 var result = hmac.encrypt(this.data);
115 return base64urlencode(window.atob(sjcl.codec.base64.fromBits(result))) == sig;
119 function RSASHAAlgorithm(hash, keyPEM)
121 if (hash == "sha1") {
123 } else if (hash == "sha256") {
124 this.hash = "sha256";
126 throw new NoSuchAlgorithmException("JWT algorithm: " + hash);
128 this.keyPEM = keyPEM;
130 RSASHAAlgorithm.prototype =
132 update: function _update(data)
136 finalize: function _finalize()
140 sign: function _sign()
142 var rsa = new RSAKey();
143 rsa.readPrivateKeyFromPEMString(this.keyPEM);
144 var hSig = rsa.signString(this.data, this.hash);
145 return base64urlencode(base64urldecode(hex2b64(hSig))); // TODO replace this with hex2b64urlencode!
147 verify: function _verify(sig)
149 var result = this.keyPEM.verifyString(this.data, b64urltohex(sig));
154 function WebToken(objectStr, algorithm)
156 this.objectStr = objectStr;
157 this.pkAlgorithm = algorithm;
160 var WebTokenParser = {
162 parse: function _parse(input)
164 var parts = input.split(".");
165 if (parts.length != 3) {
166 throw new MalformedWebToken("Must have three parts");
168 var token = new WebToken();
169 token.headerSegment = parts[0];
170 token.payloadSegment = parts[1];
171 token.cryptoSegment = parts[2];
173 token.pkAlgorithm = base64urldecode(parts[0]);
178 function jsonObj(strOrObject)
180 if (typeof strOrObject == "string") {
181 return JSON.parse(strOrObject);
186 function constructAlgorithm(jwtAlgStr, key)
188 if ("ES256" === jwtAlgStr) {
189 throw new NotImplementedException("ECDSA-SHA256 not yet implemented");
190 } else if ("ES384" === jwtAlgStr) {
191 throw new NotImplementedException("ECDSA-SHA384 not yet implemented");
192 } else if ("ES512" === jwtAlgStr) {
193 throw new NotImplementedException("ECDSA-SHA512 not yet implemented");
194 } else if ("HS256" === jwtAlgStr) {
195 return new HMACAlgorithm("sha256", key);
196 } else if ("HS384" === jwtAlgStr) {
197 throw new NotImplementedException("HMAC-SHA384 not yet implemented");
198 } else if ("HS512" === jwtAlgStr) {
199 throw new NotImplementedException("HMAC-SHA512 not yet implemented");
200 } else if ("RS256" === jwtAlgStr) {
201 return new RSASHAAlgorithm("sha256", key);
202 } else if ("RS384" === jwtAlgStr) {
203 throw new NotImplementedException("RSA-SHA384 not yet implemented");
204 } else if ("RS512" === jwtAlgStr) {
205 throw new NotImplementedException("RSA-SHA512 not yet implemented");
207 throw new NoSuchAlgorithmException("Unknown algorithm: " + jwtAlgStr);
213 serialize: function _serialize(key)
215 var header = jsonObj(this.pkAlgorithm);
216 var jwtAlgStr = header.alg;
217 var algorithm = constructAlgorithm(jwtAlgStr, key);
218 var algBytes = base64urlencode(this.pkAlgorithm);
219 var jsonBytes = base64urlencode(this.objectStr);
221 var stringToSign = algBytes + "." + jsonBytes;
222 algorithm.update(stringToSign);
223 var digestValue = algorithm.finalize();
225 var signatureValue = algorithm.sign();
226 return algBytes + "." + jsonBytes + "." + signatureValue;
229 verify: function _verify(key)
231 var header = jsonObj(this.pkAlgorithm);
232 var jwtAlgStr = header.alg;
233 var algorithm = constructAlgorithm(jwtAlgStr, key);
234 algorithm.update(this.headerSegment + "." + this.payloadSegment);
235 algorithm.finalize();
236 return algorithm.verify(this.cryptoSegment);
240 jwt.WebToken = WebToken;
241 jwt.WebTokenParser = WebTokenParser;
242 jwt.base64urlencode = base64urlencode;
243 jwt.base64urldecode = base64urldecode;