package plan9.auth; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Date; import java.util.Arrays;import java.util.ArrayList; import plan9.lib.Attr;import plan9.lib.Attrs;import plan9.lib.Key;import plan9.lib.Keys;import plan9.lib.Packer;import plan9.lib.Strings;import plan9.lib.Base16;import plan9.lib.Encoding;import plan9.lib.Base64;import plan9.lib.Strings;import plan9.lib.Msgio;import plan9.lib.RemoteError; import plan9.Log;import plan9.LogFactory; import static plan9.auth.Pki.*; public class Verifier { static final Log log = LogFactory.logger(Verifier.class); ArrayList<Principal> keys = new ArrayList<Principal>(); public final Principal addkey(PK pk){ try{ Hash h = Pki.hash("sha1", pk); Principal p = keybyhash(h); if(p == null){ p = new Principal(pk, null, h); keys.add(p); } return p; }catch(CryptoError e){ // can't happen, but if it did, just ignore the key return null; } } public final Principal addkey(Principal p){ Principal op = keybyhash(p.pkhash); if(op != null) return op; keys.add(p); return p; } public final Principal keybyhash(Hash h) { for(Principal p : keys) if(p.eqhash(h)) return p; return null; } public Certificate verify(ArrayList<Object> seq) throws ValidationFailed { ArrayList<Certificate> certs = new ArrayList<Certificate>(); Name n0 = null; Certificate cn = null; Tag tag = null; long expires = 0; for(Object o : seq){ if(o instanceof Certificate){ Certificate cert = (Certificate)o; if(cert.expires != 0 && cert.expires <= now()) throw new ValidationFailed("certificate expired: "+cert); if(cn != null){ if(!cn.subject.principal.eq(cert.issuer.principal)) throw new ValidationFailed(String.format("certificate chain has mismatched principals: subject %s but next issuer %s", cn.subject, cert.issuer)); if(log.debugging()) log.debug("subject %s → issuer %s ok", cn.subject, cert.issuer.principal); } certs.add(cert); if(n0 == null) n0 = cert.issuer; if(cert.expires != 0 && cert.expires < expires) expires = cert.expires; if(!cert.isNameCert()){ if(tag != null){ tag = tag.intersect(cert.tag); if(tag.isnone()) throw new ValidationFailed("certificate chain gives no authority"); }else tag = cert.tag; } cn = cert; }else if(o instanceof Principal){ Principal p = (Principal)o; if(p.pk != null){ // otherwise it's just a hash, and makes no difference if(keybyhash(p.pkhash) == null) keys.add(p); } }else if(o instanceof PK){ addkey((PK)o); }else if(o instanceof SK){ addkey(((SK)o).pk()); }else if(o instanceof Signature){ Signature sig = (Signature)o; if(cn == null) throw new ValidationFailed("invalid proof sequence: signature missing certificate: "+sig); Principal p = sig.signer; if(p.pk == null){ // if needed, ought to appear earlier in the sequence p = keybyhash(sig.signer.pkhash); if(p == null) throw new ValidationFailed("missing public key for signer "+sig.signer); // could sig.signer.addkey(p.pk); } // verify signature if(!validsig(cn, sig, p)) throw new ValidationFailed("signature does not match certificate: "+sig.signer); } } if(n0 == null || cn == null) throw new ValidationFailed("proof sequence proved nothing"); if(log.debugging()){ String s = String.format("Verifier: %s speaks for %s", cn.subject, n0); if(tag != null) s += " regarding "+tag; if(expires != 0) s += String.format(" expires %d [%s]", expires, new Date(expires*1000)); log.debug("%s", s); } return new Certificate(n0, cn.subject, expires, tag); } private static int now(){ return (int)((new Date()).getTime()/1000); } public final static boolean validsig(Certificate cert, Signature sig, Principal p) throws ValidationFailed { try{ ByteBuffer b = ByteBuffer.allocate(8192); cert.pack(b); if(!sig.data.alg.equals("sha1")) throw new ValidationFailed("unimplemented hash algorithm: "+sig.data.alg); byte[] h = Aids.sha1(b.array(), 0, b.position(), null); if(log.debugging()) log.debug("hash cert: %s", Aids.b16(h)); // TO DO: pkcs1 if(!sig.signer.pk.verify(sig.sig, new BigInteger(1, h))) throw new ValidationFailed("signature verification failed: "+cert); return true; }catch(Exception e){ // any error causes validation to fail throw new ValidationFailed(e.getMessage()); } }}
package plan9.auth;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.Arrays;
import java.util.ArrayList;
import plan9.lib.Attr;
import plan9.lib.Attrs;
import plan9.lib.Key;
import plan9.lib.Keys;
import plan9.lib.Packer;
import plan9.lib.Strings;
import plan9.lib.Base16;
import plan9.lib.Encoding;
import plan9.lib.Base64;
import plan9.lib.Strings;
import plan9.lib.Msgio;
import plan9.lib.RemoteError;
import plan9.Log;
import plan9.LogFactory;
import static plan9.auth.Pki.*;
public class Verifier {
static final Log log = LogFactory.logger(Verifier.class);
ArrayList<Principal> keys = new ArrayList<Principal>();
public final Principal addkey(PK pk){
try{
Hash h = Pki.hash("sha1", pk);
Principal p = keybyhash(h);
if(p == null){
p = new Principal(pk, null, h);
keys.add(p);
}
return p;
}catch(CryptoError e){
// can't happen, but if it did, just ignore the key
return null;
}
}
public final Principal addkey(Principal p){
Principal op = keybyhash(p.pkhash);
if(op != null)
return op;
keys.add(p);
return p;
}
public final Principal keybyhash(Hash h) {
for(Principal p : keys)
if(p.eqhash(h))
return p;
return null;
}
public Certificate verify(ArrayList<Object> seq) throws ValidationFailed {
ArrayList<Certificate> certs = new ArrayList<Certificate>();
Name n0 = null;
Certificate cn = null;
Tag tag = null;
long expires = 0;
for(Object o : seq){
if(o instanceof Certificate){
Certificate cert = (Certificate)o;
if(cert.expires != 0 && cert.expires <= now())
throw new ValidationFailed("certificate expired: "+cert);
if(cn != null){
if(!cn.subject.principal.eq(cert.issuer.principal))
throw new ValidationFailed(String.format("certificate chain has mismatched principals: subject %s but next issuer %s", cn.subject, cert.issuer));
if(log.debugging())
log.debug("subject %s → issuer %s ok", cn.subject, cert.issuer.principal);
}
certs.add(cert);
if(n0 == null)
n0 = cert.issuer;
if(cert.expires != 0 && cert.expires < expires)
expires = cert.expires;
if(!cert.isNameCert()){
if(tag != null){
tag = tag.intersect(cert.tag);
if(tag.isnone())
throw new ValidationFailed("certificate chain gives no authority");
}else
tag = cert.tag;
}
cn = cert;
}else if(o instanceof Principal){
Principal p = (Principal)o;
if(p.pk != null){ // otherwise it's just a hash, and makes no difference
if(keybyhash(p.pkhash) == null)
keys.add(p);
}
}else if(o instanceof PK){
addkey((PK)o);
}else if(o instanceof SK){
addkey(((SK)o).pk());
}else if(o instanceof Signature){
Signature sig = (Signature)o;
if(cn == null)
throw new ValidationFailed("invalid proof sequence: signature missing certificate: "+sig);
Principal p = sig.signer;
if(p.pk == null){
// if needed, ought to appear earlier in the sequence
p = keybyhash(sig.signer.pkhash);
if(p == null)
throw new ValidationFailed("missing public key for signer "+sig.signer);
// could sig.signer.addkey(p.pk);
}
// verify signature
if(!validsig(cn, sig, p))
throw new ValidationFailed("signature does not match certificate: "+sig.signer);
}
}
if(n0 == null || cn == null)
throw new ValidationFailed("proof sequence proved nothing");
if(log.debugging()){
String s = String.format("Verifier: %s speaks for %s", cn.subject, n0);
if(tag != null)
s += " regarding "+tag;
if(expires != 0)
s += String.format(" expires %d [%s]", expires, new Date(expires*1000));
log.debug("%s", s);
}
return new Certificate(n0, cn.subject, expires, tag);
}
private static int now(){
return (int)((new Date()).getTime()/1000);
}
public final static boolean validsig(Certificate cert, Signature sig, Principal p) throws ValidationFailed {
try{
ByteBuffer b = ByteBuffer.allocate(8192);
cert.pack(b);
if(!sig.data.alg.equals("sha1"))
throw new ValidationFailed("unimplemented hash algorithm: "+sig.data.alg);
byte[] h = Aids.sha1(b.array(), 0, b.position(), null);
if(log.debugging())
log.debug("hash cert: %s", Aids.b16(h));
// TO DO: pkcs1
if(!sig.signer.pk.verify(sig.sig, new BigInteger(1, h)))
throw new ValidationFailed("signature verification failed: "+cert);
return true;
}catch(Exception e){
// any error causes validation to fail
throw new ValidationFailed(e.getMessage());
}
}
}