merge in default branch

JNI-106
heck 3 years ago
commit 175519edd2

@ -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

@ -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

@ -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

@ -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

@ -12,6 +12,7 @@ tstylesheet {
template "interface" document("../cxx/foundation_pEp_jniadapter_{@name}.cc", "text")
||
#include <cassert>
#include <pEp/blacklist.h>
#include <pEp/openpgp_compat.h>
#include <pEp/key_reset.h>
@ -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 {
||

@ -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

@ -3,7 +3,6 @@
#include <pEp/keymanagement.h>
#include <pEp/message_api.h>
#include <pEp/sync_api.h>
#include <pEp/Adapter.hh>
#include <pEp/pEpLog.hh>
#include <pEp/passphrase_cache.hh>
#include <pEp/callback_dispatcher.hh>
@ -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<jclass>(
_env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/SyncHandshakeSignal")));
passphraseTypeClass = reinterpret_cast<jclass>(
_env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/PassphraseType")));
engineClass = reinterpret_cast<jclass>(_env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/Engine")));
messageConstructorMethodID = _env->GetMethodID(messageClass, "<init>", "(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;

@ -5,11 +5,10 @@
namespace pEp {
namespace JNIAdapter {
char* passphraseRequiredCallback();
char* passphraseRequiredCallback(const PEP_STATUS status);
template<typename... A> PEP_STATUS passphraseWrap(
PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a);
};
};
template<typename... A> PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a);
}
}
#include "passphrase_callback.hxx"

@ -5,8 +5,7 @@
namespace pEp {
namespace JNIAdapter {
template<typename... A> PEP_STATUS passphraseWrap(
PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a) {
template<typename... A> 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 {

@ -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

@ -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 {

@ -5,11 +5,10 @@
namespace pEp {
namespace JNIAdapter {
char* passphraseRequiredCallback();
char* passphraseRequiredCallback(const PEP_STATUS status);
template<typename... A> PEP_STATUS passphraseWrap(
PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a);
};
};
template<typename... A> PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a);
}
}
#include "passphrase_callback.hxx"

@ -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";
}
});

@ -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 <code>"I"</code> for incoming message <code>"O"</code> 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 <code>"I"</code> for incoming message <code>"O"</code> 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";
}
}

@ -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:" //
+ "(?<ai>\\w)" //
+ "(?<si>\\d{2})" //
+ "(?<lta>\\w{12})" //
+ "(?<sn>\\d{4})" //
+ "(?<sqn>\\d{5,6})" //
+ "\\}"
// Application Header Block
+ "\\{2:" //
+ "(?<ii>I|O)" //
+ "(?<mt>\\d{3})" //
+ "(\\d{10})?" //
+ "(?<da>\\w{12})" //
+ "(.*)?" //
+ "(?<mp>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:(?<trn>\\w{1,16})\n" //
+ "(:21:(?<rr>.{1,16})\n)?" //
+ ":79:(?<narrative>.*?)" //
+ "\n-\\}" //
// trailer
+ ".*"
;
static final Pattern mt999_pattern = Pattern.compile(mt999_regex);
}

@ -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 pp for SWIFT messages.
*
* <p>
* See <a href=
* "https://www.sepaforcorporates.com/swift-for-corporates/read-swift-message-structure/"
* target="_blank">SWIFT message structure</a>, <a href=
* "https://www.ibm.com/support/knowledgecenter/SSRH32_3.0.0_SWS/mt_msg_format.html"
* target="_blank">MT message format</a>, <a href=
* "http://www.iotafinance.com/en/SWIFT-ISO15022-Message-type-MT999.html" target
* ="_blank">MT999</a>
* </p>
* <p>
* Copyright 2019, <a href="https://pep.security" target="_blank">pp
* Security</a>.
* </p>
*
* @author Volker Birk
* @version %I% %G%
*/
// FIXME: rework to stringbuffer
public class MTMsgCodec {
public Engine pEp;
/**
* Constructs a MessageCodec using pp engine.
*
* @param pEp_engine pp 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 (?<type>.*?)-----\n" + "(\\w:.*?$\n)*" + "\n"
+ "(?<block>.*?)" + "-----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/(?<bic>\\w+)";
private final static String uri_regex = "(?<bic>\\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 <code>pgp_text</code> 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 <code>payload</code> 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 pp 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<String> result = new Vector<String>();
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 pp Message to a String with a serialized pp for SWIFT message.
*
* @param msg pp Message to encode if from or to are not set they're taken
* by parsing the header of <code>longmsg</code>; in this case
* message direction is being taken from there, too
* @param config properties with configuration
* @return String with encoded pp for SWIFT message
* @throws UnsupportedOperationException if <code>EncFormat</code> is other than
* <code>None</code>, <code>Inline</code>
* or <code>PEP</code>
*/
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<Blob> 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 pp message from MTFIN or MTXML using SWIFT header info
*
* @param header MTFIN header structure
* @param txt MTFIN message text
* @return pp 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<Identity> _to = new Vector<Identity>();
_to.add(to);
m.setTo(_to);
m.setDir(header.inputIdentifier.compareTo("I") == 0 ? Message.Direction.Incoming : Message.Direction.Outgoing);
m.setLongmsg(txt);
ArrayList<Pair<String, String>> al = new ArrayList<Pair<String, String>>();
Pair<String, String> field = new Pair<String, String>("X-pEp-Version", pEp.getProtocolVersion());
al.add(field);
m.setOptFields(al);
return m;
}
/**
* decodes pp Messages from a String with serialized pp for SWIFT messages.
*
* @param txt String to decode from
* @throws ParseException if the String does not contain SWIFT messages only
* @return array with pp Messages
*
*/
public Message[] decode(String txt) throws ParseException {
Vector<Message> result = new Vector<Message>();
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<Blob> a = new Vector<Blob>();
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);
}
}

@ -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

@ -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);
}
}

@ -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<String> keys = new Vector<String>();
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<Identity> to = new Vector<Identity>();
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...");