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