|
1 | | -/*global process, exports*/ |
2 | | -constBuffer=require('buffer').Buffer; |
3 | | -constStream=require('stream'); |
4 | | -constutil=require('util'); |
5 | | -constbase64url=require('base64url'); |
6 | | -constjwa=require('jwa'); |
| 1 | +/*global exports*/ |
| 2 | +constSignStream=require('./lib/sign-stream'); |
| 3 | +constVerifyStream=require('./lib/verify-stream'); |
7 | 4 |
|
8 | 5 | constALGORITHMS=[ |
9 | 6 | 'HS256','HS384','HS512', |
10 | 7 | 'RS256','RS384','RS512', |
11 | | -'ES256','ES384','ES512', |
| 8 | +'ES256','ES384','ES512' |
12 | 9 | ]; |
13 | 10 |
|
14 | | -functiontoString(obj){ |
15 | | -if(typeofobj==='string') |
16 | | -returnobj; |
17 | | -if(typeofobj==='number'||Buffer.isBuffer(obj)) |
18 | | -returnobj.toString(); |
19 | | -returnJSON.stringify(obj); |
20 | | -} |
21 | | - |
22 | | -functionjwsSecuredInput(header,payload){ |
23 | | -constencodedHeader=base64url(toString(header),'binary'); |
24 | | -constencodedPayload=base64url(toString(payload),'binary'); |
25 | | -returnutil.format('%s.%s',encodedHeader,encodedPayload); |
26 | | -} |
27 | | - |
28 | | -functionjwsSign(opts){ |
29 | | -constheader=opts.header; |
30 | | -constpayload=opts.payload; |
31 | | -constsecretOrKey=opts.secret||opts.privateKey; |
32 | | -constalgo=jwa(header.alg); |
33 | | -constsecuredInput=jwsSecuredInput(header,payload); |
34 | | -constsignature=algo.sign(securedInput,secretOrKey); |
35 | | -returnutil.format('%s.%s',securedInput,signature); |
36 | | -} |
37 | | - |
38 | | -functionisObject(thing){ |
39 | | -returnObject.prototype.toString.call(thing)==='[object Object]'; |
40 | | -} |
41 | | - |
42 | | -functionsafeJsonParse(thing){ |
43 | | -if(isObject(thing)) |
44 | | -returnthing; |
45 | | -try{returnJSON.parse(thing)} |
46 | | -catch(e){returnundefined} |
47 | | -} |
48 | | - |
49 | | -functionheaderFromJWS(jwsSig){ |
50 | | -constencodedHeader=jwsSig.split('.',1)[0]; |
51 | | -returnsafeJsonParse(base64url.decode(encodedHeader,'binary')); |
52 | | -} |
53 | | - |
54 | | -functionsecuredInputFromJWS(jwsSig){ |
55 | | -returnjwsSig.split('.',2).join('.'); |
56 | | -} |
57 | | - |
58 | | -functionalgoFromJWS(jwsSig){ |
59 | | -varerr; |
60 | | -constheader=headerFromJWS(jwsSig); |
61 | | -if(typeofheader!='object'){ |
62 | | -err=newError("Invalid token: no header in signature '"+jwsSig+"'"); |
63 | | -err.code="MISSING_HEADER"; |
64 | | -err.signature=jwsSig; |
65 | | -throwerr; |
66 | | -} |
67 | | -if(!header.alg){ |
68 | | -err=newError("Missing `alg` field in header for signature '"+jwsSig+"'"); |
69 | | -err.code="MISSING_ALGORITHM"; |
70 | | -err.header=header; |
71 | | -err.signature=jwsSig; |
72 | | -throwerr; |
73 | | -} |
74 | | -returnheader.alg; |
75 | | -} |
76 | | - |
77 | | -functionsignatureFromJWS(jwsSig){ |
78 | | -returnjwsSig.split('.')[2]; |
79 | | -} |
80 | | - |
81 | | -functionpayloadFromJWS(jwsSig){ |
82 | | -constpayload=jwsSig.split('.')[1]; |
83 | | -returnbase64url.decode(payload,'binary'); |
84 | | -} |
85 | | - |
86 | | -constJWS_REGEX=/^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/; |
87 | | -functionisValidJws(string){ |
88 | | -if(!JWS_REGEX.test(string)) |
89 | | -returnfalse; |
90 | | -if(!headerFromJWS(string)) |
91 | | -returnfalse; |
92 | | -returntrue; |
93 | | -} |
94 | | - |
95 | | -functionjwsVerify(jwsSig,secretOrKey){ |
96 | | -jwsSig=toString(jwsSig); |
97 | | -constsignature=signatureFromJWS(jwsSig); |
98 | | -constsecuredInput=securedInputFromJWS(jwsSig); |
99 | | -constalgo=jwa(algoFromJWS(jwsSig)); |
100 | | -returnalgo.verify(securedInput,signature,secretOrKey); |
101 | | -} |
102 | | - |
103 | | -functionjwsDecode(jwsSig,opts){ |
104 | | -opts=opts||{}; |
105 | | -jwsSig=toString(jwsSig); |
106 | | -if(!isValidJws(jwsSig)) |
107 | | -returnnull; |
108 | | -constheader=headerFromJWS(jwsSig); |
109 | | -if(!header) |
110 | | -returnnull; |
111 | | -varpayload=payloadFromJWS(jwsSig); |
112 | | -if(header.typ==='JWT'||opts.json) |
113 | | -payload=JSON.parse(payload); |
114 | | -return{ |
115 | | -header:header, |
116 | | -payload:payload, |
117 | | -signature:signatureFromJWS(jwsSig), |
118 | | -}; |
119 | | -} |
120 | | - |
121 | | -functionSignStream(opts){ |
122 | | -constsecret=opts.secret||opts.privateKey||opts.key; |
123 | | -constsecretStream=newDataStream(secret); |
124 | | -this.readable=true; |
125 | | -this.header=opts.header; |
126 | | -this.secret=this.privateKey=this.key=secretStream; |
127 | | -this.payload=newDataStream(opts.payload); |
128 | | -this.secret.once('close',function(){ |
129 | | -if(!this.payload.writable&&this.readable) |
130 | | -this.sign(); |
131 | | -}.bind(this)); |
132 | | - |
133 | | -this.payload.once('close',function(){ |
134 | | -if(!this.secret.writable&&this.readable) |
135 | | -this.sign(); |
136 | | -}.bind(this)); |
137 | | -} |
138 | | -util.inherits(SignStream,Stream); |
139 | | -SignStream.prototype.sign=functionsign(){ |
140 | | -constsignature=jwsSign({ |
141 | | -header:this.header, |
142 | | -payload:this.payload.buffer, |
143 | | -secret:this.secret.buffer, |
144 | | -}); |
145 | | -this.emit('done',signature); |
146 | | -this.emit('data',signature); |
147 | | -this.emit('end'); |
148 | | -this.readable=false; |
149 | | -returnsignature; |
150 | | -}; |
151 | | - |
152 | | -functionVerifyStream(opts){ |
153 | | -opts=opts||{}; |
154 | | -constsecretOrKey=opts.secret||opts.publicKey||opts.key; |
155 | | -constsecretStream=newDataStream(secretOrKey); |
156 | | -this.readable=true; |
157 | | -this.secret=this.publicKey=this.key=secretStream; |
158 | | -this.signature=newDataStream(opts.signature); |
159 | | -this.secret.once('close',function(){ |
160 | | -if(!this.signature.writable&&this.readable) |
161 | | -this.verify(); |
162 | | -}.bind(this)); |
163 | | - |
164 | | -this.signature.once('close',function(){ |
165 | | -if(!this.secret.writable&&this.readable) |
166 | | -this.verify(); |
167 | | -}.bind(this)); |
168 | | -} |
169 | | -util.inherits(VerifyStream,Stream); |
170 | | -VerifyStream.prototype.verify=functionverify(){ |
171 | | -constvalid=jwsVerify(this.signature.buffer,this.key.buffer); |
172 | | -constobj=jwsDecode(this.signature.buffer); |
173 | | -this.emit('done',valid,obj); |
174 | | -this.emit('data',valid); |
175 | | -this.emit('end'); |
176 | | -this.readable=false; |
177 | | -returnvalid; |
178 | | -}; |
179 | | - |
180 | | -functionDataStream(data){ |
181 | | -this.buffer=Buffer(data||0); |
182 | | -this.writable=true; |
183 | | -this.readable=true; |
184 | | -if(!data) |
185 | | -returnthis; |
186 | | -if(typeofdata.pipe==='function') |
187 | | -data.pipe(this); |
188 | | -elseif(data.length){ |
189 | | -this.writable=false; |
190 | | -process.nextTick(function(){ |
191 | | -this.buffer=data; |
192 | | -this.emit('end',data); |
193 | | -this.readable=false; |
194 | | -this.emit('close'); |
195 | | -}.bind(this)); |
196 | | -} |
197 | | -} |
198 | | -util.inherits(DataStream,Stream); |
199 | | - |
200 | | -DataStream.prototype.write=functionwrite(data){ |
201 | | -this.buffer=Buffer.concat([this.buffer,Buffer(data)]); |
202 | | -this.emit('data',data); |
203 | | -}; |
204 | | - |
205 | | -DataStream.prototype.end=functionend(data){ |
206 | | -if(data) |
207 | | -this.write(data); |
208 | | -this.emit('end',data); |
209 | | -this.emit('close'); |
210 | | -this.writable=false; |
211 | | -this.readable=false; |
212 | | -}; |
213 | | - |
214 | 11 | exports.ALGORITHMS=ALGORITHMS; |
215 | | -exports.sign=jwsSign; |
216 | | -exports.verify=jwsVerify; |
217 | | -exports.decode=jwsDecode; |
218 | | -exports.isValid=isValidJws; |
| 12 | +exports.sign=SignStream.sign; |
| 13 | +exports.verify=VerifyStream.verify; |
| 14 | +exports.decode=VerifyStream.decode; |
| 15 | +exports.isValid=VerifyStream.isValid; |
219 | 16 | exports.createSign=functioncreateSign(opts){ |
220 | 17 | returnnewSignStream(opts); |
221 | 18 | }; |
|