package com.subgraph.orchid.circuits.hs; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.subgraph.orchid.HiddenServiceCircuit; import com.subgraph.orchid.TorConfig; import com.subgraph.orchid.circuits.hs.HSDescriptorCookie.CookieType; import com.subgraph.orchid.crypto.TorMessageDigest; import com.subgraph.orchid.data.Base32; import com.subgraph.orchid.data.HexDigest; public class HiddenService { private final TorConfig config; private final byte[] permanentId; private HSDescriptor descriptor; private HiddenServiceCircuit circuit; static byte[] decodeOnion(String onionAddress) { final int idx = onionAddress.indexOf(".onion"); if(idx == -1) { return Base32.base32Decode(onionAddress); } else { return Base32.base32Decode(onionAddress.substring(0, idx)); } } HiddenService(TorConfig config, byte[] permanentId) { this.config = config; this.permanentId = permanentId; } String getOnionAddressForLogging() { if(config.getSafeLogging()) { return "[scrubbed]"; } else { return getOnionAddress(); } } String getOnionAddress() { return Base32.base32Encode(permanentId) + ".onion"; } boolean hasCurrentDescriptor() { return (descriptor != null && !descriptor.isExpired()); } HSDescriptor getDescriptor() { return descriptor; } void setDescriptor(HSDescriptor descriptor) { this.descriptor = descriptor; } HiddenServiceCircuit getCircuit() { return circuit; } void setCircuit(HiddenServiceCircuit circuit) { this.circuit = circuit; } HSDescriptorCookie getAuthenticationCookie() { return config.getHidServAuth(getOnionAddress()); } List<HexDigest> getAllCurrentDescriptorIds() { final List<HexDigest> ids = new ArrayList<HexDigest>(); ids.add(getCurrentDescriptorId(0)); ids.add(getCurrentDescriptorId(1)); return ids; } HexDigest getCurrentDescriptorId(int replica) { final TorMessageDigest digest = new TorMessageDigest(); digest.update(permanentId); digest.update(getCurrentSecretId(replica)); return digest.getHexDigest(); } byte[] getCurrentSecretId(int replica) { final TorMessageDigest digest = new TorMessageDigest(); digest.update(getCurrentTimePeriod()); final HSDescriptorCookie cookie = getAuthenticationCookie(); if(cookie != null && cookie.getType() == CookieType.COOKIE_STEALTH) { digest.update(cookie.getValue()); } digest.update(new byte[] { (byte) replica }); return digest.getDigestBytes(); } byte[] getCurrentTimePeriod() { final long now = System.currentTimeMillis() / 1000; final int idByte = permanentId[0] & 0xFF; return calculateTimePeriod(now, idByte); } static byte[] calculateTimePeriod(long currentTime, int idByte) { final long t = (currentTime + (idByte * 86400L / 256)) / 86400L; return toNetworkBytes(t); } static byte[] toNetworkBytes(long value) { final byte[] result = new byte[4]; for(int i = 3; i >= 0; i--) { result[i] = (byte) (value & 0xFF); value >>= 8; } return result; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(permanentId); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HiddenService other = (HiddenService) obj; if (!Arrays.equals(permanentId, other.permanentId)) return false; return true; } }