package org.torproject.jtor.data; import java.util.Arrays; import java.util.List; import org.bouncycastle.util.encoders.Hex; import org.torproject.jtor.TorException; import org.torproject.jtor.crypto.TorMessageDigest; /** * This class represents both digests and fingerprints that appear in directory * documents. The names fingerprint and digest are used interchangeably in * the specification but generally a fingerprint is a message digest (ie: SHA1) * over the DER ASN.1 encoding of a public key. A digest is usually * a message digest over a set of fields in a directory document. * * Digests always appear as a 40 character hex string: * * 0EA20CAA3CE696E561BC08B15E00106700E8F682 * * Fingerprints may either appear as a single hex string as above or sometimes in * a more easily human-parsed spaced format: * * 1E0F 5874 2268 E82F C600 D81D 9064 07C5 7CC2 C3A7 * */ public class HexDigest { public static HexDigest createFromStringList(List<String> strings) { StringBuilder builder = new StringBuilder(); for(String chunk: strings) builder.append(chunk); return createFromString(builder.toString()); } public static HexDigest createFromString(String fingerprint) { final String[] parts = fingerprint.split(" "); if(parts.length > 1) return createFromStringList(Arrays.asList(parts)); final byte[] digestData = Hex.decode(fingerprint); return new HexDigest(digestData); } public static HexDigest createFromDigestBytes(byte[] data) { return new HexDigest(data); } public static HexDigest createDigestForData(byte[] data) { final TorMessageDigest digest = new TorMessageDigest(); digest.update(data); return new HexDigest(digest.getDigestBytes()); } private final byte[] digestBytes = new byte[TorMessageDigest.TOR_DIGEST_SIZE]; private HexDigest(byte[] data) { if(data.length != TorMessageDigest.TOR_DIGEST_SIZE) { throw new TorException("Digest data is not the correct length "+ data.length +" != " + TorMessageDigest.TOR_DIGEST_SIZE); } System.arraycopy(data, 0, digestBytes, 0, TorMessageDigest.TOR_DIGEST_SIZE); } public byte[] getRawBytes() { return digestBytes; } public String toString() { return new String(Hex.encode(digestBytes)); } /** * Return a spaced fingerprint representation of this HexDigest. * * ex: * * 1E0F 5874 2268 E82F C600 D81D 9064 07C5 7CC2 C3A7 * * @return A string representation of this HexDigest in the spaced fingerprint format. */ public String toSpacedString() { final String original = toString(); final StringBuilder builder = new StringBuilder(); for(int i = 0; i < original.length(); i++) { if(i > 0 && (i % 4) == 0) builder.append(' '); builder.append(original.charAt(i)); } return builder.toString(); } public boolean equals(Object o) { if(!(o instanceof HexDigest)) return false; final HexDigest other = (HexDigest)o; return Arrays.equals(other.digestBytes, this.digestBytes); } public int hashCode() { int hash = 0; for(int i = 0; i < 4; i++) { hash <<= 8; hash |= (digestBytes[i] & 0xFF); } return hash; } }