package com.subgraph.orchid.circuits.hs; import java.nio.ByteBuffer; import java.util.logging.Logger; import com.subgraph.orchid.TorParsingException; import com.subgraph.orchid.crypto.TorSignature; import com.subgraph.orchid.directory.DocumentFieldParserImpl; import com.subgraph.orchid.directory.parsing.BasicDocumentParsingResult; import com.subgraph.orchid.directory.parsing.DocumentFieldParser; import com.subgraph.orchid.directory.parsing.DocumentObject; import com.subgraph.orchid.directory.parsing.DocumentParser; import com.subgraph.orchid.directory.parsing.DocumentParsingHandler; import com.subgraph.orchid.directory.parsing.DocumentParsingResult; import com.subgraph.orchid.directory.parsing.DocumentParsingResultHandler; import com.subgraph.orchid.encoders.Base64; public class HSDescriptorParser implements DocumentParser<HSDescriptor>{ private static final Logger logger = Logger.getLogger(HSDescriptor.class.getName()); private final DocumentFieldParser fieldParser; private final HSDescriptor descriptor; private final HSAuthentication authentication; private DocumentParsingResultHandler<HSDescriptor> resultHandler; public HSDescriptorParser(HiddenService hiddenService, DocumentFieldParser fieldParser) { this(hiddenService, fieldParser, null); } public HSDescriptorParser(HiddenService hiddenService, DocumentFieldParser fieldParser, HSDescriptorCookie cookie) { this.fieldParser = fieldParser; this.fieldParser.setHandler(createParsingHandler()); this.descriptor = new HSDescriptor(hiddenService); this.authentication = new HSAuthentication(cookie); } private DocumentParsingHandler createParsingHandler() { return new DocumentParsingHandler() { public void parseKeywordLine() { processKeywordLine(); } public void endOfDocument() { } }; } public boolean parse(DocumentParsingResultHandler<HSDescriptor> resultHandler) { this.resultHandler = resultHandler; fieldParser.startSignedEntity(); try { fieldParser.processDocument(); return true; } catch(TorParsingException e) { resultHandler.parsingError(e.getMessage()); return false; } } public DocumentParsingResult<HSDescriptor> parse() { final BasicDocumentParsingResult<HSDescriptor> result = new BasicDocumentParsingResult<HSDescriptor>(); parse(result); return result; } private void processKeywordLine() { final HSDescriptorKeyword keyword = HSDescriptorKeyword.findKeyword(fieldParser.getCurrentKeyword()); if(!keyword.equals(HSDescriptorKeyword.UNKNOWN_KEYWORD)) { processKeyword(keyword); } } private void processKeyword(HSDescriptorKeyword keyword) { switch(keyword) { case RENDEZVOUS_SERVICE_DESCRIPTOR: descriptor.setDescriptorId(fieldParser.parseBase32Digest()); break; case VERSION: if(fieldParser.parseInteger() != 2) { throw new TorParsingException("Unexpected Descriptor version"); } break; case PERMANENT_KEY: descriptor.setPermanentKey(fieldParser.parsePublicKey()); break; case SECRET_ID_PART: descriptor.setSecretIdPart(fieldParser.parseBase32Digest()); break; case PUBLICATION_TIME: descriptor.setPublicationTime(fieldParser.parseTimestamp()); break; case PROTOCOL_VERSIONS: descriptor.setProtocolVersions(fieldParser.parseIntegerList()); break; case INTRODUCTION_POINTS: processIntroductionPoints(); break; case SIGNATURE: processSignature(); break; case UNKNOWN_KEYWORD: break; } } private void processIntroductionPoints() { final DocumentObject ob = fieldParser.parseObject(); final ByteBuffer buffer = createIntroductionPointBuffer(ob); final IntroductionPointParser parser = new IntroductionPointParser(new DocumentFieldParserImpl(buffer)); parser.parse(new DocumentParsingResultHandler<IntroductionPoint>() { public void documentParsed(IntroductionPoint document) { logger.fine("adding intro point "+ document.getIdentity()); descriptor.addIntroductionPoint(document); } public void documentInvalid(IntroductionPoint document, String message) { logger.info("Invalid introduction point received"); } public void parsingError(String message) { logger.info("Error parsing introduction points: "+ message); } }); } private ByteBuffer createIntroductionPointBuffer(DocumentObject ob) { final byte[] content = Base64.decode(ob.getContent(false)); if(content[0] == 'i') { return ByteBuffer.wrap(content); } else { try { byte[] decrypted = authentication.decryptIntroductionPoints(content); return ByteBuffer.wrap(decrypted); } catch (HSAuthenticationException e) { throw new TorParsingException("Failed to decrypt introduction points: "+ e.getMessage()); } } } private void processSignature() { fieldParser.endSignedEntity(); final TorSignature signature = fieldParser.parseSignature(); if(!fieldParser.verifySignedEntity(descriptor.getPermanentKey(), signature)) { resultHandler.documentInvalid(descriptor, "Signature verification failed"); fieldParser.logWarn("Signature failed for descriptor: "+ descriptor.getDescriptorId().toBase32()); return; } resultHandler.documentParsed(descriptor); } }