diff --git a/.hgignore b/.hgignore index 1364052..dbbd3a4 100644 --- a/.hgignore +++ b/.hgignore @@ -15,6 +15,8 @@ dist/* # ignore generated files status_list.yml2 +passphrase_status_list.yml2 + foundation_pEp_jniadapter__Blob.h foundation_pEp_jniadapter_AbstractEngine.h foundation_pEp_jniadapter_Engine.* @@ -35,6 +37,7 @@ Rating.java Status.java SyncHandshakeResult.java SyncHandshakeSignal.java +PassphraseType.java # ignore pep-homes test/resources/per-user-dirs/alice/.pEp diff --git a/Makefile.conf b/Makefile.conf index 2551f0d..cc24c6f 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -67,7 +67,7 @@ ifeq ($(DEBUG),1) CXXFLAGS+=-g else $(info Release Build (set DEBUG=1 for debug build)) - CXXFLAGS+=-DNDEBUG=1 + CXXFLAGS+=-DNDEBUG=1 -O3 endif ### YML_PATH is needed in the environment of every call to a program of the YML2 distribution diff --git a/src/Makefile b/src/Makefile index bba68cc..325fee2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -66,6 +66,7 @@ GENERATED_JAVA=\ $(JAVA_PKG_ROOT)/Rating.java \ $(JAVA_PKG_ROOT)/SyncHandshakeResult.java \ $(JAVA_PKG_ROOT)/SyncHandshakeSignal.java \ + $(JAVA_PKG_ROOT)/PassphraseType.java \ $(JAVA_PKG_ROOT)/interfaces/EngineInterface.java \ $(JAVA_PKG_ROOT)/interfaces/MessageInterface.java \ $(JAVA_PKG_ROOT)/exceptions/*.java diff --git a/src/codegen/Makefile b/src/codegen/Makefile index 16ca8bd..556aae4 100644 --- a/src/codegen/Makefile +++ b/src/codegen/Makefile @@ -38,11 +38,13 @@ codegen: create-dirs gen-status-codes $(YML2_MARKERS) $(YML2_MARKERS): $(MARKER_DIR)/%.marker : %.ysl2 pEp.yml2 $(YML2_INCLUDES) $(YML2_PROC) -y $< pEp.yml2 -gen-status-codes: status_list.yml2 +gen-status-codes: status_list.yml2 passphrase_status_list.yml2 status_list.yml2: pEp.yml2 bash ../../utils/extract_pEp_status_codes_from_engine.sh "$(PEP_HEADER)" $@ +passphrase_status_list.yml2: status_list.yml2 + grep passphrase $< > $@ # ------------- Housekeeping --------------- create-dirs: @@ -54,4 +56,5 @@ remove-dirs: clean: remove-dirs rm -f status_list.yml2 + rm -f passphrase_status_list.yml2 diff --git a/src/codegen/gen_cpp_Engine.ysl2 b/src/codegen/gen_cpp_Engine.ysl2 index 96b9d93..50a4d0f 100644 --- a/src/codegen/gen_cpp_Engine.ysl2 +++ b/src/codegen/gen_cpp_Engine.ysl2 @@ -12,6 +12,7 @@ tstylesheet { template "interface" document("../cxx/foundation_pEp_jniadapter_{@name}.cc", "text") || + #include #include #include #include @@ -73,34 +74,7 @@ tstylesheet { choose { when "@cached = 'true'" { || - pEpLog("cached passphrase mode"); - bool retryAgain = false; - int maxRetries = 3; - int retryCount = 0; - PEP_STATUS status; - do { - // the actual target function - pEpLog("calling passphrase_cache.api(::«@name»())"); - status = passphrase_cache.api(::«@name»,session()`apply "parm", mode=call`); - pEpLog("PEP_STATUS:" << status); - if(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE ) { - pEpLog("none of the cached passphrases worked"); - if(retryCount < maxRetries) { - // call the app - char* _passphrase = passphraseRequiredCallback(); - pEpLog("callback returned, config_passphrase() with new passphrase"); - PEP_STATUS status = ::config_passphrase(session(),passphrase_cache.add(_passphrase)); - retryAgain = true; - retryCount++; - } else { - pEpLog("max retries reached:" << maxRetries); - retryAgain = false; - } - } else { - retryAgain = false; - } - } while (retryAgain); - + PEP_STATUS status = passphraseWrap(::«@name», session()`apply "parm", mode=call`); || } otherwise { || diff --git a/src/codegen/pEp.yml2 b/src/codegen/pEp.yml2 index c3022fa..d446b3a 100644 --- a/src/codegen/pEp.yml2 +++ b/src/codegen/pEp.yml2 @@ -10,7 +10,11 @@ namespace pEp { exception Status { include ./status_list.yml2 }; - + + enum PassphraseType { + include ./passphrase_status_list.yml2 + }; + enum Color { PEP_color_no_color > 0 PEP_color_yellow > 1 diff --git a/src/cxx/foundation_pEp_jniadapter_AbstractEngine.cc b/src/cxx/foundation_pEp_jniadapter_AbstractEngine.cc index dc6b72d..bdbf80e 100644 --- a/src/cxx/foundation_pEp_jniadapter_AbstractEngine.cc +++ b/src/cxx/foundation_pEp_jniadapter_AbstractEngine.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -21,20 +20,24 @@ JavaVM *jvm= nullptr; std::mutex mutex_obj; -jfieldID field_value = nullptr; +jfieldID signal_field_value = nullptr; +jfieldID passphrase_type_field_value = nullptr; jmethodID messageConstructorMethodID = nullptr; jmethodID messageToSendMethodID = nullptr; jmethodID notifyHandShakeMethodID = nullptr; jmethodID needsFastPollMethodID = nullptr; jmethodID passphraseRequiredMethodID = nullptr; -jmethodID method_values = nullptr; +jmethodID sync_handshake_signal_values = nullptr; +jmethodID passphrase_status_values = nullptr; +jmethodID passphrase_callback_values = nullptr; jobject objj = nullptr; jclass messageClass = nullptr; -jclass identityClass = nullptr;; +jclass identityClass = nullptr; jclass signalClass = nullptr; jclass engineClass = nullptr; +jclass passphraseTypeClass = nullptr; namespace JNISync { JNIEnv* env() { @@ -72,6 +75,8 @@ void jni_init() { _env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/_Identity"))); signalClass = reinterpret_cast( _env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/SyncHandshakeSignal"))); + passphraseTypeClass = reinterpret_cast( + _env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/PassphraseType"))); engineClass = reinterpret_cast(_env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/Engine"))); messageConstructorMethodID = _env->GetMethodID(messageClass, "", "(J)V"); @@ -90,19 +95,47 @@ void jni_init() { passphraseRequiredMethodID = _env->GetMethodID( engineClass, "passphraseRequiredFromC", - "()[B"); + "(Lfoundation/pEp/jniadapter/PassphraseType;)[B"); - method_values = JNISync::env()->GetStaticMethodID(signalClass, "values", + sync_handshake_signal_values = JNISync::env()->GetStaticMethodID(signalClass, "values", "()[Lfoundation/pEp/jniadapter/SyncHandshakeSignal;"); - field_value = JNISync::env()->GetFieldID(signalClass, "value", "I"); + passphrase_status_values = JNISync::env()->GetStaticMethodID(passphraseTypeClass, "values", + "()[Lfoundation/pEp/jniadapter/PassphraseType;"); + signal_field_value = JNISync::env()->GetFieldID(signalClass, "value", "I"); + passphrase_type_field_value = JNISync::env()->GetFieldID(passphraseTypeClass, "value", "I"); } -char* JNIAdapter::passphraseRequiredCallback() { +char* JNIAdapter::passphraseRequiredCallback(const PEP_STATUS status) { pEpLog("called"); + jobject status_ = nullptr; + { + assert(passphraseTypeClass); + assert(passphrase_status_values); + assert(passphrase_type_field_value); + + jobjectArray values = (jobjectArray) JNISync::env()->CallStaticObjectMethod(passphraseTypeClass, + passphrase_status_values); + if (JNISync::env()->ExceptionCheck()) { + JNISync::env()->ExceptionClear(); + throw_pEp_Exception(JNISync::env(), PEP_UNKNOWN_ERROR); + } + + jsize values_size = JNISync::env()->GetArrayLength(values); + for (jsize i = 0; i < values_size; i++) { + jobject element = JNISync::env()->GetObjectArrayElement(values, i); + assert(element); + jint value = JNISync::env()->GetIntField(element, passphrase_type_field_value); + if (value == (jint) status) { + status_ = element; + break; + } + JNISync::env()->DeleteLocalRef(element); + } + } assert(objj && passphraseRequiredMethodID); - jobject ppJO = JNISync::env()->CallObjectMethod(objj, passphraseRequiredMethodID); + jobject ppJO = JNISync::env()->CallObjectMethod(objj, passphraseRequiredMethodID, status_); if (JNISync::env()->ExceptionCheck()) { JNISync::env()->ExceptionDescribe(); JNISync::env()->ExceptionClear(); @@ -160,11 +193,11 @@ PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handsha jobject signal_ = nullptr; { assert(signalClass); - assert(method_values); - assert(field_value); + assert(sync_handshake_signal_values); + assert(signal_field_value); jobjectArray values = (jobjectArray) JNISync::env()->CallStaticObjectMethod(signalClass, - method_values); + sync_handshake_signal_values); if (JNISync::env()->ExceptionCheck()) { JNISync::env()->ExceptionClear(); return PEP_UNKNOWN_ERROR; @@ -174,7 +207,7 @@ PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handsha for (jsize i = 0; i < values_size; i++) { jobject element = JNISync::env()->GetObjectArrayElement(values, i); assert(element); - jint value = JNISync::env()->GetIntField(element, field_value); + jint value = JNISync::env()->GetIntField(element, signal_field_value); if (value == (jint) signal) { signal_ = element; break; diff --git a/src/cxx/passphrase_callback.hh b/src/cxx/passphrase_callback.hh index 8482077..e0f9804 100644 --- a/src/cxx/passphrase_callback.hh +++ b/src/cxx/passphrase_callback.hh @@ -5,11 +5,10 @@ namespace pEp { namespace JNIAdapter { - char* passphraseRequiredCallback(); + char* passphraseRequiredCallback(const PEP_STATUS status); - template PEP_STATUS passphraseWrap( - PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); - }; -}; + template PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); + } +} #include "passphrase_callback.hxx" \ No newline at end of file diff --git a/src/cxx/passphrase_callback.hxx b/src/cxx/passphrase_callback.hxx index 20bfa36..e812841 100644 --- a/src/cxx/passphrase_callback.hxx +++ b/src/cxx/passphrase_callback.hxx @@ -5,8 +5,7 @@ namespace pEp { namespace JNIAdapter { - template PEP_STATUS passphraseWrap( - PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a) { + template PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a) { pEpLog("cached passphrase mode"); bool retryAgain = false; int maxRetries = 3; @@ -17,14 +16,16 @@ namespace pEp { pEpLog("calling passphrase_cache.api from basic_api"); status = passphrase_cache.api(f, session, a...); pEpLog("PEP_STATUS:" << status); - if (status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE) { + if (status == PEP_PASSPHRASE_REQUIRED || + status == PEP_WRONG_PASSPHRASE || + status == PEP_PASSPHRASE_FOR_NEW_KEYS_REQUIRED) + { pEpLog("none of the cached passphrases worked"); if (retryCount < maxRetries) { // call the app - char *_passphrase = passphraseRequiredCallback(); + char *_passphrase = passphraseRequiredCallback(status); pEpLog("callback returned, config_passphrase() with new passphrase"); - PEP_STATUS status = ::config_passphrase(session, - passphrase_cache.add(_passphrase)); + PEP_STATUS status = ::config_passphrase(session, passphrase_cache.add(_passphrase)); retryAgain = true; retryCount++; } else { diff --git a/src/java/foundation/pEp/jniadapter/AbstractEngine.java b/src/java/foundation/pEp/jniadapter/AbstractEngine.java index ca2e09b..1b18fca 100644 --- a/src/java/foundation/pEp/jniadapter/AbstractEngine.java +++ b/src/java/foundation/pEp/jniadapter/AbstractEngine.java @@ -113,7 +113,8 @@ abstract class AbstractEngine extends UniquelyIdentifiable implements AbstractEn private int notifyHandshakeCallFromC(_Identity _myself, _Identity _partner, SyncHandshakeSignal _signal) { Identity myself = new Identity(_myself); - Identity partner = new Identity(_partner); + Identity partner = (_partner != null) ? new Identity(_partner) : null; + System.out.println("pEpSync" +"notifyHandshakeCallFromC: " + notifyHandshakeCallback); if (notifyHandshakeCallback != null) { notifyHandshakeCallback.notifyHandshake(myself, partner, _signal); @@ -123,11 +124,11 @@ abstract class AbstractEngine extends UniquelyIdentifiable implements AbstractEn return 0; } - private byte[] passphraseRequiredFromC() { + private byte[] passphraseRequiredFromC(final PassphraseType passphraseType) { String ret = ""; if (passphraseRequiredCallback != null) { System.out.println("calling passphraseRequiredCallback on engine ObjID:" + getId()); - ret = passphraseRequiredCallback.passphraseRequired(); + ret = passphraseRequiredCallback.passphraseRequired(passphraseType); } else { System.out.println("no callback registered on engine ObjID:" + getId()); // if this happens (no callback registered diff --git a/src/java/foundation/pEp/jniadapter/Sync.java b/src/java/foundation/pEp/jniadapter/Sync.java index b4fc818..21f28e0 100644 --- a/src/java/foundation/pEp/jniadapter/Sync.java +++ b/src/java/foundation/pEp/jniadapter/Sync.java @@ -16,7 +16,7 @@ public interface Sync { } interface PassphraseRequiredCallback { - String passphraseRequired(); + String passphraseRequired(final PassphraseType pEpStatus); } public class DefaultCallback implements Sync.MessageToSendCallback, Sync.NotifyHandshakeCallback, Sync.NeedsFastPollCallback { diff --git a/src/passphrase_callback.hh b/src/passphrase_callback.hh index 8482077..e0f9804 100644 --- a/src/passphrase_callback.hh +++ b/src/passphrase_callback.hh @@ -5,11 +5,10 @@ namespace pEp { namespace JNIAdapter { - char* passphraseRequiredCallback(); + char* passphraseRequiredCallback(const PEP_STATUS status); - template PEP_STATUS passphraseWrap( - PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); - }; -}; + template PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); + } +} #include "passphrase_callback.hxx" \ No newline at end of file diff --git a/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java b/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java index 4265690..f307984 100644 --- a/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java +++ b/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java @@ -51,10 +51,11 @@ class TestAlice { // Register callback passphraseRequired() ctx.engine.setPassphraseRequiredCallback(new Sync.PassphraseRequiredCallback() { @Override - public String passphraseRequired() { + public String passphraseRequired(PassphraseType type) { log("passphraseRequired() called"); log("Please Enter Passphrase..."); sleep(2000); + assert type == PassphraseType.pEpPassphraseRequired; return "passphrase_alice"; } }); diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java new file mode 100644 index 0000000..ff3796b --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java @@ -0,0 +1,194 @@ +package foundation.pEp.jniadapter.test.speedtest; + +import java.text.ParseException; +import java.util.regex.*; + +/** + * MT999 is a Free Format Message. + */ + +public class MT999 extends SWIFTMsg { + + String trn; + String rr; + String narrative; + + /** + * Construct MT999 message by parsing. + * + * @param txt text to parse + * @throws ParseException if not a valid MT999 message + */ + + public MT999(String txt) throws ParseException { + Matcher m = MTConstants.mt999_pattern.matcher(txt); + if (!m.matches()) + throw new ParseException("not a valid MT999 message", 0); + + // retrieve Basic Header and Application Header fields + retrieveHeader(m); + + // no User Header Block + + // Text Block + trn = m.group("trn"); + rr = m.group("rr"); + narrative = m.group("narrative"); + } + + /** + * Construct MT999 message. + * + * @param srcAddress is sender's address + * @param dstAddress is receiver's address + * @param dir "I" for incoming message "O" for + * outgoing message + * @param id transaction id + * @param payload freeform text + * @param session session number + * @param sequence sequence number + */ + + public MT999(String srcAddress, String dstAddress, String dir, String id, String relref, String payload, + String session, String sequence) { + + if (srcAddress.length() != 12) + throw new IllegalArgumentException("srcAddress must be 12 characters"); + if (dstAddress.length() != 12) + throw new IllegalArgumentException("dstAddress must be 12 characters"); + if (dir.compareTo("I") != 0 && dir.compareTo("O") != 0) + throw new IllegalArgumentException("dir must be I or O"); + if (id.length() < 1 || id.length() > 16) + throw new IllegalArgumentException("id must be between 1 and 12 characters"); + if (relref.length() > 16) + throw new IllegalArgumentException("related reference must be at most 16 characters"); + if (payload.length() < 1) + throw new IllegalArgumentException("payload must have a value"); + + if (session.length() != 4) + throw new IllegalArgumentException("session must be 4 digits"); + if (sequence.length() != 6) + throw new IllegalArgumentException("sequence must be 6 digits"); + + mt999(); + + logicalTerminalAddress = srcAddress; + destinationAddress = dstAddress; + inputIdentifier = dir; + + trn = id; + rr = relref; + narrative = payload; + + sessionNumber = session; + sequenceNumber = sequence; + } + + /** + * Construct MT999 message with defaults for session and sequence. + * + * @param srcAddress is sender's address + * @param dstAddress is receiver's address + * @param dir "I" for incoming message "O" for + * outgoing message + * @param id transaction id + * @param payload freeform text + */ + + public MT999(String srcAddress, String dstAddress, String dir, String id, String relref, String payload) { + + + if (srcAddress.length() != 12) + throw new IllegalArgumentException("srcAddress must be 12 characters"); + if (dstAddress.length() != 12) + throw new IllegalArgumentException("dstAddress must be 12 characters"); + if (dir.compareTo("I") != 0 && dir.compareTo("O") != 0) + throw new IllegalArgumentException("dir must be I or O"); + if (id.length() < 1 || id.length() > 16) + throw new IllegalArgumentException("id must be between 1 and 12 characters"); + if (relref.length() > 16) + throw new IllegalArgumentException("related reference must be at most 16 characters"); + if (payload.length() < 1) + throw new IllegalArgumentException("payload must have a value"); + + mt999(); + + logicalTerminalAddress = srcAddress; + destinationAddress = dstAddress; + inputIdentifier = dir; + + trn = id; + rr = relref; + narrative = payload; + + sessionNumber = "0000"; + sequenceNumber = "000000"; + } + + /** + * Convert MT999 message by composing. + * + * @param fmt rendering format + * @return string representation of MT999 message + */ + + public String toString(MTConstants.Format fmt) { + String result; + // FIXME: rework to stringbuffer + switch (fmt) { + case MTFIN: + // Basic Header Block + result = "{1:"; + result += applicationIdentifier; + result += serviceIdentifier; + result += logicalTerminalAddress; + result += sessionNumber; + result += sequenceNumber; + result += "}"; + + // Application Header Block + result += "{2:"; + result += inputIdentifier; + result += messageType; + result += destinationAddress; + result += messagePriority; + result += "}"; + + // no User Header Block + + // Text Block + result += "{4:\n"; + result += ":20:" + trn + "\n"; + if (rr != null && rr.length() > 0) + result += ":21:" + rr +"\n"; + result += ":79:\n" + narrative; + result += "\n-}"; + return result; + + case MTXML: + throw new UnsupportedOperationException("MTXML not yet implemented"); + } + + throw new AssertionError("this should never happen"); + } + + /** + * Convert MT999 message by composing. + * + * @return string representation of MT999 message + */ + + public String toString() { + return toString(MTConstants.Format.MTFIN); + } + + // MT999 specifica + + private void mt999() { + applicationIdentifier = "F"; // FIN + serviceIdentifier = "01"; // FIN + messageType = "999"; + messagePriority = "N"; + } + +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java new file mode 100644 index 0000000..59b000f --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java @@ -0,0 +1,60 @@ +package foundation.pEp.jniadapter.test.speedtest; + +import java.util.regex.Pattern; + +public class MTConstants { + + /** + * Rendering Format of a SWIFT message. + */ + + enum Format { + MTFIN, MTXML + } + + /** + * SWIFTMessages have a Basic Header Block and an Application Header Block + */ + + static final String mt_regex = "(?ms)" + // Basic Header Block + + "\\{1:" // + + "(?\\w)" // + + "(?\\d{2})" // + + "(?\\w{12})" // + + "(?\\d{4})" // + + "(?\\d{5,6})" // + + "\\}" + + // Application Header Block + + "\\{2:" // + + "(?I|O)" // + + "(?\\d{3})" // + + "(\\d{10})?" // + + "(?\\w{12})" // + + "(.*)?" // + + "(?U|N|S)" // + + "\\}" + + // FIXME: User Header Block (the wrong way). The trailing .* is to make it useable for basic swift and mt999 ... + + "(\\{3:?(.*)\\})?(.*)" // + ; + + static final Pattern mt_pattern = Pattern.compile(mt_regex); + + static final String mt999_regex = mt_regex + // Text Block + + "\\{4:\n" // + + ":20:(?\\w{1,16})\n" // + + "(:21:(?.{1,16})\n)?" // + + ":79:(?.*?)" // + + "\n-\\}" // + // trailer + + ".*" + + ; + + static final Pattern mt999_pattern = Pattern.compile(mt999_regex); + +} + diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java new file mode 100644 index 0000000..f1b96d3 --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java @@ -0,0 +1,480 @@ +package foundation.pEp.jniadapter.test.speedtest; + +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Properties; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import foundation.pEp.jniadapter.Blob; +import foundation.pEp.jniadapter.Engine; +import foundation.pEp.jniadapter.Identity; +import foundation.pEp.jniadapter.Message; +import foundation.pEp.jniadapter.Pair; + +/** + * MTMsgCodec is the Codec class for encoding/decoding p≡p for SWIFT messages. + * + *

+ * See SWIFT message structure, MT message format, MT999 + *

+ *

+ * Copyright 2019, p≡p + * Security. + *

+ * + * @author Volker Birk + * @version %I% %G% + */ + +// FIXME: rework to stringbuffer + +public class MTMsgCodec { + public Engine pEp; + + /** + * Constructs a MessageCodec using p≡p engine. + * + * @param pEp_engine p≡p engine to use + */ + + public MTMsgCodec(Engine pEp_engine) { + pEp = pEp_engine; + } + + private final static String magicKeys = "pEpKeys"; + private final static String magicEnc = "pEpMessage"; + + private final static String pgp_regex = "(?ms)" + "-----BEGIN PGP (?.*?)-----\n" + "(\\w:.*?$\n)*" + "\n" + + "(?.*?)" + "-----END PGP (.*?)-----.*"; + + private static final Pattern pgp_pattern = Pattern.compile(pgp_regex); + + private static final String pgptypeMessage = "MESSAGE"; + private static final String pgptypePubkey = "PUBLIC KEY BLOCK"; + + // FIXME: private final static String uri_regex = "payto://swift/(?\\w+)"; + private final static String uri_regex = "(?\\w+)@BIC"; + + private final static Pattern uri_pattern = Pattern.compile(uri_regex); + + /** + * Strips PGP header and footer from encryption or key data. + * + * @param pgp_text text to work on + * @throws ParseException if text is not valid PGP data + * @return pgp_text without PGP header + */ + + protected static String stripFromPGP(String pgp_text) throws ParseException { + + Matcher m = pgp_pattern.matcher(pgp_text); + if (!m.matches()) + throw new ParseException("not a PGP block", 0); + + return m.group("block"); + } + + /** + * Adds PGP header and footer. + * + * @param payload text to decorate + * @param pgp_type PGP data type + * @return payload with added header and footer + */ + + protected static String addPGPHeader(String payload, String pgp_type) { + // FIXME: rework to stringbuffer + return "-----BEGIN PGP " + pgp_type + "-----\n\n" + payload + "\n-----END PGP " + pgp_type + "-----\n"; + } + + /** + * Decodes a BIC from an URI. + * + * @param uri the URI to decode from + * @throws ParseException if URI has not the correct form + * @return decoded BIC + */ + + public static String decodeBIC(String uri) throws ParseException { + Matcher m = uri_pattern.matcher(uri); + if (!m.matches()) + throw new ParseException("not a valid URI", 0); + + return m.group("bic"); + } + + /** + * Encodes a BIC into an URI. + * + * @param bic BIC to encode + * @return encoded URI + */ + + public static String encodeBIC(String bic) { + // return "payto://swift/" + bic + return bic + "@BIC"; + } + + /** + * Generates a list of transporting MT999 messages for a payload. + * + * @param from source address + * @param to destination address + * @param ii message direction + * @param trn message id + * @param payload payload to split + * @param magic magic string to mark as p≡p message + * + * @return array of String with MT999 messages + */ + + protected String[] transportMT999(String from, String to, String ii, String trn, String rr, String payload, + String magic) { + + Vector result = new Vector(); + payload = payload.trim(); + + int j = 1, f = 0, t = 0; + for (int i = payload.indexOf("\n"); i != -1; i = payload.indexOf("\n", i + 1), j++) { + + if (j % 34 == 0) { + t = i + 1; + String cont = t < payload.length() - 1 ? "." : ""; + MT999 mt999 = new MT999(from, to, ii, trn, rr, magic + "\n" + payload.substring(f, t) + cont); + result.add(mt999.toString()); + f = i + 1; + } + } + if (t < payload.length() - 1) { + int z = payload.charAt(payload.length() - 1) == '\n' ? 1 : 0; + MT999 mt999 = new MT999(from, to, ii, "23", // fixed trn + "", magic + "\n" + payload.substring(t, payload.length() - z)); + result.add(mt999.toString()); + } + + return result.toArray(new String[result.size()]); + } + + /** + * encodes a p≡p Message to a String with a serialized p≡p for SWIFT message. + * + * @param msg p≡p Message to encode if from or to are not set they're taken + * by parsing the header of longmsg; in this case + * message direction is being taken from there, too + * @param config properties with configuration + * @return String with encoded p≡p for SWIFT message + * @throws UnsupportedOperationException if EncFormat is other than + * None, Inline + * or PEP + */ + + public String encode(Message msg, Properties config) { + if (msg.getLongmsg() == null || msg.getLongmsg().length() < 1) + throw new IllegalArgumentException("longmsg must contain the message"); + + String result = ""; + + String msgid = msg.getId() != null && msg.getId().length() > 0 ? msg.getId() : "23"; + String dir = msg.getDir() == Message.Direction.Incoming ? "I" : "O"; + + if (msgid.length() > 12) { + if (msgid.substring(0, 3).compareTo("pEp.") == 0 && msgid.length() >= 15) + msgid = msgid.substring(4, 15); + else + msgid = msgid.substring(0, 11); + } + + String from = msg.getFrom() != null && msg.getFrom().address.length() > 0 ? msg.getFrom().address : null; + + String to = msg.getTo() != null && msg.getTo().size() > 0 && msg.getTo().elementAt(0).address.length() > 0 + ? msg.getTo().elementAt(0).address + : null; + + if (from != null) { + try { + from = decodeBIC(from); + if (from.length() != 12) + throw new IllegalArgumentException("from address must be URI with BIC12"); + } catch (ParseException ex) { + throw new IllegalArgumentException("from address must be URI with BIC12"); + } + } + + if (to != null) { + try { + to = decodeBIC(to); + if (to.length() != 12) + throw new IllegalArgumentException("to address must be URI with BIC12"); + } catch (ParseException ex) { + throw new IllegalArgumentException("to address must be URI with BIC12"); + } + } + + switch (msg.getEncFormat()) { + case None: + result = msg.getLongmsg(); // we send the message unmodified + if (result.substring(0, 3).compareTo("{1:") == 0) { + // probably SWIFT MTFIN + + if (from == null || to == null || msgid == null) { + // parse SWIFT header + SWIFTMsg mh = new SWIFTMsg(); + + try { + mh.parseHeader(result); + } catch (ParseException ex) { + throw new UnsupportedOperationException("unsupported message format"); + } + + if (from == null) + from = mh.logicalTerminalAddress; + if (to == null) + to = mh.destinationAddress; + dir = mh.inputIdentifier; + } + } else if (result.substring(0, 1).compareTo("<") == 0) { + // probably XML + + // FIXME: let's do the job for MTXML, too + return result; + } else { // we don't know this format, so let it be + return result; + } + + if(msg.getAttachments() != null) + for (int i = 0; i < msg.getAttachments().size(); ++i) { // FIXME: can attachments become null? + Blob attach = msg.getAttachments().elementAt(i); + String magic; + + if (attach.mime_type.compareToIgnoreCase("application/pgp-keys") == 0) + magic = magicKeys; + else + break; // don't know this MIME type + + // we send an MT999 with the keys + String payload; + try { + payload = stripFromPGP(new String(attach.data, StandardCharsets.UTF_8)); + } catch (ParseException ex) { + // cannot parse this + break; + } + + String[] msgs = transportMT999(from, to, dir, msgid, "", payload, magic); + + for (int j = 0; j < msgs.length; ++j) { + if (j > 0) + result += "\n"; + result += msgs[j].toString(); + } + } + break; + + case PEP: + case PGPMIME: + case Inline: + if (from == null || to == null) + throw new IllegalArgumentException("from and to must be set to URIs with BIC12"); + + String pgp_txt; + Vector attachments = null; + + if (msg.getEncFormat() == Message.EncFormat.PEP || msg.getEncFormat() == Message.EncFormat.PGPMIME) { + if (msg.getAttachments() == null || msg.getAttachments().size() != 2) + throw new IllegalArgumentException("no valid message format"); + Blob attach = msg.getAttachments().elementAt(1); + pgp_txt = new String(attach.data, StandardCharsets.UTF_8); + } else /* Inline */ { + pgp_txt = msg.getLongmsg(); + } + + String payload; + try { + payload = stripFromPGP(pgp_txt); + } catch (ParseException ex) { + // cannot parse this + throw new IllegalArgumentException("illegal encryption text"); + } + String[] msgs = transportMT999(from, to, dir, msgid, "", payload, magicEnc); + + for (int j = 0; j < msgs.length; ++j) { + if (j > 0) + result += "\n"; + result += msgs[j].toString(); + } + + if (msg.getEncFormat() == Message.EncFormat.Inline) { + String[] attached_key = null; + attachments = msg.getAttachments(); + + for (int i = 0; attachments != null && i < attachments.size(); ++i) { + Blob attach = attachments.elementAt(i); +// if (attach.mime_type.compareTo("application/pgp-keys") == 0) { + // we send an MT999 with the keys + try { + payload = stripFromPGP(new String(attach.data, StandardCharsets.UTF_8)); + } catch (ParseException ex) { + // cannot parse this + break; + } + msgs = transportMT999(from, to, dir, msgid, "", payload, magicKeys); + + for (int j = 0; j < msgs.length; ++j) { + if (j > 0) + result += "\n"; + result += msgs[j].toString(); + } +// } else { +// throw new UnsupportedOperationException( +// "only application/pgp-keys is supported with Inline but got " + attach.mime_type); +// } + } + } + break; + + default: + throw new UnsupportedOperationException("unsupported encryption format"); + } + + return result; + } + + /** + * Creates p≡p message from MTFIN or MTXML using SWIFT header info + * + * @param header MTFIN header structure + * @param txt MTFIN message text + * @return p≡p message + */ + + protected Message pEpMessageFromSWIFTMessage(SWIFTMsg header, String txt) { + Message m = new Message(); + + Identity from = new Identity(); + from.address = encodeBIC(header.logicalTerminalAddress); + m.setFrom(from); + + Identity to = new Identity(); + to.address = encodeBIC(header.destinationAddress); + Vector _to = new Vector(); + _to.add(to); + m.setTo(_to); + + m.setDir(header.inputIdentifier.compareTo("I") == 0 ? Message.Direction.Incoming : Message.Direction.Outgoing); + + m.setLongmsg(txt); + + ArrayList> al = new ArrayList>(); + Pair field = new Pair("X-pEp-Version", pEp.getProtocolVersion()); + al.add(field); + m.setOptFields(al); + return m; + } + + /** + * decodes p≡p Messages from a String with serialized p≡p for SWIFT messages. + * + * @param txt String to decode from + * @throws ParseException if the String does not contain SWIFT messages only + * @return array with p≡p Messages + * + */ + + public Message[] decode(String txt) throws ParseException { + Vector result = new Vector(); + + if (txt.substring(0, 3).compareTo("{1:") == 0) { + // probably SWIFT MTFIN + + int f = 0; + int t = txt.indexOf("{1:", 3); + if (t == -1) + t = txt.length(); + + String key_payload = ""; + String enc_payload = ""; + while (f < txt.length()) { + String _txt = txt.substring(f, t); + + f = t; + t = txt.indexOf("{1:", f + 3); + if (t == -1) + t = txt.length(); + + Message last = null; + + SWIFTMsg m = new SWIFTMsg(); + m.parseHeader(_txt); + + boolean done = false; + if (m.messageType.compareTo("999") == 0) { + MT999 _m = new MT999(_txt); + String narrative = _m.narrative.trim(); + if (narrative.substring(0, magicKeys.length()).compareTo(magicKeys) == 0) { + key_payload += narrative.substring(magicKeys.length()).trim(); + if (key_payload.substring(key_payload.length()).compareTo(".") == 0) { + key_payload = key_payload.substring(0, key_payload.length() - 2); + } else { + // p≡p keys + String keydata = addPGPHeader(key_payload, pgptypePubkey); + key_payload = ""; + + if (last == null) + last = pEpMessageFromSWIFTMessage(m, "p≡p keys"); + + Vector a = new Vector(); + Blob b = new Blob(); + b.data = keydata.getBytes(StandardCharsets.UTF_8); + b.mime_type = "application/pgp-keys"; + b.filename = "pEpKeys.asc"; + a.add(b); + last.setAttachments(a); + + result.add(last); + last = null; + + } + done = true; + } else if (narrative.substring(0, magicEnc.length()).compareTo(magicEnc) == 0) { + // p≡p encrypted data + enc_payload += narrative.substring(magicEnc.length()).trim(); + if (enc_payload.substring(enc_payload.length()).compareTo(".") == 0) { + enc_payload = enc_payload.substring(0, enc_payload.length() - 2); + } else { + // p≡p encryption + String encdata = addPGPHeader(enc_payload, pgptypeMessage); + Message r = pEpMessageFromSWIFTMessage(m, encdata); + r.setEncFormat(Message.EncFormat.Inline); + result.add(r); + } + done = true; + } + } + + if (!done) { + last = pEpMessageFromSWIFTMessage(m, _txt); + result.add(last); + } + } + } else if (txt.substring(0, 1).compareTo("<") == 0) { + // probably XML + + // FIXME: let's do the job for MTXML, too + throw new UnsupportedOperationException("XML not yet implemented"); + } else { // we don't know this format + throw new ParseException("not a valid SWIFT message", 0); + } + + Message[] _result = new Message[result.size()]; + return result.toArray(_result); + } +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/Makefile b/test/java/foundation/pEp/jniadapter/test/speedtest/Makefile new file mode 100644 index 0000000..9b9204d --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/Makefile @@ -0,0 +1,38 @@ +include ../../../../../../../Makefile.conf +include ../Makefile.conf + +TEST_UNIT_NAME=speedtest + +JAVA_CLASSES = \ + MT999.class \ + MTConstants.class \ + MTMsgCodec.class \ + SpeedTest.class \ + SWIFTMsg.class + +.PHONY: pitytest compile alice test clean + +all: alice compile + +pitytest: + $(MAKE) -C $(PITYTEST_DIR) + +alice: compile clean-pep-home-alice + cd $(JAVA_CWD);pwd;HOME=$(JAVA_PEP_HOME_DIR_ALICE) $(JAVA) $(JAVA_PKG_BASENAME).$(TEST_UNIT_NAME).SpeedTest + +compile: $(JAVA_CLASSES) pitytest + +%.class: %.java + cd $(JAVA_CWD);javac -cp $(CLASSPATH) $(JAVA_PKG_BASEPATH)/$(TEST_UNIT_NAME)/$< + +clean: + rm -f $(JAVA_CLASSES) + rm -f *.class + rm -f *.log + rm -Rf .gnupg + rm -Rf .lldb + +clean-pep-home: clean-pep-home-alice + +clean-pep-home-alice: + rm -rf $(PEP_HOME_DIR_ALICE)/.pEp diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SWIFTMsg.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SWIFTMsg.java new file mode 100644 index 0000000..268992a --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SWIFTMsg.java @@ -0,0 +1,58 @@ +package foundation.pEp.jniadapter.test.speedtest; + +import java.text.ParseException; +import java.util.regex.*; + +public class SWIFTMsg { + // Basic Header Block + public String applicationIdentifier; + public String serviceIdentifier; + public String logicalTerminalAddress; + public String sessionNumber; + public String sequenceNumber; + + // Application Header Block + public String inputIdentifier; + public String messageType; + public String destinationAddress; + public String messagePriority; + + /** + * retrieve header fields of Basic Header Block and Application Header + * Block + * + * @param m Matcher object of a regex result + */ + + public void retrieveHeader(Matcher m) { + // Basic Header Block + applicationIdentifier = m.group("ai"); + serviceIdentifier = m.group("si"); + logicalTerminalAddress = m.group("lta"); + sessionNumber = m.group("sn"); + sequenceNumber = m.group("sqn"); + + // Application Header Block + inputIdentifier = m.group("ii"); + messageType = m.group("mt"); + destinationAddress = m.group("da"); + messagePriority = m.group("mp"); + } + + /** + * parse MTFIN header and retrieve header fields into variables. + * + * @param txt MTxxx message to parse + * @throws ParseException if not a valid MTxxx message + */ + + public void parseHeader(String txt) throws ParseException { +// String header = txt.substring(0, 50); + + Matcher m = MTConstants.mt_pattern.matcher(txt); + if (!m.matches()) + throw new ParseException("not a valid MTxxx message", 0); + + retrieveHeader(m); + } +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java new file mode 100644 index 0000000..6b2f81e --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java @@ -0,0 +1,295 @@ +package foundation.pEp.jniadapter.test.speedtest; + +import java.text.ParseException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.charset.StandardCharsets; +import java.util.Vector; +import java.util.Scanner; + +import foundation.pEp.jniadapter.*; + +public class SpeedTest { + private static Engine pEp = new Engine(); + private static MTMsgCodec codec = new MTMsgCodec(pEp); + private static Identity me = new Identity(true); + private static Identity you = new Identity(); + + private static long decodingCount = 0; + private static long encodingCount = 0; + private static int deth = 0; + private static int enth = 0; + + private static String testData = null; + + + protected static void decodingTest(Engine eng, long n, String testDataEnc) { + for (long i = 0; i < n; ++i) { + try { + Message[] msgs = codec.decode(testDataEnc); + Vector keys = new Vector(); + Engine.decrypt_message_Return ret = eng.decrypt_message(msgs[0], keys, 0); + String txt = ret.dst.getLongmsg(); + } catch (ParseException ex) { + System.err.println("error: parsing test data"); + System.exit(3); + } + } + } + + protected class DecodingThread extends Thread { + private long _n; + private String _testDataEnc; + private Engine _localpEp; + + public DecodingThread(long n, String testDataEnc) { + _n = n; + _testDataEnc = testDataEnc; + _localpEp = new Engine(); + } + + public void run() { + decodingTest(_localpEp, _n, _testDataEnc); + } + } + + private static Message encrypt(Engine eng, String data) { + Message m = new Message(); + m.setDir(Message.Direction.Outgoing); + m.setFrom(me); + Vector to = new Vector(); + to.add(you); + m.setTo(to); + m.setLongmsg(data); + return eng.encrypt_message(m, null, Message.EncFormat.Inline); + } + + protected static void encodingTest(Engine eng, long n, String testData) { + for (long i = 0; i < n; ++i) { + Message enc = encrypt(eng, testData); + String txt = codec.encode(enc, null); + } + } + + protected class EncodingThread extends Thread { + private long _n; + private String _testData; + private Engine _localpEp; + + public EncodingThread(long n, String testData) { + _n = n; + _testData = testData; + _localpEp = new Engine(); + } + + public void run() { + encodingTest(_localpEp, _n, _testData); + } + } + + private static void parseOpts(String[] args) { + for (int i = 0; i < args.length; ++i) { + if (args[i].compareTo("-h") == 0 || args[i].compareTo("--help") == 0) { + System.out.println("SpeedTest [-e |--encode NUMBER] [-d | --decode NUMBER] [-f | --file TESTDATA] [-jd | --decoding-threads DT] [-je | --encoding-threads] [-h | --help]\n" + + "\nEncodes and/or decodes messages to measure the speed.\n\n" + + " -d, --decode NUMBER decode NUMBER messages per thread\n" + + " -e, --encode NUMBER encode NUMBER messages per thread\n" + + " -f, --file TESTDATA file with test data as UTF-8 encoded text\n" + + " -jd, --decoding-threads DT starting DT threads for decoding\n" + + " -je, --encoding-threads ET starting ET threads for encoding\n" + + " -h, --help show this help message\n" + + "\nThis program encrypts and encodes, and decrypts and decodes test data\n" + + "NUMBER times, respectively. If you omit -f it will encode a default data set.\n" + ); + System.exit(0); + } else if (args[i].compareTo("-d") == 0 || args[i].compareTo("--decode") == 0) { + try { + decodingCount = Long.parseLong(args[i + 1]); + ++i; + } catch (NumberFormatException ex) { + System.err.println(String.format("error: decimal number expected but found %s", args[i + 1])); + System.exit(1); + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i])); + System.exit(1); + } + } else if (args[i].compareTo("-e") == 0 || args[i].compareTo("--encode") == 0) { + try { + encodingCount = Long.parseLong(args[i + 1]); + ++i; + } catch (NumberFormatException ex) { + System.err.println(String.format("error: decimal number expected but found %s", args[i + 1])); + System.exit(1); + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i])); + System.exit(1); + } + } else if (args[i].compareTo("-f") == 0 || args[i].compareTo("--file") == 0) { + String filename = ""; + + try { + filename = args[i + 1]; + ++i; + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.println(String.format("error: %s is requiring a filename as argument", args[i])); + System.exit(1); + } + + try { + if (filename.compareTo("-") == 0) { + Scanner s = new Scanner(System.in).useDelimiter("\\A"); + testData = s.hasNext() ? s.next() : ""; + } else { + testData = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8); + } + + } catch (Exception ex) { + System.err.println(String.format("error: cannot read file %s", args[i])); + System.exit(2); + } + } else if (args[i].compareTo("-jd") == 0 || args[i].compareTo("----decoding-threads") == 0) { + try { + deth = Integer.parseInt(args[i + 1]); + ++i; + } catch (NumberFormatException ex) { + System.err.println(String.format("error: decimal number expected but found %s", args[i + 1])); + System.exit(1); + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i])); + System.exit(1); + } + } else if (args[i].compareTo("-je") == 0 || args[i].compareTo("----encoding-threads") == 0) { + try { + enth = Integer.parseInt(args[i + 1]); + ++i; + } catch (NumberFormatException ex) { + System.err.println(String.format("error: decimal number expected but found %s", args[i + 1])); + System.exit(1); + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i])); + System.exit(1); + } + } else { + System.err.println(String.format("illegal parameter: %s", args[i])); + System.exit(1); + } + } + } + + public static void main(String[] args) { + System.out.println("Initializing..."); + int cores = Runtime.getRuntime().availableProcessors(); + System.out.println(String.format("Number of cores: %d", cores)); + + encodingCount = 10; + decodingCount = 10; + enth = cores; + deth = cores; + + MT999 testMessage = new MT999("232323232323", "424242424242", "O", "23", "", "Hello, world"); + testData = testMessage.toString(); + + parseOpts(args); + + if (decodingCount < 0 || encodingCount < 0 || !(encodingCount > 0 || decodingCount > 0)) { + System.err.println("arguments error: -d or -e (or both) must be used with a positive number"); + System.exit(1); + } + + if (deth < 1 || enth < 1 || deth > 1024 || enth > 1024) { + System.err.println("arguments error: -jd or -je must be used with a positive number (max 1024)"); + System.exit(1); + } + + me.address = MTMsgCodec.encodeBIC("232323232323"); + me.username = "sender"; + me.user_id = "23"; + pEp.myself(me); + + you.address = MTMsgCodec.encodeBIC("424242424242"); + you.username = "receiver"; + you.user_id = me.user_id; + pEp.myself(you); // make a key for it + + Message enc = encrypt(pEp, testData); + String testDataEnc = codec.encode(enc, null); + + Thread[] dts = new Thread[deth]; + Thread[] ets = new Thread[enth]; + + System.out.println("Creating " + deth + " decoding threads"); + + // create threads + if (deth > 1) { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < deth; ++i) { + dts[i] = st.new DecodingThread(decodingCount, testDataEnc); + } + } + + System.out.println("Creating " + enth + " encoding threads"); + if (enth > 1) { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < enth; ++i) { + ets[i] = st.new EncodingThread(encodingCount, testData); + } + } + + + // Benchmark starting + System.out.println("Starting benchmark..."); + System.out.println("decoding " + decodingCount + " msgs per thread"); + long startTime = System.nanoTime(); + + if (deth == 1) { + decodingTest(pEp, decodingCount, testDataEnc); + } else { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < deth; ++i) { + dts[i].start(); + } + for (int i = 0; i < deth; ++i) { + try { + dts[i].join(); + } catch (InterruptedException ex) { + } + } + } + + + long decodingTime = System.nanoTime(); + long decodingDelta = decodingTime - startTime; + + System.out.println("encoding " + decodingCount + " msgs per thread"); + if (enth == 1) { + encodingTest(pEp, decodingCount, testData); + } else { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < enth; ++i) { + ets[i].start(); + } + for (int i = 0; i < enth; ++i) { + try { + ets[i].join(); + } catch (InterruptedException ex) { + } + } + } + + long encodingDelta = System.nanoTime() - decodingTime; + + double encTimeSecs = (double) encodingDelta / 1000000000; + double decTimeSecs = (double) decodingDelta / 1000000000; + + long encTotal = encodingCount * enth; + long decTotal = decodingCount * deth; + + double enr = (double) encTotal / encTimeSecs; + double der = (double) decTotal / decTimeSecs; + + System.out.println(String.format( + "encrypted and encoded %d messages in %.3f sec. (%.1f msgs/sec) using %d threads\n" + + "decrypted and decoded %d messages in %.3f sec. (%.1f msgs/sec) using %d threads", + encTotal, encTimeSecs, enr, enth, decTotal, decTimeSecs, der, deth)); + } +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh b/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh new file mode 100755 index 0000000..4f4fd65 --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +ENGINE_LIB_PATH=$HOME/local-default/lib + +export HOME=../resources/per-user-dirs/alice +export LD_LIBRARY_PATH=$ENGINE_LIB_PATH +export DYLD_LIBRARY_PATH=$ENGINE_LIB_PATH + +cd ../../../../../ +java -enableassertions -Xcheck:jni -cp .:../../src -Djava.library.path=.:../../src foundation.pEp.jniadapter.test.speedtest.SpeedTest $@