List of usage examples for org.bouncycastle.asn1 ASN1InputStream readObject
public ASN1Primitive readObject() throws IOException
From source file:org.cryptoworkshop.ximix.client.connection.NodeServicesConnection.java
License:Apache License
private void open() throws IOException, ServiceConnectionException { this.connection = new Socket(address, portNo); cOut = connection.getOutputStream(); cIn = connection.getInputStream();//from ww w .j a va 2 s.c om ASN1InputStream aIn = new ASN1InputStream(cIn, 300000); // TODO: nodeInfo = NodeInfo.getInstance(aIn.readObject()); if (!name.equals(nodeInfo.getName())) { try { close(); } catch (ServiceConnectionException e) { // ignore } eventNotifier.notify(EventNotifier.Level.ERROR, "Node " + name + " identified itself as " + nodeInfo.getName() + " - closing connection"); throw new ServiceConnectionException( "Node " + name + " identified itself as " + nodeInfo.getName() + " - closing connection"); } }
From source file:org.cryptoworkshop.ximix.client.verify.ECDecryptionChallengeVerifier.java
License:Apache License
/** * Verify that the decryption challenge transcript is valid, throwing an exception if an issue is found.. * * @throws TranscriptVerificationException on verification failure. *//*ww w .j a va 2 s . c o m*/ public void verify() throws TranscriptVerificationException { ASN1InputStream aIn = new ASN1InputStream(logStream); ASN1InputStream resultIn = new ASN1InputStream(resultStream); ASN1InputStream lastIn = new ASN1InputStream(lastStageStream); try { int messageIndex = -1; ECPair[] encPairs = null; ASN1Object obj; while ((obj = aIn.readObject()) != null) { ChallengeLogMessage logMessage = ChallengeLogMessage.getInstance(obj); ECPoint[] sourceMessage = logMessage.getSourceMessage(); ECDecryptionProof[] proofs = logMessage.getProofs(); ECPublicKeyParameters currentPubKey = (ECPublicKeyParameters) PublicKeyFactory .createKey(logMessage.getKeyInfo()); if (!isSameParameters(pubKey.getParameters(), currentPubKey.getParameters())) { throw new TranscriptVerificationException( "Log message indicates inconsistent public key parameters."); } if (messageIndex != logMessage.getIndex()) { if (activePeers.length != 0) { LagrangeWeightCalculator weightCalculator = new LagrangeWeightCalculator(maxSequenceNo + 1, pubKey.getParameters().getN()); ECPoint accumulatedQ = null; BigInteger[] weights = weightCalculator.computeWeights(activePeers); // verify the partial public keys represent the one we have. for (int i = 0; i != weights.length; i++) { if (weights[i] != null) { if (accumulatedQ == null) { accumulatedQ = activePeers[i].getQ().multiply(weights[i]); } else { accumulatedQ = accumulatedQ.add(activePeers[i].getQ().multiply(weights[i])); } } } if (!pubKey.getQ().equals(accumulatedQ)) { throw new TranscriptVerificationException( "Log message indicates inconsistent public key."); } // verify the partial decrypts result in the final message int len = activeMsgParts[0].length; for (int i = 1; i != activeMsgParts.length; i++) { if (activeMsgParts[i].length != len) { throw new TranscriptVerificationException("Partial decrypt length mismatch"); } } int baseIndex = 0; for (int i = 0; i != activeMsgParts.length; i++) { if (activeMsgParts[i] != null) { baseIndex = i; break; } } BigInteger baseWeight = weights[baseIndex]; ECPoint[] decryptions = reassemblePoints(activeMsgParts, encPairs, weights, baseIndex, baseWeight); ECPoint[] recordedDecrypts = PointSequence .getInstance(pubKey.getParameters().getCurve(), resultIn.readObject()) .getECPoints(); if (!Arrays.areEqual(decryptions, recordedDecrypts)) { throw new TranscriptVerificationException( "Recorded decrypts do not match partial ones."); } // reset the peers array. for (int i = 0; i != activePeers.length; i++) { activePeers[i] = null; } for (int i = 0; i != activeMsgParts.length; i++) { activeMsgParts[i] = null; } } else if (messageIndex != -1) { throw new TranscriptVerificationException("Nothing to verify!"); } messageIndex = logMessage.getIndex(); PostedMessage pM = PostedMessage.getInstance(lastIn.readObject()); encPairs = PairSequence.getInstance(pubKey.getParameters().getCurve(), pM.getMessage()) .getECPairs(); } addPeer(logMessage.getSequenceNo(), currentPubKey, sourceMessage); if (!logMessage.hasPassed()) { throw new TranscriptVerificationException("Log message indicates challenge did not pass."); } for (int i = 0; i != proofs.length; i++) { if (!proofs[i].isVerified(activePeers[logMessage.getSequenceNo()], encPairs[i].getX(), sourceMessage[i])) { throw new TranscriptVerificationException( "Proof results do not match combined source message and cipher text."); } } } } catch (TranscriptVerificationException e) { throw e; } catch (Exception e) { throw new TranscriptVerificationException( "Exception validating decryption challenge transcript: " + e.getMessage(), e); } }
From source file:org.cryptoworkshop.ximix.client.verify.ECShuffledTranscriptVerifier.java
License:Apache License
private boolean loadWitnesses(ASN1InputStream transcript, int maxCount) throws TranscriptVerificationException { try {/*from ww w . j a v a 2s . c om*/ ASN1Primitive obj = null; if (maxCount < 0) { while ((obj = transcript.readObject()) != null) { PostedData pM = PostedData.getInstance(obj); MessageCommitment cm = MessageCommitment.getInstance(pM.getData()); witnesses.put(pM.getIndex(), cm); finalIndexesOfInterest.add(cm.getNewIndex()); } } else { int count = 0; while ((count < maxCount) && (obj = transcript.readObject()) != null) { PostedData pM = PostedData.getInstance(obj); MessageCommitment cm = MessageCommitment.getInstance(pM.getData()); witnesses.put(pM.getIndex(), cm); finalIndexesOfInterest.add(cm.getNewIndex()); count++; } } return (obj != null); } catch (Exception e) { throw new TranscriptVerificationException("Exception validating transcripts: " + e.getMessage(), e); } }
From source file:org.cryptoworkshop.ximix.client.verify.ECShuffledTranscriptVerifier.java
License:Apache License
private void loadCommitments(InputStream initialTranscript, InputStream finalTranscript) throws TranscriptVerificationException { try {//from w w w . ja va 2s . c om CMSSignedDataParser cmsParser = new CMSSignedDataParser(new BcDigestCalculatorProvider(), initialTranscript); ASN1InputStream aIn = new ASN1InputStream(cmsParser.getSignedContent().getContentStream()); ASN1Primitive obj; while ((obj = aIn.readObject()) != null) { PostedMessage pM = PostedMessage.getInstance(obj); if (witnesses.containsKey(pM.getIndex())) { initialMap.put(pM.getIndex(), pM); } } cmsParser = new CMSSignedDataParser(new BcDigestCalculatorProvider(), finalTranscript); aIn = new ASN1InputStream(cmsParser.getSignedContent().getContentStream()); while ((obj = aIn.readObject()) != null) { PostedMessage pM = PostedMessage.getInstance(obj); if (finalIndexesOfInterest.contains(pM.getIndex())) { finalMap.put(pM.getIndex(), pM); finalIndexesOfInterest.remove(pM.getIndex()); } } } catch (Exception e) { throw new TranscriptVerificationException("Exception validating transcripts: " + e.getMessage(), e); } }
From source file:org.cryptoworkshop.ximix.client.verify.LinkIndexVerifier.java
License:Apache License
public void verify(int stepNo, boolean isWithPairing, InputStream transcript) throws TranscriptVerificationException { CMSSignedDataParser cmsParser;// ww w. j av a2 s . c om SignerId currentSID; Set<Integer> pmIndexes = new HashSet<>(); Set<Integer> cmIndexes = new HashSet<>(); try { cmsParser = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), transcript); ASN1InputStream aIn = new ASN1InputStream(cmsParser.getSignedContent().getContentStream()); Object obj; while ((obj = aIn.readObject()) != null) { PostedData pM = PostedData.getInstance(obj); MessageCommitment cm = MessageCommitment.getInstance(pM.getData()); pmIndexes.add(pM.getIndex()); cmIndexes.add(cm.getNewIndex()); } currentSID = ((SignerInformation) cmsParser.getSignerInfos().getSigners().iterator().next()).getSID(); } catch (Exception e) { throw new TranscriptVerificationException("Cannot parse CMS wrapper on transcript: " + e.getMessage(), e); } SHA512Digest seedDigest = new SHA512Digest(); byte[] stepSeed = new byte[seedDigest.getDigestSize()]; // we follow the formulation in "Randomized Partial Checking Revisited" where the seed is // modified by the step number, the one difference being that in our case this will only take // place at the start of a pairing, or on an individual step. seedDigest.update(this.challengeSeed, 0, this.challengeSeed.length); seedDigest.update((byte) (stepNo >>> 24)); seedDigest.update((byte) (stepNo >>> 16)); seedDigest.update((byte) (stepNo >>> 8)); seedDigest.update((byte) stepNo); seedDigest.doFinal(stepSeed, 0); IndexNumberGenerator challenger; if (boardSize != 1) { challenger = new SeededChallenger(boardSize, stepNo, stepSeed); } else { challenger = new SerialChallenger(boardSize, stepNo, stepSeed); } Set<Integer> indexes = new HashSet<>(); while (challenger.hasNext()) { indexes.add(challenger.nextIndex()); } if (boardSize != 1 && isWithPairing) { if (!currentSID.equals(lastSID)) { for (int i = 0; i != boardSize; i++) { nextIndexes.add(i); } } else { indexes = new HashSet<>(nextIndexes); } } lastSID = currentSID; if (indexes.size() != pmIndexes.size()) { throw new TranscriptVerificationException( "Entries in witness table do not correspond to seeding - step " + stepNo + " size( " + indexes.size() + ", " + pmIndexes.size() + ")"); } indexes.removeAll(pmIndexes); nextIndexes.removeAll(cmIndexes); if (!indexes.isEmpty()) { throw new TranscriptVerificationException( "Entries in witness table do not correspond to seeding - step " + stepNo + " unaccounted " + indexes.size()); } }
From source file:org.cryptoworkshop.ximix.client.verify.LinkIndexVerifier.java
License:Apache License
/** * Return the number of messages that were on the board producing these commitments. * * @param fileList list of general transcript files. * @return number of messages on the board. * @throws TranscriptVerificationException if there is a mismatch in the file size. *//*from w w w. j a v a2s .c om*/ public static int getAndCheckBoardSize(File[] fileList) throws TranscriptVerificationException { int boardSize = -1; for (File file : fileList) { int count = 0; try { CMSSignedDataParser cmsParser = new CMSSignedDataParser(new BcDigestCalculatorProvider(), new BufferedInputStream(new FileInputStream(file))); ASN1InputStream aIn = new ASN1InputStream(cmsParser.getSignedContent().getContentStream()); while (aIn.readObject() != null) { count++; } if (boardSize == -1) { boardSize = count; } else if (count != boardSize) { throw new TranscriptVerificationException( "Size mismatch in commitment files: " + file.getPath()); } cmsParser.close(); } catch (Exception e) { throw new TranscriptVerificationException( "Size check failed on " + file.getPath() + ": " + e.getMessage(), e); } } return boardSize; }
From source file:org.cryptoworkshop.ximix.client.verify.test.VerifierTest.java
License:Apache License
private byte[] getSequence(byte[] init, MessageChooser chooser) throws Exception { CMSSignedDataParser cmsParser = new CMSSignedDataParser(new BcDigestCalculatorProvider(), new ByteArrayInputStream(init)); ASN1InputStream aIn = new ASN1InputStream(cmsParser.getSignedContent().getContentStream()); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); CMSSignedDataStreamGenerator cmsGen = new CMSSignedDataStreamGenerator(); OutputStream outputStream = cmsGen.open(bOut, true); DEROutputStream dOut = new DEROutputStream(outputStream); ASN1Primitive obj;/* w w w.j av a 2 s. c o m*/ int count = 0; while ((obj = aIn.readObject()) != null) { if (chooser.chooseMessage(count++)) { dOut.writeObject(obj); } } dOut.close(); cmsParser.close(); outputStream.close(); return bOut.toByteArray(); }
From source file:org.cryptoworkshop.ximix.node.core.XimixServices.java
License:Apache License
public void run() { try {//ww w .ja v a 2s . c o m s.setSoTimeout(5000); // TODO: should be a config item InputStream sIn = s.getInputStream(); OutputStream sOut = s.getOutputStream(); ASN1InputStream aIn = new ASN1InputStream(sIn, maxInputSize); // TODO: should be a config item DEROutputStream aOut = new DEROutputStream(sOut); aOut.writeObject(new NodeInfo(nodeContext.getName(), nodeContext.getCapabilities())); while (!stopped.get()) { try { //System.out.println("Connection from: "+s.getRemoteSocketAddress()) Object o; while ((o = aIn.readObject()) != null && !nodeContext.isStopCalled()) { Message message = Message.getInstance(o); NodeService nodeService = nodeContext.getService(message); nodeContext.getEventNotifier().notify(EventNotifier.Level.DEBUG, "Received Message: " + message.getType()); if (nodeService != null) { MessageReply reply = nodeService.handle(message); nodeContext.getEventNotifier().notify(EventNotifier.Level.DEBUG, "Reply Message: " + reply); aOut.writeObject(reply); } else { aOut.writeObject(new MessageReply(MessageReply.Type.ERROR, new DERUTF8String("Node " + nodeContext.getName() + ": unable to find service for " + message.getType()))); } } nodeContext.getEventNotifier().notify(EventNotifier.Level.INFO, "Service connection on " + nodeContext.getName() + " shutdown, stop called = " + nodeContext.isStopCalled()); break; } catch (SocketTimeoutException e) { continue; } } shutdownLatch.countDown(); s.close(); } catch (IOException e) { throwableHandler.notify(EventNotifier.Level.WARN, e); } }
From source file:org.cryptoworkshop.ximix.node.crypto.service.NodeShuffledBoardDecryptionService.java
License:Apache License
public MessageReply handle(Message message) { switch (((CommandMessage) message).getType()) { case FILE_UPLOAD: FileTransferMessage transMessage = FileTransferMessage.getInstance(message.getPayload()); File destinationFile = new File(workDirectory, transMessage.getFileName()); try {//from w ww . j a v a 2 s .c o m OutputStream fileStream = activeFiles.get(destinationFile); if (fileStream == null) { fileStream = new BufferedOutputStream(new FileOutputStream(destinationFile)); activeFiles.put(destinationFile, fileStream); } if (transMessage.isEndOfTransfer()) { fileStream.close(); activeFiles.remove(destinationFile); } else { fileStream.write(transMessage.getChunk()); } } catch (IOException e) { return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String(transMessage.getFileName() + ": " + e.getMessage())); } return new MessageReply(MessageReply.Type.OKAY, new DERUTF8String(transMessage.getFileName())); case SETUP_PARTIAL_DECRYPT: final DecryptShuffledBoardMessage setupMessage = DecryptShuffledBoardMessage .getInstance(message.getPayload()); SubjectPublicKeyInfo keyInfo = nodeContext.getPublicKey(setupMessage.getKeyID()); ECPublicKeyParameters pubKey; try { if (keyInfo != null) { pubKey = (ECPublicKeyParameters) PublicKeyFactory.createKey(keyInfo); } else { // see if the key exists elsewhere on the MIXNET. FetchPublicKeyMessage fetchMessage = new FetchPublicKeyMessage(setupMessage.getKeyID()); MessageReply reply = nodeContext.getPeerMap().values().iterator().next() .sendMessage(ClientMessage.Type.FETCH_PUBLIC_KEY, fetchMessage); if (reply.getPayload() != null) { pubKey = (ECPublicKeyParameters) PublicKeyFactory .createKey(reply.getPayload().toASN1Primitive().getEncoded()); } else { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Unable to find public key " + setupMessage.getKeyID()); return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String("Unable to locate key " + setupMessage.getKeyID())); } } } catch (Exception e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Unable to process data for key " + setupMessage.getKeyID()); return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String("Unable to process data for key " + setupMessage.getKeyID())); } // verify signatures. File[] files = workDirectory.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(setupMessage.getBoardName()) && name.endsWith(".gtr"); } }); final Map<Integer, File> generalTranscripts = createTranscriptMap(signatureVerifier, files); int boardSize; try { boardSize = LinkIndexVerifier.getAndCheckBoardSize(files); } catch (TranscriptVerificationException e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Decrypt refused, size validation failed: " + e.getMessage(), e); return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String("Decrypt refused, size validation failed: " + e.getMessage())); } files = workDirectory.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(setupMessage.getBoardName()) && name.endsWith(".wtr"); } }); final Map<Integer, File> witnessTranscripts = createTranscriptMap(signatureVerifier, files); files = workDirectory.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(setupMessage.getBoardName()) && name.endsWith(".sc"); } }); final Map<String, byte[]> seedCommitmentMap = createSeedCommitmentMap(signatureVerifier, files); files = workDirectory.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(setupMessage.getBoardName()) && name.endsWith(".svw"); } }); final Map<String, byte[][]> seedAndWitnessesMap = createSeedAndWitnessMap(files); LinkIndexVerifier.Builder verifierBuilder = new LinkIndexVerifier.Builder(boardSize); try { verifierBuilder.setNetworkSeeds(seedCommitmentMap, seedAndWitnessesMap); for (Integer key : generalTranscripts.keySet()) { BufferedInputStream bIn = new BufferedInputStream( new FileInputStream(generalTranscripts.get(key))); verifierBuilder.addTranscript(bIn); bIn.close(); } LinkIndexVerifier linkIndexVerifier = verifierBuilder.build(); // verify which links have been opened. for (Integer key : witnessTranscripts.keySet()) { BufferedInputStream bIn = new BufferedInputStream( new FileInputStream(witnessTranscripts.get(key))); linkIndexVerifier.verify(key, setupMessage.isWithPairing(), bIn); bIn.close(); } linkIndexVerifier = null; // free the resources // verify the opened commitments. for (Integer key : witnessTranscripts.keySet()) { File transcriptFile = witnessTranscripts.get(key); File initialTranscript = generalTranscripts.get(key); File nextTranscript = generalTranscripts.get(key + 1); InputStream witnessTranscriptStream = new BufferedInputStream( new FileInputStream(transcriptFile)); ECShuffledTranscriptVerifier verifier = new ECShuffledTranscriptVerifier(pubKey, witnessTranscriptStream, initialTranscript, nextTranscript); verifier.verify(); witnessTranscriptStream.close(); } } catch (CommitmentVerificationException e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Decrypt refused, validation failed: " + e.getMessage(), e); return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String("Decrypt refused, validation failed: " + e.getMessage())); } catch (TranscriptVerificationException e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Decrypt refused, validation failed: " + e.getMessage(), e); return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String("Decrypt refused, validation failed: " + e.getMessage())); } catch (Exception e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, setupMessage.getBoardName() + ": " + e.getMessage(), e); return new MessageReply(MessageReply.Type.ERROR, new DERUTF8String(setupMessage.getBoardName() + ": " + e.getMessage())); } File finalFile = generalTranscripts.get(witnessTranscripts.size()); try { CMSSignedDataParser cmsParser = new CMSSignedDataParser(new BcDigestCalculatorProvider(), new BufferedInputStream(new FileInputStream(finalFile))); activeDecrypts.put(setupMessage.getBoardName(), new ASN1InputStream(cmsParser.getSignedContent().getContentStream())); return new MessageReply(MessageReply.Type.OKAY, new DERUTF8String(setupMessage.getBoardName())); } catch (Exception e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Unable to process data for download key " + setupMessage.getKeyID()); return new MessageReply(MessageReply.Type.ERROR, new ErrorMessage("Error opening posted message stream")); } case DOWNLOAD_PARTIAL_DECRYPTS: DownloadShuffledBoardMessage downMessage = DownloadShuffledBoardMessage .getInstance(message.getPayload()); PostedMessageDataBlock.Builder partialDecryptsBuilder = new PostedMessageDataBlock.Builder( downMessage.getBlockSize()); PrivateKeyOperator operator = nodeContext.getPrivateKeyOperator(downMessage.getKeyID()); if (!(operator instanceof ECPrivateKeyOperator)) { return new MessageReply(MessageReply.Type.ERROR, new ErrorMessage("Inappropriate key type")); } ECPrivateKeyOperator ecOperator = (ECPrivateKeyOperator) operator; ECDomainParameters domainParameters = ecOperator.getDomainParameters(); ASN1InputStream aIn = activeDecrypts.get(downMessage.getBoardName()); if (aIn == null) { return new MessageReply(MessageReply.Type.OKAY, new ShareMessage(operator.getSequenceNo(), partialDecryptsBuilder.build())); } try { Object o = null; ProofGenerator pGen = new ProofGenerator(ecOperator, new SecureRandom()); // TODO: randomness while (partialDecryptsBuilder.hasCapacity() && (o = aIn.readObject()) != null) { PostedMessage postedMessage = PostedMessage.getInstance(o); PairSequence ps = PairSequence.getInstance(domainParameters.getCurve(), postedMessage.getMessage()); ECPair[] pairs = ps.getECPairs(); ECDecryptionProof[] proofs = new ECDecryptionProof[pairs.length]; for (int j = 0; j != pairs.length; j++) { ECPoint c = pairs[j].getX(); pairs[j] = new ECPair(ecOperator.transform(pairs[j].getX()), pairs[j].getY()); proofs[j] = pGen.computeProof(c, pairs[j]); } partialDecryptsBuilder.add(new PairSequenceWithProofs(pairs, proofs).getEncoded()); } if (o == null) { activeDecrypts.remove(downMessage.getBoardName()); aIn.close(); } return new MessageReply(MessageReply.Type.OKAY, new ShareMessage(operator.getSequenceNo(), partialDecryptsBuilder.build())); } catch (Exception e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Error parsing posted message stream: " + e.getMessage(), e); return new MessageReply(MessageReply.Type.ERROR, new ErrorMessage("Error parsing posted message stream: " + e.getMessage())); } default: nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Unknown command: " + message.getType()); return new MessageReply(MessageReply.Type.ERROR, new ErrorMessage("Unknown command: " + message.getType())); } }
From source file:org.cryptoworkshop.ximix.node.crypto.service.NodeShuffledBoardDecryptionService.java
License:Apache License
private Map<String, byte[][]> createSeedAndWitnessMap(File[] fileList) { final Map<String, byte[][]> transcripts = new TreeMap<>(); for (File file : fileList) { String name = file.getName(); int beginIndex = name.indexOf('.') + 1; String nodeName = name.substring(beginIndex, name.indexOf('.', beginIndex)); try {//from ww w. j a v a 2 s.c o m ASN1InputStream aIn = new ASN1InputStream(new FileInputStream(file)); SeedAndWitnessMessage sAnW = SeedAndWitnessMessage.getInstance(aIn.readObject()); if (aIn.readObject() != null) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "createSeedAndWitnessMap extra data found: " + file.getPath()); } transcripts.put(nodeName, new byte[][] { sAnW.getSeed(), sAnW.getWitness() }); aIn.close(); } catch (Exception e) { nodeContext.getEventNotifier().notify(EventNotifier.Level.ERROR, "Signature check failed on " + file.getPath() + ": " + e.getMessage(), e); } } return transcripts; }