package net.i2p.data; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.security.NoSuchAlgorithmException; import com.nettgryppa.security.HashCash; /** * Extend Destination with methods to verify its Certificate. * The router does not check Certificates, it doesn't care. * Apps however (particularly addressbook) may wish to enforce various * cert content, format, and policies. * This class is written such that apps may extend it to * create their own policies. * * @author zzz */ public class VerifiedDestination extends Destination { public VerifiedDestination() { super(); } /** * alternative constructor which takes a base64 string representation * @param s a Base64 representation of the destination, as (eg) is used in hosts.txt */ public VerifiedDestination(String s) throws DataFormatException { this(); fromBase64(s); } /** * create from an existing Dest * @param d must be non-null */ public VerifiedDestination(Destination d) throws DataFormatException { this(d.toBase64()); } /** * verify the certificate. * @param allowNone If true, allow a NULL or HIDDEN certificate. */ public boolean verifyCert(boolean allowNone) { if (_publicKey == null || _signingKey == null || _certificate == null) return false; switch (_certificate.getCertificateType()) { case Certificate.CERTIFICATE_TYPE_NULL: case Certificate.CERTIFICATE_TYPE_HIDDEN: return allowNone; case Certificate.CERTIFICATE_TYPE_HASHCASH: return verifyHashCashCert(); case Certificate.CERTIFICATE_TYPE_SIGNED: return verifySignedCert(); } return verifyUnknownCert(); } /** Defaults for HashCash Certs */ public final static int MIN_HASHCASH_EFFORT = 20; /** * HashCash Certs are used to demonstrate proof-of-work. * * We define a HashCash Certificate as follows: * - length: typically 47 bytes, but may vary somewhat * - contents: A version 1 HashCash Stamp, * defined at http://www.hashcash.org/docs/hashcash.html#stamp_format__version_1_ * modified to remove the contents of the 4th field (the resource) * original is ver:bits:date:resource:[ext]:rand:counter * I2P version is ver:bits:date::[ext]:rand:counter * The HashCash is calculated with the following resource: * The Base64 of the Public Key concatenated with the Base64 of the Signing Public Key * (NOT the Base64 of the concatenated keys) * To generate a Cert of this type, see PrivateKeyFile.main() * To verify, we must put the keys back into the resource field of the stamp, * then pass it to the HashCash constructor, then get the number of leading * zeros and see if it meets our minimum effort. */ protected boolean verifyHashCashCert() { String hcs = DataHelper.getUTF8(_certificate.getPayload()); int end1 = 0; for (int i = 0; i < 3; i++) { end1 = 1 + hcs.indexOf(':', end1); if (end1 < 0) return false; } int start2 = hcs.indexOf(':', end1); if (start2 < 0) return false; // put the keys back into the 4th field of the stamp hcs = hcs.substring(0, end1) + _publicKey.toBase64() + _signingKey.toBase64() + hcs.substring(start2); HashCash hc; try { hc = new HashCash(hcs); } catch (IllegalArgumentException iae) { return false; } catch (NoSuchAlgorithmException nsae) { return false; } return hc.getValue() >= MIN_HASHCASH_EFFORT; } /** Defaults for Signed Certs */ public final static int CERTIFICATE_LENGTH_SIGNED = Signature.SIGNATURE_BYTES; public final static int CERTIFICATE_LENGTH_SIGNED_WITH_HASH = Signature.SIGNATURE_BYTES + Hash.HASH_LENGTH; /** * Signed Certs are signed by a 3rd-party Destination. * They can be used for a second-level domain, for example, to sign the * Destination for a third-level domain. Or for a central authority * to approve a destination. * * We define a Signed Certificate as follows: * - length: Either 44 or 72 bytes * - contents: * 1: a 44 byte Signature * 2 (optional): a 32 byte Hash of the signing Destination * This can be a hint to the verification process to help find * the identity and keys of the signing Destination. * Data which is signed: The first 384 bytes of the Destination * (i.e. the Public Key and Signing Public Key, WITHOUT the Certificate) * * It is not appropriate to enforce a particular delegation scheme here. * The application will need to apply additional steps to select * an appropriate signing Destination and verify the signature. * * See PrivateKeyFile.verifySignature() for sample verification code. * */ protected boolean verifySignedCert() { return _certificate.getPayload() != null && (_certificate.getPayload().length == CERTIFICATE_LENGTH_SIGNED || _certificate.getPayload().length == CERTIFICATE_LENGTH_SIGNED_WITH_HASH); } /** * Reject all unknown certs */ protected boolean verifyUnknownCert() { return false; } @Override public String toString() { StringBuilder buf = new StringBuilder(128); buf.append(super.toString()); buf.append("\n\tVerified Certificate? ").append(verifyCert(true)); return buf.toString(); } }