12. Araxis Merge ファイル比較レポート

このレポートは Araxis Merge11/20/2017 2:20:34 PM GMT Standard Time によって作成されました。Merge に関する情報については www.araxis.com をご覧ください。このレポートは XHTML と CSS2 を使用し、最新の標準基準に準拠したブラウザでご覧になれます。このレポートを印刷するための最適条件は、背景の色とイメージの印刷を可能にして、印刷の向きは横を使用します。

12.1 ファイルの比較

# ロケーション ファイル 更新日時
1 C:\Merge Test Files\8.0.47\java\org\apache\catalina\authenticator DigestAuthenticator.java Fri Sep 29 16:53:28 2017 UTC
2 C:\Merge Test Files\8.5.23\java\org\apache\catalina\authenticator DigestAuthenticator.java Thu Sep 28 11:32:16 2017 UTC

12.2 比較の概要

説明 第1ファイルと第2ファイル
テキスト ブロック
変更なし 8 1280
変更箇所 3 6
挿入箇所 4 9
削除箇所 0 0

12.3 比較のオプション

余白 連続した空白文字を 1 文字として扱う
大文字/小文字 大文字と小文字の相違を意味のあるものとして扱う
行終端文字 行終端文字(CRLF 文字)の相違を無視する
CR/LF 文字 比較の詳細に示されない

12.4 有効な正規表現

有効な正規表現はありません。

12.5 比較の詳細

1   /*   1   /*
2    * License d to the A pache Soft ware Found ation (ASF ) under on e or more   2    * License d to the A pache Soft ware Found ation (ASF ) under on e or more
3    * contrib utor licen se agreeme nts.  See  the NOTICE  file dist ributed wi th   3    * contrib utor licen se agreeme nts.  See  the NOTICE  file dist ributed wi th
4    * this wo rk for add itional in formation  regarding  copyright  ownership.   4    * this wo rk for add itional in formation  regarding  copyright  ownership.
5    * The ASF  licenses  this file  to You und er the Apa che Licens e, Version  2.0   5    * The ASF  licenses  this file  to You und er the Apa che Licens e, Version  2.0
6    * (the "L icense");  you may no t use this  file exce pt in comp liance wit h   6    * (the "L icense");  you may no t use this  file exce pt in comp liance wit h
7    * the Lic ense.  You  may obtai n a copy o f the Lice nse at   7    * the Lic ense.  You  may obtai n a copy o f the Lice nse at
8    *   8    *
9    *      ht tp://www.a pache.org/ licenses/L ICENSE-2.0   9    *      ht tp://www.a pache.org/ licenses/L ICENSE-2.0
10    *   10    *
11    * Unless  required b y applicab le law or  agreed to  in writing , software   11    * Unless  required b y applicab le law or  agreed to  in writing , software
12    * distrib uted under  the Licen se is dist ributed on  an "AS IS " BASIS,   12    * distrib uted under  the Licen se is dist ributed on  an "AS IS " BASIS,
13    * WITHOUT  WARRANTIE S OR CONDI TIONS OF A NY KIND, e ither expr ess or imp lied.   13    * WITHOUT  WARRANTIE S OR CONDI TIONS OF A NY KIND, e ither expr ess or imp lied.
14    * See the  License f or the spe cific lang uage gover ning permi ssions and   14    * See the  License f or the spe cific lang uage gover ning permi ssions and
15    * limitat ions under  the Licen se.   15    * limitat ions under  the Licen se.
16    */   16    */
17   package or g.apache.c atalina.au thenticato r;   17   package or g.apache.c atalina.au thenticato r;
18     18  
19   import jav a.io.IOExc eption;   19   import jav a.io.IOExc eption;
20   import jav a.io.Strin gReader;   20   import jav a.io.Strin gReader;
21   import jav a.nio.char set.Standa rdCharsets ;   21   import jav a.nio.char set.Standa rdCharsets ;
22   import jav a.security .Principal ;   22   import jav a.security .Principal ;
23   import jav a.util.Lin kedHashMap ;   23   import jav a.util.Lin kedHashMap ;
24   import jav a.util.Map ;   24   import jav a.util.Map ;
25     25  
26   import jav ax.servlet .http.Http ServletReq uest;   26   import jav ax.servlet .http.Http ServletReq uest;
27   import jav ax.servlet .http.Http ServletRes ponse;   27   import jav ax.servlet .http.Http ServletRes ponse;
28     28  
29   import org .apache.ca talina.Lif ecycleExce ption;   29   import org .apache.ca talina.Lif ecycleExce ption;
30   import org .apache.ca talina.Rea lm;   30   import org .apache.ca talina.Rea lm;
31   import org .apache.ca talina.con nector.Req uest;   31   import org .apache.ca talina.con nector.Req uest;
32   import org .apache.ju li.logging .Log;   32   import org .apache.ju li.logging .Log;
33   import org .apache.ju li.logging .LogFactor y;   33   import org .apache.ju li.logging .LogFactor y;
34   import org .apache.to mcat.util. http.parse r.Authoriz ation;   34   import org .apache.to mcat.util. http.parse r.Authoriz ation;
35   import org .apache.to mcat.util. security.C oncurrentM essageDige st;   35   import org .apache.to mcat.util. security.C oncurrentM essageDige st;
36   import org .apache.to mcat.util. security.M D5Encoder;   36   import org .apache.to mcat.util. security.M D5Encoder;
37     37  
38     38  
39   /**   39   /**
40    * An <b>A uthenticat or</b> and  <b>Valve< /b> implem entation o f HTTP DIG EST   40    * An <b>A uthenticat or</b> and  <b>Valve< /b> implem entation o f HTTP DIG EST
41    * Authent ication (s ee RFC 206 9).   41    * Authent ication (s ee RFC 206 9).
42    *   42    *
43    * @author  Craig R.  McClanahan   43    * @author  Craig R.  McClanahan
44    * @author  Remy Mauc herat   44    * @author  Remy Mauc herat
45    */   45    */
46   public cla ss DigestA uthenticat or extends  Authentic atorBase {   46   public cla ss DigestA uthenticat or extends  Authentic atorBase {
47     47  
48       privat e static f inal Log l og = LogFa ctory.getL og(DigestA uthenticat or.class);   48       privat e static f inal Log l og = LogFa ctory.getL og(DigestA uthenticat or.class);
49     49  
50     50  
51       // --- ---------- ---------- ---------- ---------- ---------- ---------  Constants   51       // --- ---------- ---------- ---------- ---------- ---------- ---------  Constants
52     52  
53       /**   53       /**
54        * Tom cat's DIGE ST impleme ntation on ly support s auth qua lity of pr otection.   54        * Tom cat's DIGE ST impleme ntation on ly support s auth qua lity of pr otection.
55        */   55        */
56       protec ted static  final Str ing QOP =  "auth";   56       protec ted static  final Str ing QOP =  "auth";
57     57  
58     58  
59       // --- ---------- ---------- ---------- ---------- ---------- ------ Con structors   59       // --- ---------- ---------- ---------- ---------- ---------- ------ Con structors
60     60  
61       public  DigestAut henticator () {   61       public  DigestAut henticator () {
62           su per();   62           su per();
63           se tCache(fal se);   63           se tCache(fal se);
64       }   64       }
65     65  
66     66  
67       // --- ---------- ---------- ---------- ---------- ----------  Instance  Variables   67       // --- ---------- ---------- ---------- ---------- ----------  Instance  Variables
68     68  
69       /**   69       /**
70        * Lis t of serve r nonce va lues curre ntly being  tracked   70        * Lis t of serve r nonce va lues curre ntly being  tracked
71        */   71        */
72       protec ted Map<St ring,Nonce Info> nonc es;   72       protec ted Map<St ring,Nonce Info> nonc es;
73     73  
74     74  
75       /**   75       /**
76        * The  last time stamp used  to genera te a nonce . Each non ce should  get a   76        * The  last time stamp used  to genera te a nonce . Each non ce should  get a
77        * uni que timest amp.   77        * uni que timest amp.
78        */   78        */
79       protec ted long l astTimesta mp = 0;   79       protec ted long l astTimesta mp = 0;
80       protec ted final  Object las tTimestamp Lock = new  Object();   80       protec ted final  Object las tTimestamp Lock = new  Object();
81     81  
82     82  
83       /**   83       /**
84        * Max imum numbe r of serve r nonces t o keep in  the cache.  If not sp ecified,   84        * Max imum numbe r of serve r nonces t o keep in  the cache.  If not sp ecified,
85        * the  default v alue of 10 00 is used .   85        * the  default v alue of 10 00 is used .
86        */   86        */
87       protec ted int no nceCacheSi ze = 1000;   87       protec ted int no nceCacheSi ze = 1000;
88     88  
89     89  
90       /**   90       /**
91        * The  window si ze to use  to track s een nonce  count valu es for a g iven   91        * The  window si ze to use  to track s een nonce  count valu es for a g iven
92        * non ce. If not  specified , the defa ult of 100  is used.   92        * non ce. If not  specified , the defa ult of 100  is used.
93        */   93        */
94       protec ted int no nceCountWi ndowSize =  100;   94       protec ted int no nceCountWi ndowSize =  100;
95     95  
96       /**   96       /**
97        * Pri vate key.   97        * Pri vate key.
98        */   98        */
99       protec ted String  key = nul l;   99       protec ted String  key = nul l;
100     100  
101     101  
102       /**   102       /**
103        * How  long serv er nonces  are valid  for in mil liseconds.  Defaults  to 5   103        * How  long serv er nonces  are valid  for in mil liseconds.  Defaults  to 5
104        * min utes.   104        * min utes.
105        */   105        */
106       protec ted long n onceValidi ty = 5 * 6 0 * 1000;   106       protec ted long n onceValidi ty = 5 * 6 0 * 1000;
107     107  
108     108  
109       /**   109       /**
110        * Opa que string .   110        * Opa que string .
111        */   111        */
112       protec ted String  opaque;   112       protec ted String  opaque;
113     113  
114     114  
115       /**   115       /**
116        * Sho uld the UR I be valid ated as re quired by  RFC2617? C an be disa bled in   116        * Sho uld the UR I be valid ated as re quired by  RFC2617? C an be disa bled in
117        * rev erse proxi es where t he proxy h as modifie d the URI.   117        * rev erse proxi es where t he proxy h as modifie d the URI.
118        */   118        */
119       protec ted boolea n validate Uri = true ;   119       protec ted boolea n validate Uri = true ;
120     120  
121       // --- ---------- ---------- ---------- ---------- ---------- -------- P roperties   121       // --- ---------- ---------- ---------- ---------- ---------- -------- P roperties
122     122  
123       public  int getNo nceCountWi ndowSize()  {   123       public  int getNo nceCountWi ndowSize()  {
124           re turn nonce CountWindo wSize;   124           re turn nonce CountWindo wSize;
125       }   125       }
126     126  
127     127  
128       public  void setN onceCountW indowSize( int nonceC ountWindow Size) {   128       public  void setN onceCountW indowSize( int nonceC ountWindow Size) {
129           th is.nonceCo untWindowS ize = nonc eCountWind owSize;   129           th is.nonceCo untWindowS ize = nonc eCountWind owSize;
130       }   130       }
131     131  
132     132  
133       public  int getNo nceCacheSi ze() {   133       public  int getNo nceCacheSi ze() {
134           re turn nonce CacheSize;   134           re turn nonce CacheSize;
135       }   135       }
136     136  
137     137  
138       public  void setN onceCacheS ize(int no nceCacheSi ze) {   138       public  void setN onceCacheS ize(int no nceCacheSi ze) {
139           th is.nonceCa cheSize =  nonceCache Size;   139           th is.nonceCa cheSize =  nonceCache Size;
140       }   140       }
141     141  
142     142  
143       public  String ge tKey() {   143       public  String ge tKey() {
144           re turn key;   144           re turn key;
145       }   145       }
146     146  
147     147  
148       public  void setK ey(String  key) {   148       public  void setK ey(String  key) {
149           th is.key = k ey;   149           th is.key = k ey;
150       }   150       }
151     151  
152     152  
153       public  long getN onceValidi ty() {   153       public  long getN onceValidi ty() {
154           re turn nonce Validity;   154           re turn nonce Validity;
155       }   155       }
156     156  
157     157  
158       public  void setN onceValidi ty(long no nceValidit y) {   158       public  void setN onceValidi ty(long no nceValidit y) {
159           th is.nonceVa lidity = n onceValidi ty;   159           th is.nonceVa lidity = n onceValidi ty;
160       }   160       }
161     161  
162     162  
163       public  String ge tOpaque()  {   163       public  String ge tOpaque()  {
164           re turn opaqu e;   164           re turn opaqu e;
165       }   165       }
166     166  
167     167  
168       public  void setO paque(Stri ng opaque)  {   168       public  void setO paque(Stri ng opaque)  {
169           th is.opaque  = opaque;   169           th is.opaque  = opaque;
170       }   170       }
171     171  
172     172  
173       public  boolean i sValidateU ri() {   173       public  boolean i sValidateU ri() {
174           re turn valid ateUri;   174           re turn valid ateUri;
175       }   175       }
176     176  
177     177  
178       public  void setV alidateUri (boolean v alidateUri ) {   178       public  void setV alidateUri (boolean v alidateUri ) {
179           th is.validat eUri = val idateUri;   179           th is.validat eUri = val idateUri;
180       }   180       }
181     181  
182     182  
183       // --- ---------- ---------- ---------- ---------- ---------- ---- Publi c Methods   183       // --- ---------- ---------- ---------- ---------- ---------- ---- Publi c Methods
184     184  
185       /**   185       /**
186        * Aut henticate  the user m aking this  request,  based on t he specifi ed   186        * Aut henticate  the user m aking this  request,  based on t he specifi ed
187        * log in configu ration.  R eturn <cod e>true</co de> if any  specified   187        * log in configu ration.  R eturn <cod e>true</co de> if any  specified
188        * con straint ha s been sat isfied, or  <code>fal se</code>  if we have   188        * con straint ha s been sat isfied, or  <code>fal se</code>  if we have
189        * cre ated a res ponse chal lenge alre ady.   189        * cre ated a res ponse chal lenge alre ady.
190        *   190        *
191        * @pa ram reques t Request  we are pro cessing   191        * @pa ram reques t Request  we are pro cessing
192        * @pa ram respon se Respons e we are c reating   192        * @pa ram respon se Respons e we are c reating
193        *   193        *
194        * @ex ception IO Exception  if an inpu t/output e rror occur s   194        * @ex ception IO Exception  if an inpu t/output e rror occur s
195        */   195        */
196       @Overr ide   196       @Overr ide
197         p ubli c
 boolean  a uthenticat e(Request  request, H ttpServlet Response r esponse)
  197         p rote c ted  boolean  doA uthenticat e(Request  request, H ttpServlet Response r esponse)
198                throws I OException  {   198                throws I OException  {
199     199  
200           //  NOTE: We  don't try  to reauthe nticate us ing any ex isting SSO  session,   200           //  NOTE: We  don't try  to reauthe nticate us ing any ex isting SSO  session,
201           //  because t hat will o nly work i f the orig inal authe ntication  was   201           //  because t hat will o nly work i f the orig inal authe ntication  was
202           //  BASIC or  FORM, whic h are less  secure th an the DIG EST auth-t ype   202           //  BASIC or  FORM, whic h are less  secure th an the DIG EST auth-t ype
203           //  specified  for this  webapp   203           //  specified  for this  webapp
204           //   204           //
205           //  Change to  true belo w to allow  previous  FORM or BA SIC authen tications   205           //  Change to  true belo w to allow  previous  FORM or BA SIC authen tications
206           //  to authen ticate use rs for thi s webapp   206           //  to authen ticate use rs for thi s webapp
207           //  TODO make  this a co nfigurable  attribute  (in Singl eSignOn??)   207           //  TODO make  this a co nfigurable  attribute  (in Singl eSignOn??)
208           if  (checkFor CachedAuth entication (request,  response,  false)) {   208           if  (checkFor CachedAuth entication (request,  response,  false)) {
209                return t rue;   209                return t rue;
210           }   210           }
211     211  
212           //  Validate  any creden tials alre ady includ ed with th is request   212           //  Validate  any creden tials alre ady includ ed with th is request
213           Pr incipal pr incipal =  null;   213           Pr incipal pr incipal =  null;
214           St ring autho rization =  request.g etHeader(" authorizat ion");   214           St ring autho rization =  request.g etHeader(" authorizat ion");
215           Di gestInfo d igestInfo  = new Dige stInfo(get Opaque(),  getNonceVa lidity(),   215           Di gestInfo d igestInfo  = new Dige stInfo(get Opaque(),  getNonceVa lidity(),
216                    getK ey(), nonc es, isVali dateUri()) ;   216                    getK ey(), nonc es, isVali dateUri()) ;
217           if  (authoriz ation != n ull) {   217           if  (authoriz ation != n ull) {
218                if (dige stInfo.par se(request , authoriz ation)) {   218                if (dige stInfo.par se(request , authoriz ation)) {
219                    if ( digestInfo .validate( request))  {   219                    if ( digestInfo .validate( request))  {
220                         principal  = digestIn fo.authent icate(cont ext.getRea lm());   220                         principal  = digestIn fo.authent icate(cont ext.getRea lm());
221                    }   221                    }
222     222  
223                    if ( principal  != null &&  !digestIn fo.isNonce Stale()) {   223                    if ( principal  != null &&  !digestIn fo.isNonce Stale()) {
224                         register(r equest, re sponse, pr incipal,   224                         register(r equest, re sponse, pr incipal,
225                                 Ht tpServletR equest.DIG EST_AUTH,   225                                 Ht tpServletR equest.DIG EST_AUTH,
226                                 di gestInfo.g etUsername (), null);   226                                 di gestInfo.g etUsername (), null);
227                         return tru e;   227                         return tru e;
228                    }   228                    }
229                }   229                }
230           }   230           }
231     231  
232           //  Send an " unauthoriz ed" respon se and an  appropriat e challeng e   232           //  Send an " unauthoriz ed" respon se and an  appropriat e challeng e
233     233  
234           //  Next, gen erate a no nce token  (that is a  token whi ch is supp osed   234           //  Next, gen erate a no nce token  (that is a  token whi ch is supp osed
235           //  to be uni que).   235           //  to be uni que).
236           St ring nonce  = generat eNonce(req uest);   236           St ring nonce  = generat eNonce(req uest);
237     237  
238           se tAuthentic ateHeader( request, r esponse, n once,   238           se tAuthentic ateHeader( request, r esponse, n once,
239                    prin cipal != n ull && dig estInfo.is NonceStale ());   239                    prin cipal != n ull && dig estInfo.is NonceStale ());
240           re sponse.sen dError(Htt pServletRe sponse.SC_ UNAUTHORIZ ED);   240           re sponse.sen dError(Htt pServletRe sponse.SC_ UNAUTHORIZ ED);
241           re turn false ;   241           re turn false ;
242       }   242       }
243     243  
244     244  
245       @Overr ide   245       @Overr ide
246       protec ted String  getAuthMe thod() {   246       protec ted String  getAuthMe thod() {
247           re turn HttpS ervletRequ est.DIGEST _AUTH;   247           re turn HttpS ervletRequ est.DIGEST _AUTH;
248       }   248       }
249     249  
250     250  
251       // --- ---------- ---------- ---------- ---------- ---------- - Protecte d Methods   251       // --- ---------- ---------- ---------- ---------- ---------- - Protecte d Methods
252     252  
253     253  
254       /**   254       /**
255        * Rem oves the q uotes on a  string. R FC2617 sta tes quotes  are optio nal for   255        * Rem oves the q uotes on a  string. R FC2617 sta tes quotes  are optio nal for
256        * all  parameter s except r ealm.   256        * all  parameter s except r ealm.
        257        *
        258        * @pa ram quoted String The  quoted st ring
        259        * @pa ram quotes Required < code>true< /code> if  quotes wer e required
        260        * @re turn The u nquoted st ring
257        */   261        */
258       protec ted static  String re moveQuotes (String qu otedString ,   262       protec ted static  String re moveQuotes (String qu otedString ,
259                                                boolean q uotesRequi red) {   263                                                boolean q uotesRequi red) {
260           // support bo th quoted  and non-qu oted   264           // support bo th quoted  and non-qu oted
261           if  (quotedSt ring.lengt h() > 0 &&  quotedStr ing.charAt (0) != '"'  &&   265           if  (quotedSt ring.lengt h() > 0 &&  quotedStr ing.charAt (0) != '"'  &&
262                    !quo tesRequire d) {   266                    !quo tesRequire d) {
263                return q uotedStrin g;   267                return q uotedStrin g;
264           }  else if (q uotedStrin g.length()  > 2) {   268           }  else if (q uotedStrin g.length()  > 2) {
265                return q uotedStrin g.substrin g(1, quote dString.le ngth() - 1 );   269                return q uotedStrin g.substrin g(1, quote dString.le ngth() - 1 );
266           }  else {   270           }  else {
267                return " ";   271                return " ";
268           }   272           }
269       }   273       }
270     274  
271       /**   275       /**
272        * Rem oves the q uotes on a  string.   276        * Rem oves the q uotes on a  string.
        277        *
        278        * @pa ram quoted String The  quoted st ring
        279        * @re turn The u nquoted st ring
273        */   280        */
274       protec ted static  String re moveQuotes (String qu otedString ) {   281       protec ted static  String re moveQuotes (String qu otedString ) {
275           re turn remov eQuotes(qu otedString , false);   282           re turn remov eQuotes(qu otedString , false);
276       }   283       }
277     284  
278       /**   285       /**
279        * Gen erate a un ique token . The toke n is gener ated accor ding to th e   286        * Gen erate a un ique token . The toke n is gener ated accor ding to th e
280        * fol lowing pat tern. NOnc eToken = B ase64 ( MD 5 ( client -IP ":"   287        * fol lowing pat tern. NOnc eToken = B ase64 ( MD 5 ( client -IP ":"
281        * tim e-stamp ": " private- key ) ).   288        * tim e-stamp ": " private- key ) ).
282        *   289        *
283        * @pa ram reques t HTTP Ser vlet reque st   290        * @pa ram reques t HTTP Ser vlet reque st
        291        * @re turn The g enerated n once
284        */   292        */
285       protec ted String  generateN once(Reque st request ) {   293       protec ted String  generateN once(Reque st request ) {
286     294  
287           lo ng current Time = Sys tem.curren tTimeMilli s();   295           lo ng current Time = Sys tem.curren tTimeMilli s();
288     296  
289           sy nchronized  (lastTime stampLock)  {   297           sy nchronized  (lastTime stampLock)  {
290                if (curr entTime >  lastTimest amp) {   298                if (curr entTime >  lastTimest amp) {
291                    last Timestamp  = currentT ime;   299                    last Timestamp  = currentT ime;
292                } else {   300                } else {
293                    curr entTime =  ++lastTime stamp;   301                    curr entTime =  ++lastTime stamp;
294                }   302                }
295           }   303           }
296     304  
297           St ring ipTim eKey =   305           St ring ipTim eKey =
298                request. getRemoteA ddr() + ": " + curren tTime + ": " + getKey ();   306                request. getRemoteA ddr() + ": " + curren tTime + ": " + getKey ();
299     307  
300           by te[] buffe r = Concur rentMessag eDigest.di gestMD5(   308           by te[] buffe r = Concur rentMessag eDigest.di gestMD5(
301                    ipTi meKey.getB ytes(Stand ardCharset s.ISO_8859 _1));   309                    ipTi meKey.getB ytes(Stand ardCharset s.ISO_8859 _1));
302           St ring nonce  = current Time + ":"  + MD5Enco der.encode (buffer);   310           St ring nonce  = current Time + ":"  + MD5Enco der.encode (buffer);
303     311  
304           No nceInfo in fo = new N onceInfo(c urrentTime , getNonce CountWindo wSize());   312           No nceInfo in fo = new N onceInfo(c urrentTime , getNonce CountWindo wSize());
305           sy nchronized  (nonces)  {   313           sy nchronized  (nonces)  {
306                nonces.p ut(nonce,  info);   314                nonces.p ut(nonce,  info);
307           }   315           }
308     316  
309           re turn nonce ;   317           re turn nonce ;
310       }   318       }
311     319  
312     320  
313       /**   321       /**
314        * Gen erates the  WWW-Authe nticate he ader.   322        * Gen erates the  WWW-Authe nticate he ader.
315        * <p>   323        * <p>
316        * The  header MU ST follow  this templ ate :   324        * The  header MU ST follow  this templ ate :
317        * <pr e>   325        * <pr e>
318        *       WWW-Auth enticate     = "WWW-A uthenticat e" ":" "Di gest"   326        *       WWW-Auth enticate     = "WWW-A uthenticat e" ":" "Di gest"
319        *                               digest -challenge   327        *                               digest -challenge
320        *   328        *
321        *       digest-c hallenge     = 1#( re alm | [ do main ] | n once |   329        *       digest-c hallenge     = 1#( re alm | [ do main ] | n once |
322        *                    [ dige st-opaque  ] |[ stale  ] | [ alg orithm ] )   330        *                    [ dige st-opaque  ] |[ stale  ] | [ alg orithm ] )
323        *   331        *
324        *       realm                 = "realm " "=" real m-value   332        *       realm                 = "realm " "=" real m-value
325        *       realm-va lue          = quoted -string   333        *       realm-va lue          = quoted -string
326        *       domain                = "domai n" "=" &lt ;"&gt; 1#U RI &lt;"&g t;   334        *       domain                = "domai n" "=" &lt ;"&gt; 1#U RI &lt;"&g t;
327        *       nonce                 = "nonce " "=" nonc e-value   335        *       nonce                 = "nonce " "=" nonc e-value
328        *       nonce-va lue          = quoted -string   336        *       nonce-va lue          = quoted -string
329        *       opaque                = "opaqu e" "=" quo ted-string   337        *       opaque                = "opaqu e" "=" quo ted-string
330        *       stale                 = "stale " "=" ( "t rue" | "fa lse" )   338        *       stale                 = "stale " "=" ( "t rue" | "fa lse" )
331        *       algorith m            = "algor ithm" "="  ( "MD5" |  token )   339        *       algorith m            = "algor ithm" "="  ( "MD5" |  token )
332        * </p re>   340        * </p re>
333        *   341        *
334        * @pa ram reques t HTTP Ser vlet reque st   342        * @pa ram reques t HTTP Ser vlet reque st
335        * @pa ram respon se HTTP Se rvlet resp onse   343        * @pa ram respon se HTTP Se rvlet resp onse
336        * @pa ram nonce  nonce toke n   344        * @pa ram nonce  nonce toke n
        345        * @pa ram isNonc eStale <co de>true</c ode> to ad d a stale  parameter
337        */   346        */
338       protec ted void s etAuthenti cateHeader (HttpServl etRequest  request,   347       protec ted void s etAuthenti cateHeader (HttpServl etRequest  request,
339                                                HttpServl etResponse  response,   348                                                HttpServl etResponse  response,
340                                                String no nce,   349                                                String no nce,
341                                                boolean i sNonceStal e) {   350                                                boolean i sNonceStal e) {
342     351  
343           St ring realm Name = get RealmName( context);   352           St ring realm Name = get RealmName( context);
344     353  
345           St ring authe nticateHea der;   354           St ring authe nticateHea der;
346           if  (isNonceS tale) {   355           if  (isNonceS tale) {
347                authenti cateHeader  = "Digest  realm=\""  + realmNa me + "\",  " +   356                authenti cateHeader  = "Digest  realm=\""  + realmNa me + "\",  " +
348                "qop=\""  + QOP + " \", nonce= \"" + nonc e + "\", "  + "opaque =\"" +   357                "qop=\""  + QOP + " \", nonce= \"" + nonc e + "\", "  + "opaque =\"" +
349                getOpaqu e() + "\",  stale=tru e";   358                getOpaqu e() + "\",  stale=tru e";
350           }  else {   359           }  else {
351                authenti cateHeader  = "Digest  realm=\""  + realmNa me + "\",  " +   360                authenti cateHeader  = "Digest  realm=\""  + realmNa me + "\",  " +
352                "qop=\""  + QOP + " \", nonce= \"" + nonc e + "\", "  + "opaque =\"" +   361                "qop=\""  + QOP + " \", nonce= \"" + nonc e + "\", "  + "opaque =\"" +
353                getOpaqu e() + "\"" ;   362                getOpaqu e() + "\"" ;
354           }   363           }
355     364  
356           re sponse.set Header(AUT H_HEADER_N AME, authe nticateHea der);   365           re sponse.set Header(AUT H_HEADER_N AME, authe nticateHea der);
357     366  
358       }   367       }
359     368  
360     369  
361       // --- ---------- ---------- ---------- ---------- ---------- -- Lifecyc le Methods   370       // --- ---------- ---------- ---------- ---------- ---------- -- Lifecyc le Methods
362     371  
363       @Overr ide   372       @Overr ide
364       protec ted synchr onized voi d startInt ernal() th rows Lifec ycleExcept ion {   373       protec ted synchr onized voi d startInt ernal() th rows Lifec ycleExcept ion {
365           su per.startI nternal();   374           su per.startI nternal();
366     375  
367           //  Generate  a random s ecret key   376           //  Generate  a random s ecret key
368           if  (getKey()  == null)  {   377           if  (getKey()  == null)  {
369                setKey(s essionIdGe nerator.ge nerateSess ionId());   378                setKey(s essionIdGe nerator.ge nerateSess ionId());
370           }   379           }
371     380  
372           //  Generate  the opaque  string th e same way   381           //  Generate  the opaque  string th e same way
373           if  (getOpaqu e() == nul l) {   382           if  (getOpaqu e() == nul l) {
374                setOpaqu e(sessionI dGenerator .generateS essionId() );   383                setOpaqu e(sessionI dGenerator .generateS essionId() );
375           }   384           }
376     385  
377           no nces = new  LinkedHas hMap<Strin g, DigestA uthenticat or.NonceIn fo>() {   386           no nces = new  LinkedHas hMap<Strin g, DigestA uthenticat or.NonceIn fo>() {
378     387  
379                private  static fin al long se rialVersio nUID = 1L;   388                private  static fin al long se rialVersio nUID = 1L;
380                private  static fin al long LO G_SUPPRESS _TIME = 5  * 60 * 100 0;   389                private  static fin al long LO G_SUPPRESS _TIME = 5  * 60 * 100 0;
381     390  
382                private  long lastL og = 0;   391                private  long lastL og = 0;
383     392  
384                @Overrid e   393                @Overrid e
385                protecte d boolean  removeElde stEntry(   394                protecte d boolean  removeElde stEntry(
386                         Map.Entry< String,Non ceInfo> el dest) {   395                         Map.Entry< String,Non ceInfo> el dest) {
387                    // T his is cal led from a  sync so k eep it sim ple   396                    // T his is cal led from a  sync so k eep it sim ple
388                    long  currentTi me = Syste m.currentT imeMillis( );   397                    long  currentTi me = Syste m.currentT imeMillis( );
389                    if ( size() > g etNonceCac heSize())  {   398                    if ( size() > g etNonceCac heSize())  {
390                         if (lastLo g < curren tTime &&   399                         if (lastLo g < curren tTime &&
391                                 cu rrentTime  - eldest.g etValue(). getTimesta mp() <   400                                 cu rrentTime  - eldest.g etValue(). getTimesta mp() <
392                                 ge tNonceVali dity()) {   401                                 ge tNonceVali dity()) {
393                             // Rep lay attack  is possib le   402                             // Rep lay attack  is possib le
394                             log.wa rn(sm.getS tring(   403                             log.wa rn(sm.getS tring(
395                                      "digestA uthenticat or.cacheRe move"));   404                                      "digestA uthenticat or.cacheRe move"));
396                             lastLo g = curren tTime + LO G_SUPPRESS _TIME;   405                             lastLo g = curren tTime + LO G_SUPPRESS _TIME;
397                         }   406                         }
398                         return tru e;   407                         return tru e;
399                    }   408                    }
400                    retu rn false;   409                    retu rn false;
401                }   410                }
402           };   411           };
403       }   412       }
404     413  
405         p r i vate  static cl ass Digest Info {   414         p ubl i c  static cl ass Digest Info {
406     415  
407           pr ivate fina l String o paque;   416           pr ivate fina l String o paque;
408           pr ivate fina l long non ceValidity ;   417           pr ivate fina l long non ceValidity ;
409           pr ivate fina l String k ey;   418           pr ivate fina l String k ey;
410           pr ivate fina l Map<Stri ng,NonceIn fo> nonces ;   419           pr ivate fina l Map<Stri ng,NonceIn fo> nonces ;
411           pr ivate bool ean valida teUri = tr ue;   420           pr ivate bool ean valida teUri = tr ue;
412     421  
413           pr ivate Stri ng userNam e = null;   422           pr ivate Stri ng userNam e = null;
414           pr ivate Stri ng method  = null;   423           pr ivate Stri ng method  = null;
415           pr ivate Stri ng uri = n ull;   424           pr ivate Stri ng uri = n ull;
416           pr ivate Stri ng respons e = null;   425           pr ivate Stri ng respons e = null;
417           pr ivate Stri ng nonce =  null;   426           pr ivate Stri ng nonce =  null;
418           pr ivate Stri ng nc = nu ll;   427           pr ivate Stri ng nc = nu ll;
419           pr ivate Stri ng cnonce  = null;   428           pr ivate Stri ng cnonce  = null;
420           pr ivate Stri ng realmNa me = null;   429           pr ivate Stri ng realmNa me = null;
421           pr ivate Stri ng qop = n ull;   430           pr ivate Stri ng qop = n ull;
422           pr ivate Stri ng opaqueR eceived =  null;   431           pr ivate Stri ng opaqueR eceived =  null;
423     432  
424           pr ivate bool ean nonceS tale = fal se;   433           pr ivate bool ean nonceS tale = fal se;
425     434  
426     435  
427           pu blic Diges tInfo(Stri ng opaque,  long nonc eValidity,  String ke y,   436           pu blic Diges tInfo(Stri ng opaque,  long nonc eValidity,  String ke y,
428                    Map< String,Non ceInfo> no nces, bool ean valida teUri) {   437                    Map< String,Non ceInfo> no nces, bool ean valida teUri) {
429                this.opa que = opaq ue;   438                this.opa que = opaq ue;
430                this.non ceValidity  = nonceVa lidity;   439                this.non ceValidity  = nonceVa lidity;
431                this.key  = key;   440                this.key  = key;
432                this.non ces = nonc es;   441                this.non ces = nonc es;
433                this.val idateUri =  validateU ri;   442                this.val idateUri =  validateU ri;
434           }   443           }
435     444  
436     445  
437           pu blic Strin g getUsern ame() {   446           pu blic Strin g getUsern ame() {
438                return u serName;   447                return u serName;
439           }   448           }
440     449  
441     450  
442           pu blic boole an parse(R equest req uest, Stri ng authori zation) {   451           pu blic boole an parse(R equest req uest, Stri ng authori zation) {
443                // Valid ate the au thorizatio n credenti als format   452                // Valid ate the au thorizatio n credenti als format
444                if (auth orization  == null) {   453                if (auth orization  == null) {
445                    retu rn false;   454                    retu rn false;
446                }   455                }
447     456  
448                Map<Stri ng,String>  directive s;   457                Map<Stri ng,String>  directive s;
449                try {   458                try {
450                    dire ctives = A uthorizati on.parseAu thorizatio nDigest(   459                    dire ctives = A uthorizati on.parseAu thorizatio nDigest(
451                             new St ringReader (authoriza tion));   460                             new St ringReader (authoriza tion));
452                } catch  (IOExcepti on e) {   461                } catch  (IOExcepti on e) {
453                    retu rn false;   462                    retu rn false;
454                }   463                }
455     464  
456                if (dire ctives ==  null) {   465                if (dire ctives ==  null) {
457                    retu rn false;   466                    retu rn false;
458                }   467                }
459     468  
460                method =  request.g etMethod() ;   469                method =  request.g etMethod() ;
461                userName  = directi ves.get("u sername");   470                userName  = directi ves.get("u sername");
462                realmNam e = direct ives.get(" realm");   471                realmNam e = direct ives.get(" realm");
463                nonce =  directives .get("nonc e");   472                nonce =  directives .get("nonc e");
464                nc = dir ectives.ge t("nc");   473                nc = dir ectives.ge t("nc");
465                cnonce =  directive s.get("cno nce");   474                cnonce =  directive s.get("cno nce");
466                qop = di rectives.g et("qop");   475                qop = di rectives.g et("qop");
467                uri = di rectives.g et("uri");   476                uri = di rectives.g et("uri");
468                response  = directi ves.get("r esponse");   477                response  = directi ves.get("r esponse");
469                opaqueRe ceived = d irectives. get("opaqu e");   478                opaqueRe ceived = d irectives. get("opaqu e");
470     479  
471                return t rue;   480                return t rue;
472           }   481           }
473     482  
474           pu blic boole an validat e(Request  request) {   483           pu blic boole an validat e(Request  request) {
475                if ( (us erName ==  null) || ( realmName  == null) | | (nonce = = null)   484                if ( (us erName ==  null) || ( realmName  == null) | | (nonce = = null)
476                     ||  (uri == nu ll) || (re sponse ==  null) ) {   485                     ||  (uri == nu ll) || (re sponse ==  null) ) {
477                    retu rn false;   486                    retu rn false;
478                }   487                }
479     488  
480                // Valid ate the UR I - should  match the  request l ine sent b y client   489                // Valid ate the UR I - should  match the  request l ine sent b y client
481                if (vali dateUri) {   490                if (vali dateUri) {
482                    Stri ng uriQuer y;   491                    Stri ng uriQuer y;
483                    Stri ng query =  request.g etQueryStr ing();   492                    Stri ng query =  request.g etQueryStr ing();
484                    if ( query == n ull) {   493                    if ( query == n ull) {
485                         uriQuery =  request.g etRequestU RI();   494                         uriQuery =  request.g etRequestU RI();
486                    } el se {   495                    } el se {
487                         uriQuery =  request.g etRequestU RI() + "?"  + query;   496                         uriQuery =  request.g etRequestU RI() + "?"  + query;
488                    }   497                    }
489                    if ( !uri.equal s(uriQuery )) {   498                    if ( !uri.equal s(uriQuery )) {
490                         // Some cl ients (old er Android ) use an a bsolute UR I for   499                         // Some cl ients (old er Android ) use an a bsolute UR I for
491                         // DIGEST  but a rela tive URI i n the requ est line.   500                         // DIGEST  but a rela tive URI i n the requ est line.
492                         // request . 2.3.5 <  fixed Andr oid versio n <= 4.0.3   501                         // request . 2.3.5 <  fixed Andr oid versio n <= 4.0.3
493                         String hos t = reques t.getHeade r("host");   502                         String hos t = reques t.getHeade r("host");
494                         String sch eme = requ est.getSch eme();   503                         String sch eme = requ est.getSch eme();
495                         if (host ! = null &&  !uriQuery. startsWith (scheme))  {   504                         if (host ! = null &&  !uriQuery. startsWith (scheme))  {
496                             String Builder ab solute = n ew StringB uilder();   505                             String Builder ab solute = n ew StringB uilder();
497                             absolu te.append( scheme);   506                             absolu te.append( scheme);
498                             absolu te.append( "://");   507                             absolu te.append( "://");
499                             absolu te.append( host);   508                             absolu te.append( host);
500                             absolu te.append( uriQuery);   509                             absolu te.append( uriQuery);
501                             if (!u ri.equals( absolute.t oString()) ) {   510                             if (!u ri.equals( absolute.t oString()) ) {
502                                 re turn false ;   511                                 re turn false ;
503                             }   512                             }
504                         } else {   513                         } else {
505                             return  false;   514                             return  false;
506                         }   515                         }
507                    }   516                    }
508                }   517                }
509     518  
510                // Valid ate the Re alm name   519                // Valid ate the Re alm name
511                String l cRealm = g etRealmNam e(request. getContext ());   520                String l cRealm = g etRealmNam e(request. getContext ());
512                if (!lcR ealm.equal s(realmNam e)) {   521                if (!lcR ealm.equal s(realmNam e)) {
513                    retu rn false;   522                    retu rn false;
514                }   523                }
515     524  
516                // Valid ate the op aque strin g   525                // Valid ate the op aque strin g
517                if (!opa que.equals (opaqueRec eived)) {   526                if (!opa que.equals (opaqueRec eived)) {
518                    retu rn false;   527                    retu rn false;
519                }   528                }
520     529  
521                // Valid ate nonce   530                // Valid ate nonce
522                int i =  nonce.inde xOf(':');   531                int i =  nonce.inde xOf(':');
523                if (i <  0 || (i +  1) == nonc e.length() ) {   532                if (i <  0 || (i +  1) == nonc e.length() ) {
524                    retu rn false;   533                    retu rn false;
525                }   534                }
526                long non ceTime;   535                long non ceTime;
527                try {   536                try {
528                    nonc eTime = Lo ng.parseLo ng(nonce.s ubstring(0 , i));   537                    nonc eTime = Lo ng.parseLo ng(nonce.s ubstring(0 , i));
529                } catch  (NumberFor matExcepti on nfe) {   538                } catch  (NumberFor matExcepti on nfe) {
530                    retu rn false;   539                    retu rn false;
531                }   540                }
532                String m d5clientIp TimeKey =  nonce.subs tring(i +  1);   541                String m d5clientIp TimeKey =  nonce.subs tring(i +  1);
533                long cur rentTime =  System.cu rrentTimeM illis();   542                long cur rentTime =  System.cu rrentTimeM illis();
534                if ((cur rentTime -  nonceTime ) > nonceV alidity) {   543                if ((cur rentTime -  nonceTime ) > nonceV alidity) {
535                    nonc eStale = t rue;   544                    nonc eStale = t rue;
536                    sync hronized ( nonces) {   545                    sync hronized ( nonces) {
537                         nonces.rem ove(nonce) ;   546                         nonces.rem ove(nonce) ;
538                    }   547                    }
539                }   548                }
540                String s erverIpTim eKey =   549                String s erverIpTim eKey =
541                    requ est.getRem oteAddr()  + ":" + no nceTime +  ":" + key;   550                    requ est.getRem oteAddr()  + ":" + no nceTime +  ":" + key;
542                byte[] b uffer = Co ncurrentMe ssageDiges t.digestMD 5(   551                byte[] b uffer = Co ncurrentMe ssageDiges t.digestMD 5(
543                         serverIpTi meKey.getB ytes(Stand ardCharset s.ISO_8859 _1));   552                         serverIpTi meKey.getB ytes(Stand ardCharset s.ISO_8859 _1));
544                String m d5ServerIp TimeKey =  MD5Encoder .encode(bu ffer);   553                String m d5ServerIp TimeKey =  MD5Encoder .encode(bu ffer);
545                if (!md5 ServerIpTi meKey.equa ls(md5clie ntIpTimeKe y)) {   554                if (!md5 ServerIpTi meKey.equa ls(md5clie ntIpTimeKe y)) {
546                    retu rn false;   555                    retu rn false;
547                }   556                }
548     557  
549                // Valid ate qop   558                // Valid ate qop
550                if (qop  != null &&  !QOP.equa ls(qop)) {   559                if (qop  != null &&  !QOP.equa ls(qop)) {
551                    retu rn false;   560                    retu rn false;
552                }   561                }
553     562  
554                // Valid ate cnonce  and nc   563                // Valid ate cnonce  and nc
555                // Check  if presen ce of nc a nd Cnonce  is consist ent with p resence of  qop   564                // Check  if presen ce of nc a nd Cnonce  is consist ent with p resence of  qop
556                if (qop  == null) {   565                if (qop  == null) {
557                    if ( cnonce !=  null || nc  != null)  {   566                    if ( cnonce !=  null || nc  != null)  {
558                         return fal se;   567                         return fal se;
559                    }   568                    }
560                } else {   569                } else {
561                    if ( cnonce ==  null || nc  == null)  {   570                    if ( cnonce ==  null || nc  == null)  {
562                         return fal se;   571                         return fal se;
563                    }   572                    }
564                    // R FC 2617 sa ys nc must  be 8 digi ts long. O lder Andro id clients   573                    // R FC 2617 sa ys nc must  be 8 digi ts long. O lder Andro id clients
565                    // u se 6. 2.3. 5 < fixed  Android ve rsion <= 4 .0.3   574                    // u se 6. 2.3. 5 < fixed  Android ve rsion <= 4 .0.3
566                    if ( nc.length( ) < 6 || n c.length()  > 8) {   575                    if ( nc.length( ) < 6 || n c.length()  > 8) {
567                         return fal se;   576                         return fal se;
568                    }   577                    }
569                    long  count;   578                    long  count;
570                    try  {   579                    try  {
571                         count = Lo ng.parseLo ng(nc, 16) ;   580                         count = Lo ng.parseLo ng(nc, 16) ;
572                    } ca tch (Numbe rFormatExc eption nfe ) {   581                    } ca tch (Numbe rFormatExc eption nfe ) {
573                         return fal se;   582                         return fal se;
574                    }   583                    }
575                    Nonc eInfo info ;   584                    Nonc eInfo info ;
576                    sync hronized ( nonces) {   585                    sync hronized ( nonces) {
577                         info = non ces.get(no nce);   586                         info = non ces.get(no nce);
578                    }   587                    }
579                    if ( info == nu ll) {   588                    if ( info == nu ll) {
580                         // Nonce i s valid bu t not in c ache. It m ust have d ropped out   589                         // Nonce i s valid bu t not in c ache. It m ust have d ropped out
581                         // of the  cache - fo rce a re-a uthenticat ion   590                         // of the  cache - fo rce a re-a uthenticat ion
582                         nonceStale  = true;   591                         nonceStale  = true;
583                    } el se {   592                    } el se {
584                         if (!info. nonceCount Valid(coun t)) {   593                         if (!info. nonceCount Valid(coun t)) {
585                             return  false;   594                             return  false;
586                         }   595                         }
587                    }   596                    }
588                }   597                }
589                return t rue;   598                return t rue;
590           }   599           }
591     600  
592           pu blic boole an isNonce Stale() {   601           pu blic boole an isNonce Stale() {
593                return n onceStale;   602                return n onceStale;
594           }   603           }
595     604  
596           pu blic Princ ipal authe nticate(Re alm realm)  {   605           pu blic Princ ipal authe nticate(Re alm realm)  {
597                // Secon d MD5 dige st used to  calculate  the diges t :   606                // Secon d MD5 dige st used to  calculate  the diges t :
598                // MD5(M ethod + ": " + uri)   607                // MD5(M ethod + ": " + uri)
599                String a 2 = method  + ":" + u ri;   608                String a 2 = method  + ":" + u ri;
600     609  
601                byte[] b uffer = Co ncurrentMe ssageDiges t.digestMD 5(   610                byte[] b uffer = Co ncurrentMe ssageDiges t.digestMD 5(
602                         a2.getByte s(Standard Charsets.I SO_8859_1) );   611                         a2.getByte s(Standard Charsets.I SO_8859_1) );
603                String m d5a2 = MD5 Encoder.en code(buffe r);   612                String m d5a2 = MD5 Encoder.en code(buffe r);
604     613  
605                return r ealm.authe nticate(us erName, re sponse, no nce, nc, c nonce,   614                return r ealm.authe nticate(us erName, re sponse, no nce, nc, c nonce,
606                         qop, realm Name, md5a 2);   615                         qop, realm Name, md5a 2);
607           }   616           }
608     617  
609       }   618       }
610     619  
611         p r i vate  static cl ass NonceI nfo {   620         p ubl i c  static cl ass NonceI nfo {
612           pr ivate fina l long tim estamp;   621           pr ivate fina l long tim estamp;
613           pr ivate fina l boolean  seen[];   622           pr ivate fina l boolean  seen[];
614           pr ivate fina l int offs et;   623           pr ivate fina l int offs et;
615           pr ivate int  count = 0;   624           pr ivate int  count = 0;
616     625  
617           pu blic Nonce Info(long  currentTim e, int see nWindowSiz e) {   626           pu blic Nonce Info(long  currentTim e, int see nWindowSiz e) {
618                this.tim estamp = c urrentTime ;   627                this.tim estamp = c urrentTime ;
619                seen = n ew boolean [seenWindo wSize];   628                seen = n ew boolean [seenWindo wSize];
620                offset =  seenWindo wSize / 2;   629                offset =  seenWindo wSize / 2;
621           }   630           }
622     631  
623           pu blic synch ronized bo olean nonc eCountVali d(long non ceCount) {   632           pu blic synch ronized bo olean nonc eCountVali d(long non ceCount) {
624                if ((cou nt - offse t) >= nonc eCount ||   633                if ((cou nt - offse t) >= nonc eCount ||
625                         (nonceCoun t > count  - offset +  seen.leng th)) {   634                         (nonceCoun t > count  - offset +  seen.leng th)) {
626                    retu rn false;   635                    retu rn false;
627                }   636                }
628                int chec kIndex = ( int) ((non ceCount +  offset) %  seen.lengt h);   637                int chec kIndex = ( int) ((non ceCount +  offset) %  seen.lengt h);
629                if (seen [checkInde x]) {   638                if (seen [checkInde x]) {
630                    retu rn false;   639                    retu rn false;
631                } else {   640                } else {
632                    seen [checkInde x] = true;   641                    seen [checkInde x] = true;
633                    seen [count % s een.length ] = false;   642                    seen [count % s een.length ] = false;
634                    coun t++;   643                    coun t++;
635                    retu rn true;   644                    retu rn true;
636                }   645                }
637           }   646           }
638     647  
639           pu blic long  getTimesta mp() {   648           pu blic long  getTimesta mp() {
640                return t imestamp;   649                return t imestamp;
641           }   650           }
642       }   651       }
643   }   652   }