1 package jsdsi;
2
3 import java.security.InvalidKeyException;
4 import java.security.NoSuchAlgorithmException;
5 import java.security.NoSuchProviderException;
6 import java.security.SignatureException;
7 import java.security.cert.CertificateException;
8 import java.util.Arrays;
9 import java.util.ArrayList;
10 import java.util.Iterator;
11 import java.util.List;
12
13 import jsdsi.sexp.Sexp;
14 import jsdsi.sexp.SexpList;
15 import jsdsi.sexp.SexpParseException;
16 import jsdsi.sexp.SexpUtil;
17
18 /***
19 * A statement (a <code>Cert</code>) and a sequence of
20 * <code>Certificates</code> (<code>Certs</code> +
21 * <code>validators</code>) that proves that the statement holds.
22 * Proofs are self-validating and can be composed to create new proofs.
23 *
24 * @see Certificate
25 * @see Cert
26 *
27 * @author Sameer Ajmani
28 * @author Sean Radford
29 * @version $Revision: 1.6.2.1 $ $Date: 2005/11/08 03:12:52 $
30 */
31 public class Proof extends Obj {
32
33 private static final long serialVersionUID = -1084625036476163346L;
34
35 /***
36 * New Exception used by methods in this class.
37 *
38 * @author Sameer Ajmani
39 */
40 public static class IncompatibleException extends Exception {
41 private static final long serialVersionUID = -8565230129462618920L;
42 /***
43 * @see java.lang.Throwable#Throwable(String)
44 */
45 IncompatibleException(String message) {
46 super(message);
47 }
48 }
49
50 /***
51 * The statement to prove.
52 */
53 private transient Cert cert;
54
55 /***
56 * The certificates that prove it.
57 */
58 private transient Certificate[] certs;
59
60 /***
61 * Creates a new proof from a given <code>Certificate</code>.
62 *
63 * @param c <code>Certificate</code> to create the <code>Proof</code>
64 * from.
65 */
66 public Proof(Certificate c) {
67 assert(c != null) : "null certificate";
68 cert = c.getCert();
69 certs = new Certificate[] { c };
70 }
71
72 /***
73 * Creates a new proof from a <code>Cert</code> and an array of
74 * <code>Certificate</code>s.
75 *
76 * @param c <code>Cert</code> to create the proof from.
77 * @param cs array of <code>Certificate</code> to create the proof from.
78 */
79 private Proof(Cert c, Certificate[] cs) {
80 assert(c != null) : "null cert";
81 assert(cs != null) : "null certificates";
82 cert = c;
83 certs = cs;
84 }
85
86 /***
87 * @return the <code>Cert</code> of this <code>Proof</code>.
88 */
89 public Cert getCert() {
90 return cert;
91 }
92
93 /***
94 * @return an array of <code>Certificate</code>s
95 * of this <code>Proof</code>.
96 */
97 public Certificate[] getCertificates() {
98 return certs;
99 }
100
101 /***
102 * @return a <code>Sequence</code> of this <code>certs</code>' elements.
103 */
104 public Sequence getSequence() {
105 List elems = new ArrayList();
106 for (int i = 0; i < certs.length; i++) {
107 certs[i].toElements(elems);
108 }
109 Element[] es = new Element[elems.size()];
110 return new Sequence((Element[]) elems.toArray(es));
111 }
112
113 /***
114 * @see java.lang.Object#equals(Object)
115 */
116 public boolean equals(Object o) {
117 if (o instanceof Proof) {
118 Proof p = (Proof) o;
119 return cert.equals(p.cert) && Util.equals(certs, p.certs);
120 }
121 return false;
122 }
123
124 /***
125 * @see java.lang.Object#hashCode()
126 */
127 public int hashCode() {
128 return cert.hashCode() ^ Util.hashCode(certs);
129 }
130
131 public SexpList toSexp() {
132 Sexp[] ss = new Sexp[2];
133 ss[0] = getCert().toSexp();
134 ss[1] = getSequence().toSexp();
135 return SexpUtil.toSexp("proof", ss);
136 }
137
138 static Proof parseProof(SexpList l) throws SexpParseException {
139 Iterator pbody = SexpUtil.getBody(l);
140 Cert cert = Cert.parseCert
141 (SexpUtil.getNextList(pbody, "cert"));
142 Sequence seq = Sequence.parseSequence
143 (SexpUtil.getNextList(pbody, "sequence"));
144 SexpUtil.checkDone(pbody, "proof"); // sanity check
145 // convert the sequence to and array of Certificates
146 Iterator elms = Arrays.asList(seq.getElements()).iterator();
147 List certs = new ArrayList();
148 while (elms.hasNext()) {
149 try {
150 certs.add(Certificate.fromElements(elms));
151 } catch (CertificateException e) {
152 throw new SexpParseException(e);
153 }
154 }
155 return new Proof
156 (cert, (Certificate[])certs.toArray(new Certificate[0]));
157 }
158
159 /***
160 * Verifies this proof.
161 *
162 * @see java.security.cert.Certificate#verify(java.security.PublicKey)
163 *
164 * @throws CertificateException if there are no certificates in this
165 * <code>Proof</code> or they don't match/compose correctly.
166 * @throws NoSuchAlgorithmException if an unknown crypto algorithm
167 * has been used.
168 * @throws InvalidKeyException if a key in the certificate is not correct.
169 * @throws NoSuchProviderException if there is no security provider for
170 * this algorithm.
171 * @throws SignatureException if an error occured with a signature.
172 */
173 public void verify()
174 throws CertificateException,
175 NoSuchAlgorithmException,
176 InvalidKeyException,
177 NoSuchProviderException,
178 SignatureException {
179 if (certs.length == 0) {
180 throw new CertificateException("no certificates");
181 }
182 certs[0].verify(certs[0].getPublicKey());
183 Proof p = new Proof(certs[0]);
184 try {
185 for (int i = 1; i < certs.length; i++) {
186 certs[i].verify(certs[i].getPublicKey());
187 p = p.compose(new Proof(certs[i]));
188 }
189 } catch (IncompatibleException e) {
190 throw (CertificateException)
191 new CertificateException
192 ("certificates do not compose correctly")
193 .initCause(e);
194 }
195 if (!p.getCert().equals(cert)) {
196 throw new CertificateException
197 ("composed certificates do not match proof");
198 }
199 }
200
201 /***
202 * Replaces the prefix of lhName equal to rhName with rhSubject, and
203 * returns the resulting new name.
204 *
205 * @see Name#prefixOf(Name)
206 *
207 * @param lhName <code>Name</code> to compose with <code>rhName</code>.
208 * @param rhName <code>Name</code> to compose with <code>lhName</code>.
209 * (should be a prefix of <code>lhName</code>)
210 * @param rhSubject <code>Pincipal</code> for
211 * @return a new <code>Name</code> if <code>rhName</code> is a prefix of
212 * <code>lhName</code> and bot hare not equal, or
213 * <code>rhSubject</code> if both names are equal.
214 * @throws IncompatibleException if <code>rhName</code> is not a prefix
215 * of <code>lhName</code> or another problem occurs with both
216 * names.
217 */
218 private Subject composeNames(Name lhName, Name rhName, Subject rhSubject)
219 throws IncompatibleException {
220 if (!rhName.prefixOf(lhName)) {
221 throw new IncompatibleException("Cannot compose "
222 + lhName + " with " + rhName);
223 }
224 int diff = lhName.getNames().length - rhName.getNames().length;
225 if (diff == 0) {
226 return rhSubject;
227 }
228 assert(diff > 0) : "expected righthand name length <= lefthand";
229 if (!(rhSubject instanceof Principal)) {
230 throw new IncompatibleException(
231 "Cannot compose name with non-reducing cert");
232 }
233 // replace rhName with rhSubject in lhName
234 String[] names = new String[diff];
235 int off = lhName.getNames().length - diff;
236 for (int i = 0; i < names.length; i++) {
237 names[i] = lhName.getNames()[off + i];
238 }
239 return new Name((Principal) rhSubject, names);
240 }
241
242 /***
243 * Tries to compose a name certificate with a given <code>NameCert</code>
244 * by composing a new name from both names and the subject of the second
245 * name certificate.
246 *
247 * @param lhs name certificate to compose with <code>rhs</code>.
248 * @param rhs name certificate to compose with <code>lhs</code>.
249 * @return a new name from the composition of both names or the
250 * subject of <code>rhs</code> if both names are equal.
251 * @throws IncompatibleException if <code>lhs</code> is not a
252 * <code>Name</code>.
253 */
254 private Subject composeWithNameCert(Cert lhs, NameCert rhs)
255 throws IncompatibleException {
256 if (!(lhs.getSubject() instanceof Name)) {
257 throw new IncompatibleException(
258 "Cannot compose reducing Cert with NameCert");
259 }
260 Name lhName = (Name) lhs.getSubject();
261 // TODO: allow variable-length names on RHS
262 Name rhName = rhs.getFullName();
263 return composeNames(lhName, rhName, rhs.getSubject());
264 }
265
266 /***
267 * Intersects to validities and returns the result.
268 *
269 * @param v1 validity to intersect with the other.
270 * @param v2 validity to intersect with the other.
271 * @return the intersection of <code>v1</code> and <code>v2</code>.
272 */
273 private Validity intersect(Validity v1, Validity v2) {
274 if (v1 == null) {
275 return v2;
276 }
277 if (v2 == null) {
278 return v1;
279 }
280 return v1.intersect(v2);
281 }
282
283 /***
284 * Composes to given names and creates a new <code>NameCert</code> from it.
285 *
286 * @param lhs first name to inersect.
287 * @param rhs second name to intersect.
288 * @return a new <code>NameCert</code> composed of <code>lhs</code> and
289 * <code>rhs</code>.
290 * @throws IncompatibleException if <code>lhs</code> is not a proper name
291 * (should never happen!).
292 */
293 private NameCert composeNameName(NameCert lhs, NameCert rhs)
294 throws IncompatibleException {
295 return new NameCert(
296 lhs.getIssuer(),
297 composeWithNameCert(lhs, rhs),
298 intersect(lhs.getValidity(), rhs.getValidity()),
299 lhs.getDisplay(),
300 lhs.getComment(),
301 lhs.getName());
302 }
303
304 /***
305 * Creates a new <code>AuthCert</code> from a given <code>AuthCert</code>
306 * and <code>NameCert</code>.
307 *
308 * @param lhs <code>AuthCert</code> to compose with <code>rhs</code>.
309 * @param rhs <code>NameCert</code> to compose with <code>lhs</code>.
310 * @return a new <code>AuthCert</code> composed from <code>lhs</code>
311 * and <code>rhs</code>.
312 * @throws IncompatibleException if <code>lhs</code> is not a proper name
313 * (should never happen!).
314 */
315 private AuthCert composeAuthName(AuthCert lhs, NameCert rhs)
316 throws IncompatibleException {
317 return new AuthCert(
318 lhs.getIssuer(),
319 composeWithNameCert(lhs, rhs),
320 intersect(lhs.getValidity(), rhs.getValidity()),
321 lhs.getDisplay(),
322 lhs.getComment(),
323 lhs.getTag(),
324 lhs.getPropagate());
325 }
326
327 /***
328 * Composes one <code>AuthCert</code> with another if the delegation bit
329 * in the first auth certificate is set.
330 *
331 * @param lhs <code>AuthCert</code> to compose with <code>rhs</code>.
332 * @param rhs <code>AuthCert</code> that should be composed with
333 * <code>lhs</code>.
334 * @return a new <code>AuthCert</code> from <code>lhs</code> and
335 * <code>rhs</code>.
336 * @throws IncompatibleException if the delegation bit in <code>lhs</code>
337 * is not set.
338 */
339 private AuthCert composeAuthAuth(AuthCert lhs, AuthCert rhs)
340 throws IncompatibleException {
341 if (!lhs.getPropagate()) {
342 throw new IncompatibleException("cannot propagate this auth");
343 }
344 Tag newTag = lhs.getTag().intersect(rhs.getTag());
345 if (newTag == Tag.NULL_TAG) {
346 throw new IncompatibleException("auths have null intersection");
347 }
348 return new AuthCert(
349 lhs.getIssuer(),
350 rhs.getSubject(),
351 intersect(lhs.getValidity(), rhs.getValidity()),
352 lhs.getDisplay(),
353 lhs.getComment(),
354 newTag,
355 rhs.getPropagate());
356 }
357
358 /***
359 * Returns an array of certificates from this <code>Proof</code> and
360 * a given <code>Proof</code>.
361 *
362 * @param p <code>Proof</code> to concat this certificates with.
363 * @return an array of <code>Certificate</code> that contains this
364 * <code>Proof</code>'s certificates and the certificates from
365 * <code>p</code>.
366 */
367 private Certificate[] concat(Proof p) {
368 Certificate[] cs = new Certificate[certs.length + p.certs.length];
369 System.arraycopy(certs, 0, cs, 0, certs.length);
370 System.arraycopy(p.certs, 0, cs, certs.length, p.certs.length);
371 return cs;
372 }
373
374 /***
375 * Composes this proof with another proof.
376 *
377 * @param p the proof to compose this with.
378 * @return a new proof that is the composition of this with <code>p</code>.
379 * @throws IncompatibleException if this cannot be composed with
380 * <code>p</code>.
381 */
382 public Proof compose(Proof p) throws IncompatibleException {
383 if (cert instanceof NameCert) {
384 if (!(p.cert instanceof NameCert)) {
385 throw new IncompatibleException
386 ("Cannot compose NameCert with: "
387 + p.cert.getClass().getName());
388 }
389 return new Proof
390 (composeNameName((NameCert) cert, (NameCert) p.cert),
391 concat(p));
392 }
393 if (cert instanceof AuthCert) {
394 if (p.cert instanceof NameCert) {
395 return new Proof
396 (composeAuthName((AuthCert) cert, (NameCert) p.cert),
397 concat(p));
398 }
399 if (p.cert instanceof AuthCert) {
400 return new Proof
401 (composeAuthAuth((AuthCert) cert, (AuthCert) p.cert),
402 concat(p));
403 }
404 throw new ClassCastException
405 ("Cannot compose AuthCert with: "
406 + p.cert.getClass().getName());
407 }
408 throw new ClassCastException
409 ("Unrecognized Cert subclass: "
410 + cert.getClass().getName());
411 }
412 }
This page was automatically generated by Maven