1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 }