View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.spdy.parser;
20  
21  import java.io.ByteArrayInputStream;
22  import java.nio.ByteBuffer;
23  import java.security.cert.Certificate;
24  import java.security.cert.CertificateException;
25  import java.security.cert.CertificateFactory;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.List;
29  
30  import org.eclipse.jetty.spdy.SessionException;
31  import org.eclipse.jetty.spdy.api.SessionStatus;
32  import org.eclipse.jetty.spdy.frames.ControlFrameType;
33  import org.eclipse.jetty.spdy.frames.CredentialFrame;
34  
35  public class CredentialBodyParser extends ControlFrameBodyParser
36  {
37      private final List<Certificate> certificates = new ArrayList<>();
38      private final ControlFrameParser controlFrameParser;
39      private State state = State.SLOT;
40      private int totalLength;
41      private int cursor;
42      private short slot;
43      private int proofLength;
44      private byte[] proof;
45      private int certificateLength;
46      private byte[] certificate;
47  
48      public CredentialBodyParser(ControlFrameParser controlFrameParser)
49      {
50          this.controlFrameParser = controlFrameParser;
51      }
52  
53      @Override
54      public boolean parse(ByteBuffer buffer)
55      {
56          while (buffer.hasRemaining())
57          {
58              switch (state)
59              {
60                  case SLOT:
61                  {
62                      if (buffer.remaining() >= 2)
63                      {
64                          slot = buffer.getShort();
65                          checkSlotValid();
66                          state = State.PROOF_LENGTH;
67                      }
68                      else
69                      {
70                          state = State.SLOT_BYTES;
71                          cursor = 2;
72                      }
73                      break;
74                  }
75                  case SLOT_BYTES:
76                  {
77                      byte currByte = buffer.get();
78                      --cursor;
79                      slot += (currByte & 0xFF) << 8 * cursor;
80                      if (cursor == 0)
81                      {
82                          checkSlotValid();
83                          state = State.PROOF_LENGTH;
84                      }
85                      break;
86                  }
87                  case PROOF_LENGTH:
88                  {
89                      if (buffer.remaining() >= 4)
90                      {
91                          proofLength = buffer.getInt() & 0x7F_FF_FF_FF;
92                          state = State.PROOF;
93                      }
94                      else
95                      {
96                          state = State.PROOF_LENGTH_BYTES;
97                          cursor = 4;
98                      }
99                      break;
100                 }
101                 case PROOF_LENGTH_BYTES:
102                 {
103                     byte currByte = buffer.get();
104                     --cursor;
105                     proofLength += (currByte & 0xFF) << 8 * cursor;
106                     if (cursor == 0)
107                     {
108                         proofLength &= 0x7F_FF_FF_FF;
109                         state = State.PROOF;
110                     }
111                     break;
112                 }
113                 case PROOF:
114                 {
115                     totalLength = controlFrameParser.getLength() - 2 - 4 - proofLength;
116                     proof = new byte[proofLength];
117                     if (buffer.remaining() >= proofLength)
118                     {
119                         buffer.get(proof);
120                         state = State.CERTIFICATE_LENGTH;
121                         if (totalLength == 0)
122                         {
123                             onCredential();
124                             return true;
125                         }
126                     }
127                     else
128                     {
129                         state = State.PROOF_BYTES;
130                         cursor = proofLength;
131                     }
132                     break;
133                 }
134                 case PROOF_BYTES:
135                 {
136                     proof[proofLength - cursor] = buffer.get();
137                     --cursor;
138                     if (cursor == 0)
139                     {
140                         state = State.CERTIFICATE_LENGTH;
141                         if (totalLength == 0)
142                         {
143                             onCredential();
144                             return true;
145                         }
146                     }
147                     break;
148                 }
149                 case CERTIFICATE_LENGTH:
150                 {
151                     if (buffer.remaining() >= 4)
152                     {
153                         certificateLength = buffer.getInt() & 0x7F_FF_FF_FF;
154                         state = State.CERTIFICATE;
155                     }
156                     else
157                     {
158                         state = State.CERTIFICATE_LENGTH_BYTES;
159                         cursor = 4;
160                     }
161                     break;
162                 }
163                 case CERTIFICATE_LENGTH_BYTES:
164                 {
165                     byte currByte = buffer.get();
166                     --cursor;
167                     certificateLength += (currByte & 0xFF) << 8 * cursor;
168                     if (cursor == 0)
169                     {
170                         certificateLength &= 0x7F_FF_FF_FF;
171                         state = State.CERTIFICATE;
172                     }
173                     break;
174                 }
175                 case CERTIFICATE:
176                 {
177                     totalLength -= 4 + certificateLength;
178                     certificate = new byte[certificateLength];
179                     if (buffer.remaining() >= certificateLength)
180                     {
181                         buffer.get(certificate);
182                         if (onCertificate())
183                             return true;
184                     }
185                     else
186                     {
187                         state = State.CERTIFICATE_BYTES;
188                         cursor = certificateLength;
189                     }
190                     break;
191                 }
192                 case CERTIFICATE_BYTES:
193                 {
194                     certificate[certificateLength - cursor] = buffer.get();
195                     --cursor;
196                     if (cursor == 0)
197                     {
198                         if (onCertificate())
199                             return true;
200                     }
201                     break;
202                 }
203                 default:
204                 {
205                     throw new IllegalStateException();
206                 }
207             }
208         }
209         return false;
210     }
211 
212     private void checkSlotValid()
213     {
214         if (slot <= 0)
215             throw new SessionException(SessionStatus.PROTOCOL_ERROR,
216                     "Invalid slot " + slot + " for " + ControlFrameType.CREDENTIAL + " frame");
217     }
218 
219     private boolean onCertificate()
220     {
221         certificates.add(deserializeCertificate(certificate));
222         if (totalLength == 0)
223         {
224             onCredential();
225             return true;
226         }
227         else
228         {
229             certificateLength = 0;
230             state = State.CERTIFICATE_LENGTH;
231         }
232         return false;
233     }
234 
235     private Certificate deserializeCertificate(byte[] bytes)
236     {
237         try
238         {
239             CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
240             return certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
241         }
242         catch (CertificateException x)
243         {
244             throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
245         }
246     }
247 
248     private void onCredential()
249     {
250         CredentialFrame frame = new CredentialFrame(controlFrameParser.getVersion(), slot,
251                 Arrays.copyOf(proof, proof.length), certificates.toArray(new Certificate[certificates.size()]));
252         controlFrameParser.onControlFrame(frame);
253         reset();
254     }
255 
256     private void reset()
257     {
258         state = State.SLOT;
259         totalLength = 0;
260         cursor = 0;
261         slot = 0;
262         proofLength = 0;
263         proof = null;
264         certificateLength = 0;
265         certificate = null;
266         certificates.clear();
267     }
268 
269     public enum State
270     {
271         SLOT, SLOT_BYTES, PROOF_LENGTH, PROOF_LENGTH_BYTES, PROOF, PROOF_BYTES,
272         CERTIFICATE_LENGTH, CERTIFICATE_LENGTH_BYTES, CERTIFICATE, CERTIFICATE_BYTES
273     }
274 }