merge in JNI-114 - Callback And Retry for methods that might need a passphrase Release_2.1.0-RC10
authorheck <heck@pep.foundation>
Wed, 08 Jul 2020 23:48:14 +0200
changeset 897facc79349b19
parent 882 8388ddfe965b
parent 896 4368ec659a5c
child 898 a9899ba15a45
merge in JNI-114 - Callback And Retry for methods that might need a passphrase
     1.1 --- a/.hgtags	Wed Jul 08 02:29:45 2020 +0200
     1.2 +++ b/.hgtags	Wed Jul 08 23:48:14 2020 +0200
     1.3 @@ -10,3 +10,9 @@
     1.4  cff47dbf43735b1d1459432fb201b380bf1db228 Release_2.1.0-RC7
     1.5  c083ce4caa4fdaff63135680a0d3341d4b9d130f Release_2.1.0-RC8
     1.6  8257d874259624ea0ccfe71afd3cfaf57d85a780 Release_2.1.0-RC9
     1.7 +91dcb9ff220f53df7d661f81382e0ce7e66a88f0 PassphraseCallback
     1.8 +7b08b320074079801028ad6f574aac5dd18f0709 PassphraseCallback_Complete1
     1.9 +91dcb9ff220f53df7d661f81382e0ce7e66a88f0 PassphraseCallback
    1.10 +0000000000000000000000000000000000000000 PassphraseCallback
    1.11 +7b08b320074079801028ad6f574aac5dd18f0709 PassphraseCallback_Complete1
    1.12 +0000000000000000000000000000000000000000 PassphraseCallback_Complete1
     2.1 --- a/src/foundation/pEp/jniadapter/AbstractEngine.java	Wed Jul 08 02:29:45 2020 +0200
     2.2 +++ b/src/foundation/pEp/jniadapter/AbstractEngine.java	Wed Jul 08 23:48:14 2020 +0200
     2.3 @@ -16,6 +16,7 @@
     2.4      private Sync.MessageToSendCallback messageToSendCallback;
     2.5      private Sync.NotifyHandshakeCallback notifyHandshakeCallback;
     2.6      private Sync.NeedsFastPollCallback needsFastPollCallback;
     2.7 +    private Sync.PassphraseRequiredCallback passphraseRequiredCallback;
     2.8  
     2.9      private final static DefaultCallback defaultCallback = new DefaultCallback();
    2.10  
    2.11 @@ -164,6 +165,12 @@
    2.12          this.needsFastPollCallback = needsFastPollCallback;
    2.13      }
    2.14  
    2.15 +    public void setPassphraseRequiredCallback(Sync.PassphraseRequiredCallback passphraseRequiredCallback) {
    2.16 +        System.out.println("passphraseRequiredCallback has been registered to:" + passphraseRequiredCallback.toString() + " on engine ObjID: " + getId());
    2.17 +
    2.18 +        this.passphraseRequiredCallback = passphraseRequiredCallback;
    2.19 +    }
    2.20 +
    2.21      public int needsFastPollCallFromC(boolean fast_poll_needed) {
    2.22          if (needsFastPollCallback != null) {
    2.23              needsFastPollCallback.needsFastPollCallFromC(fast_poll_needed);
    2.24 @@ -186,6 +193,22 @@
    2.25          return 0;
    2.26      }
    2.27  
    2.28 +    public byte[] passphraseRequiredFromC() {
    2.29 +        String ret = "";
    2.30 +        if (passphraseRequiredCallback != null) {
    2.31 +            System.out.println("calling passphraseRequiredCallback on engine ObjID:" + getId());
    2.32 +            ret = passphraseRequiredCallback.passphraseRequired();
    2.33 +        } else {
    2.34 +            System.out.println("no callback registered on engine ObjID:" + getId());
    2.35 +            // if this happens (no callback registered
    2.36 +            // we simply return ""
    2.37 +            // it will fail
    2.38 +            // this repeats MaxRetries times (currentluy hardcoded to 3)
    2.39 +            // Then the orig call will return with the PEP_STATUS (most likely PEP_PASSPHRASE_REQUIRED)
    2.40 +        }
    2.41 +        return toUTF8(ret);
    2.42 +    }
    2.43 +
    2.44      public int messageToSendCallFromC (Message message) {
    2.45          System.out.println("pEpSync" + "messageToSendCallFromC: " + messageToSendCallback );
    2.46          if (messageToSendCallback != null) {
     3.1 --- a/src/foundation/pEp/jniadapter/Sync.java	Wed Jul 08 02:29:45 2020 +0200
     3.2 +++ b/src/foundation/pEp/jniadapter/Sync.java	Wed Jul 08 23:48:14 2020 +0200
     3.3 @@ -19,6 +19,9 @@
     3.4          void notifyHandshake(Identity myself, Identity partner, SyncHandshakeSignal signal);
     3.5      }
     3.6  
     3.7 +    interface PassphraseRequiredCallback {
     3.8 +        String passphraseRequired();
     3.9 +    }
    3.10  
    3.11      public class DefaultCallback 
    3.12              implements Sync.MessageToSendCallback, Sync.NotifyHandshakeCallback,  Sync.NeedsFastPollCallback {
     4.1 --- a/src/foundation_pEp_jniadapter_AbstractEngine.cc	Wed Jul 08 02:29:45 2020 +0200
     4.2 +++ b/src/foundation_pEp_jniadapter_AbstractEngine.cc	Wed Jul 08 23:48:14 2020 +0200
     4.3 @@ -9,6 +9,7 @@
     4.4  #include <pEp/callback_dispatcher.hh>
     4.5  #include "throw_pEp_exception.hh"
     4.6  #include "jniutils.hh"
     4.7 +#include "passphrase_callback.hh"
     4.8  
     4.9  namespace pEp {
    4.10  using namespace pEp::JNIAdapter;
    4.11 @@ -25,6 +26,7 @@
    4.12  jmethodID messageToSendMethodID = nullptr;
    4.13  jmethodID notifyHandShakeMethodID = nullptr;
    4.14  jmethodID needsFastPollMethodID = nullptr;
    4.15 +jmethodID passphraseRequiredMethodID = nullptr;
    4.16  jmethodID method_values = nullptr;
    4.17  
    4.18  jobject objj = nullptr;
    4.19 @@ -85,12 +87,33 @@
    4.20          engineClass,
    4.21          "notifyHandshakeCallFromC",
    4.22          "(Lfoundation/pEp/jniadapter/_Identity;Lfoundation/pEp/jniadapter/_Identity;Lfoundation/pEp/jniadapter/SyncHandshakeSignal;)I");
    4.23 +    passphraseRequiredMethodID = _env->GetMethodID(
    4.24 +        engineClass,
    4.25 +        "passphraseRequiredFromC",
    4.26 +        "()[B");
    4.27  
    4.28      method_values = JNISync::env()->GetStaticMethodID(signalClass, "values",
    4.29                  "()[Lfoundation/pEp/jniadapter/SyncHandshakeSignal;");
    4.30      field_value = JNISync::env()->GetFieldID(signalClass, "value", "I");
    4.31  }
    4.32  
    4.33 +char* JNIAdapter::passphraseRequiredCallback() {
    4.34 +    pEpLog("called");
    4.35 +
    4.36 +    assert(objj && passphraseRequiredMethodID);
    4.37 +
    4.38 +    jobject ppJO = JNISync::env()->CallObjectMethod(objj, passphraseRequiredMethodID);
    4.39 +    if (JNISync::env()->ExceptionCheck()) {
    4.40 +        JNISync::env()->ExceptionDescribe();
    4.41 +        JNISync::env()->ExceptionClear();
    4.42 +    }
    4.43 +
    4.44 +    jbyteArray ppJBA = reinterpret_cast<jbyteArray>(ppJO);
    4.45 +    char* passphrase_ = to_string( JNISync::env(), ppJBA);
    4.46 +
    4.47 +    return passphrase_;
    4.48 +}
    4.49 +
    4.50  PEP_STATUS messageToSend(message *msg)
    4.51  {
    4.52      std::lock_guard<std::mutex> l(mutex_obj);
     5.1 --- a/src/gen_cpp_Engine.ysl2	Wed Jul 08 02:29:45 2020 +0200
     5.2 +++ b/src/gen_cpp_Engine.ysl2	Wed Jul 08 23:48:14 2020 +0200
     5.3 @@ -14,10 +14,12 @@
     5.4          #include <pEp/key_reset.h>
     5.5          #include <pEp/Adapter.hh>
     5.6          #include <pEp/pEpLog.hh>
     5.7 +        #include <pEp/passphrase_cache.hh>
     5.8 +
     5.9          #include "foundation_pEp_jniadapter_«@name».h"
    5.10          #include "throw_pEp_exception.hh"
    5.11          #include "jniutils.hh"
    5.12 -        #include <pEp/passphrase_cache.hh>
    5.13 +        #include "passphrase_callback.hh"
    5.14  
    5.15          using pEp::Adapter::session;
    5.16          using pEp::passphrase_cache;
    5.17 @@ -68,8 +70,34 @@
    5.18          choose {
    5.19              when "@cached = 'true'" {
    5.20              ||
    5.21 -                pEpLog("cached passphrase");
    5.22 -                PEP_STATUS status = passphrase_cache.api(::«@name»,session()`apply "parm", mode=call`);
    5.23 +                pEpLog("cached passphrase mode");
    5.24 +                bool retryAgain = false;
    5.25 +                int maxRetries = 3;
    5.26 +                int retryCount = 0;
    5.27 +                PEP_STATUS status;
    5.28 +                do {
    5.29 +                    // the actual target function
    5.30 +                    pEpLog("calling passphrase_cache.api(::«@name»())");
    5.31 +                    status = passphrase_cache.api(::«@name»,session()`apply "parm", mode=call`);
    5.32 +                    pEpLog("PEP_STATUS:" << status);
    5.33 +                    if(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE ) {
    5.34 +                        pEpLog("none of the cached passphrases worked");
    5.35 +                        if(retryCount < maxRetries) {
    5.36 +                            // call the app
    5.37 +                            char* _passphrase = passphraseRequiredCallback();
    5.38 +                            pEpLog("callback returned, config_passphrase() with new passphrase");
    5.39 +                            PEP_STATUS status = ::config_passphrase(session(),passphrase_cache.add(_passphrase));
    5.40 +                            retryAgain = true;
    5.41 +                            retryCount++;                
    5.42 +                        } else {
    5.43 +                            pEpLog("max retries reached:" << maxRetries);
    5.44 +                            retryAgain = false;
    5.45 +                        }
    5.46 +                    } else {
    5.47 +                        retryAgain = false;
    5.48 +                    }
    5.49 +                } while (retryAgain);
    5.50 +
    5.51              ||
    5.52              } otherwise {
    5.53              ||
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/passphrase_callback.hh	Wed Jul 08 23:48:14 2020 +0200
     6.3 @@ -0,0 +1,9 @@
     6.4 +
     6.5 +
     6.6 +namespace pEp {
     6.7 +    namespace JNIAdapter {
     6.8 +
     6.9 +    char* passphraseRequiredCallback();
    6.10 +
    6.11 +    };
    6.12 +};
    6.13 \ No newline at end of file
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/test/java/foundation/pEp/jniadapter/test/jni114/Makefile	Wed Jul 08 23:48:14 2020 +0200
     7.3 @@ -0,0 +1,37 @@
     7.4 +include ../../../../../../../Makefile.conf
     7.5 +include ../Makefile.conf
     7.6 +
     7.7 +TEST_UNIT_NAME=jni114
     7.8 +
     7.9 +JAVA_CLASSES = \
    7.10 + 	TestAlice.class \
    7.11 + 	../utils/AdapterBaseTestContext.class \
    7.12 + 	../utils/AdapterTestUtils.class \
    7.13 + 	../utils/TestCallbacks.class
    7.14 +
    7.15 +.PHONY: pitytest compile alice test clean
    7.16 +
    7.17 +all: alice compile
    7.18 +
    7.19 +pitytest:
    7.20 +	$(MAKE) -C $(PITYTEST_DIR)
    7.21 +
    7.22 +alice: compile clean-pep-home-alice
    7.23 +	cd $(JAVA_CWD);pwd;HOME=$(JAVA_PEP_HOME_DIR_ALICE) $(JAVA) $(JAVA_PKG_BASENAME).$(TEST_UNIT_NAME).TestAlice
    7.24 +
    7.25 +compile: $(JAVA_CLASSES) pitytest
    7.26 +
    7.27 +%.class: %.java
    7.28 +	cd $(JAVA_CWD);javac -cp $(CLASSPATH) $(JAVA_PKG_BASEPATH)/$(TEST_UNIT_NAME)/$<
    7.29 +
    7.30 +clean:
    7.31 +	rm -f $(JAVA_CLASSES)
    7.32 +	rm -f *.class
    7.33 +	rm -f *.log
    7.34 +	rm -Rf .gnupg
    7.35 +	rm -Rf .lldb
    7.36 +
    7.37 +clean-pep-home: clean-pep-home-alice
    7.38 +
    7.39 +clean-pep-home-alice:
    7.40 +	rm -rf $(PEP_HOME_DIR_ALICE)/.pEp
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java	Wed Jul 08 23:48:14 2020 +0200
     8.3 @@ -0,0 +1,76 @@
     8.4 +package foundation.pEp.jniadapter.test.jni114;
     8.5 +
     8.6 +import static foundation.pEp.pitytest.TestLogger.*;
     8.7 +import static foundation.pEp.pitytest.utils.TestUtils.readKey;
     8.8 +import static foundation.pEp.pitytest.utils.TestUtils.sleep;
     8.9 +
    8.10 +import foundation.pEp.jniadapter.*;
    8.11 +import foundation.pEp.pitytest.*;
    8.12 +import foundation.pEp.pitytest.utils.TestUtils;
    8.13 +import foundation.pEp.jniadapter.test.utils.*;
    8.14 +
    8.15 +import java.util.Vector;
    8.16 +
    8.17 +
    8.18 +// https://pep.foundation/jira/browse/JNI-111
    8.19 +
    8.20 +
    8.21 +class TestAlice {
    8.22 +    public static void main(String[] args) throws Exception {
    8.23 +//        readKey();
    8.24 +        TestSuite.getDefault().setVerbose(true);
    8.25 +        TestSuite.getDefault().setTestColor(TestUtils.TermColor.GREEN);
    8.26 +
    8.27 +        AdapterBaseTestContext jni114Ctx = new AdapterBaseTestContext();
    8.28 +        new TestUnit<AdapterBaseTestContext>("ImportKey/SetOwnKey", jni114Ctx, ctx -> {
    8.29 +            // ImportKey and setOwnKey (with passphrase, of course)
    8.30 +            ctx.alice = ctx.engine.importKey(ctx.keyAliceSecPassphrase).get(0);
    8.31 +            log(AdapterTestUtils.identityToString(ctx.alice, true));
    8.32 +            ctx.alice.user_id = "23";
    8.33 +            ctx.alice = ctx.engine.setOwnKey(ctx.alice, ctx.alice.fpr);
    8.34 +            assert ctx.alice != null : "Keyimport failed";
    8.35 +            assert ctx.alice.me == true;
    8.36 +            assert ctx.alice.comm_type == CommType.PEP_ct_pEp;
    8.37 +        });
    8.38 +
    8.39 +
    8.40 +        new TestUnit<AdapterBaseTestContext>("no callback / encrypt fails nonblocking", jni114Ctx, ctx -> {
    8.41 +            ctx.alice = ctx.engine.myself(ctx.alice);
    8.42 +            try {
    8.43 +                Message enc = ctx.engine.encrypt_message(ctx.msgToSelf, new Vector<>(), Message.EncFormat.PEP);
    8.44 +            } catch (pEpException e) {
    8.45 +                assert e instanceof pEpPassphraseRequired : "wrong exception type";
    8.46 +                return;
    8.47 +            }
    8.48 +            assert false : "encrypt_message() should have failed";
    8.49 +        });
    8.50 +
    8.51 +
    8.52 +        new TestUnit<AdapterBaseTestContext>("use callback for encrypt", jni114Ctx, ctx -> {
    8.53 +            // Register callback passphraseRequired()
    8.54 +            ctx.engine.setPassphraseRequiredCallback(new Sync.PassphraseRequiredCallback() {
    8.55 +                @Override
    8.56 +                public String passphraseRequired() {
    8.57 +                    log("passphraseRequired() called");
    8.58 +                    log("Please Enter Passphrase...");
    8.59 +                    sleep(2000);
    8.60 +                    return "passphrase_alice";
    8.61 +                }
    8.62 +            });
    8.63 +
    8.64 +            // myself
    8.65 +            ctx.alice = ctx.engine.myself(ctx.alice);
    8.66 +            log(AdapterTestUtils.identityToString(ctx.alice, true));
    8.67 +
    8.68 +            // Encrypt
    8.69 +            assert ctx.msgToSelf.getEncFormat() == Message.EncFormat.None : "Orig msg not plain";
    8.70 +            Message enc = ctx.engine.encrypt_message(ctx.msgToSelf, new Vector<>(), Message.EncFormat.PEP);
    8.71 +            assert enc.getEncFormat() == Message.EncFormat.PGPMIME : "Message not encrypted";
    8.72 +            assert !enc.getLongmsg().contains(ctx.msgToSelf.getLongmsg()) : "Message not encrypted";
    8.73 +            log(AdapterTestUtils.msgToString(enc, false));
    8.74 +        });
    8.75 +
    8.76 +
    8.77 +        TestSuite.getDefault().run();
    8.78 +    }
    8.79 +}
    8.80 \ No newline at end of file