View Javadoc
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