All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cassproject.ebac.repository.EcEncryptedValue Maven / Gradle / Ivy

package org.cassproject.ebac.repository;

import com.eduworks.ec.array.EcArray;
import com.eduworks.ec.array.EcAsyncHelper;
import com.eduworks.ec.array.EcObject;
import com.eduworks.ec.crypto.*;
import com.eduworks.schema.ebac.EbacEncryptedSecret;
import com.eduworks.schema.ebac.EbacEncryptedValue;
import forge.pkcs5;
import forge.util;
import org.cassproject.ebac.identity.EcIdentityManager;
import org.cassproject.schema.general.Ebac;
import org.cassproject.schema.general.EcRemoteLinkedData;
import org.json.ld.EcLinkedData;
import org.stjs.javascript.*;
import org.stjs.javascript.functions.Callback0;
import org.stjs.javascript.functions.Callback1;
import org.stjs.javascript.functions.Callback2;
import window.base64;

/**
 * Represents an encrypted piece of data. Provides helper functions for
 * encryption/decryption of JSON-LD objects, and provides some searchability of
 * the data within.
 *
 * @author [email protected]
 * @module com.eduworks.ec
 * @class EcEncryptedValue
 * @extends EbacEncryptedValue
 */
public class EcEncryptedValue extends EbacEncryptedValue {

    private static Map encryptOnSaveMap;

    public EcEncryptedValue() {

    }

    public static EcEncryptedValue revive(EcEncryptedValue partiallyRehydratedObject) {
        if (partiallyRehydratedObject == null)
            return null;
        EcEncryptedValue v = new EcEncryptedValue();
        v.copyFrom(partiallyRehydratedObject);
        return v;
    }

    /**
     * Converts a piece of (likely) encrypted remote linked data to an unencrypted object.
     * @param d Data to decrypt
     * @return Decrypted data
     * @memberOf EcEncryptedValue
     * @static
     * @method fromEncryptedValue
     */
    public static EcRemoteLinkedData fromEncryptedValue(EcRemoteLinkedData d){
        if (!d.isAny(new EcEncryptedValue().getTypes()))
            return d;
        EcEncryptedValue eev = new EcEncryptedValue();
        eev.copyFrom(d);
        EcEncryptedValue.encryptOnSave(d.id, true);
        EcEncryptedValue.encryptOnSave(d.shortId(), true);
        return eev.decryptIntoObject();
    }

    /**
     * Converts a piece of (likely) encrypted remote linked data to an unencrypted object.
     * @param {EcRemoteLinkedData} d Data to decrypt
     * @param {Callback1} success
     * @param {Callback1} failure
     * @param d Data to decrypt
     * @return Decrypted data
     * @memberOf EcEncryptedValue
     * @static
     * @method fromEncryptedValue
     */
    public static void fromEncryptedValueAsync(EcRemoteLinkedData d, Callback1 success, Callback1 failure){
        if (!d.isAny(new EcEncryptedValue().getTypes()))
            success.$invoke(d);
        else {
            EcEncryptedValue eev = new EcEncryptedValue();
            eev.copyFrom(d);
            EcEncryptedValue.encryptOnSave(d.id, true);
            EcEncryptedValue.encryptOnSave(d.shortId(), true);
            eev.decryptIntoObjectAsync(success,failure);
        }
    }

    /**
     * Gets the fully qualified type name, as JSON-LD allows the "namespace" of
     * the type to be defined in @context.
     *
     * @return {string} Fully qualified type name.
     * @method getEncryptedFullType
     */
    public String getEncryptedFullType() {
        if (encryptedContext == null)
            return this.encryptedType;
        if (this.encryptedType.indexOf("http") != -1)
            return this.encryptedType;

        String computedType = encryptedContext;
        if (EcObject.isObject(encryptedContext)) {
            Array typeParts = (Array) (Object) this.encryptedType.split(":");
            if (typeParts.$length() == 2) {
                computedType = (String) JSObjectAdapter.$get(encryptedContext, typeParts.$get(0));
                if (!computedType.endsWith("/"))
                    computedType += "/";
                computedType += typeParts.$get(1);
                return computedType;
            } else if (JSObjectAdapter.$get(encryptedContext, "@vocab") != null)
                computedType = (String) JSObjectAdapter.$get(encryptedContext, "@vocab");
        }
        if (!computedType.endsWith("/"))
            computedType += "/";
        computedType += this.encryptedType;
        return computedType;
    }

    public String getEncryptedDottedType() {
        return getEncryptedFullType().replace("http://", "").replace("https://", "").replaceAll("/", ".");
    }

    /**
     * Converts a piece of remote linked data to an encrypted value
     *
     * @param {EcRemoteLinkedData} d Data to encrypt
     * @param {Boolean}            hideType Flag to hide the type of the encrypted value
     *                             when encrypting
     * @return {EcEncryptedValue} Encrypted value
     * @memberOf EcEncryptedValue
     * @method toEncryptedValue
     * @static
     */
    public static EcEncryptedValue toEncryptedValue(EcRemoteLinkedData d, Boolean hideType) {
        d.updateTimestamp();

        EcEncryptedValue v = new EcEncryptedValue();

        if (hideType == null || !hideType) {
            v.encryptedType = d.type;
            v.encryptedContext = d.context;
        }
        String newIv = EcAes.newIv(16);
        String newSecret = EcAes.newIv(16);
        Object conceptName = JSObjectAdapter.$get(d, "skos:prefLabel");
        Object conceptSchemeName = JSObjectAdapter.$get(d, "dcterms:title");
        v.payload = EcAesCtr.encrypt(d.toJson(), newSecret, newIv);
        v.owner = d.owner;
        v.reader = d.reader;
        v.id = d.id;
        if (JSObjectAdapter.$get(d, "name") != null) {
            v.name = (String) JSObjectAdapter.$get(d, "name");
        }

        if (d.owner != null) {
            for (int i = 0; i < d.owner.$length(); i++) {
                EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
                eSecret.iv = newIv;
                eSecret.secret = newSecret;
                if (v.secret == null) {
                    v.secret = new Array();
                }
                v.secret.push(EcRsaOaep.encrypt(EcPk.fromPem(d.owner.$get(i)), eSecret.toEncryptableJson()));
            }
        }

        if (d.reader != null) {
            for (int i = 0; i < d.reader.$length(); i++) {
                EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
                eSecret.iv = newIv;
                eSecret.secret = newSecret;
                if (v.secret == null) {
                    v.secret = new Array();
                }
                v.secret.push(EcRsaOaep.encrypt(EcPk.fromPem(d.reader.$get(i)), eSecret.toEncryptableJson()));
            }
        }
        if (conceptName != null) {
            JSObjectAdapter.$put(v, "skos:prefLabel", conceptName);
        }
        if (conceptSchemeName != null) {
            JSObjectAdapter.$put(v, "dcterms:title", conceptSchemeName);
        }
        return v;
    }

    /**
     * Converts a piece of remote linked data to an encrypted value,
     * asynchronously
     *
     * @param {EcRemoteLinkedData}          d Data to encrypt
     * @param {boolean}                     hideType Flag to hide the type of the encrypted value
     *                                      when encrypting
     * @param {Callback1} success Callback triggered with
     *                                      successfully encrypted, returns the encrypted value
     * @param {Callback1}           failure Callback triggered on error during
     *                                      encryption
     * @memberOf EcEncryptedValue
     * @method toEncryptedValueAsync
     * @static
     */
    public static void toEncryptedValueAsync(final EcRemoteLinkedData d, Boolean hideType, final Callback1 success,
                                             final Callback1 failure) {
        d.updateTimestamp();

        final EcEncryptedValue v = new EcEncryptedValue();

        if (hideType == null || !hideType) {
            v.encryptedType = d.type;
        }
        final String newIv = EcAes.newIv(16);
        final String newSecret = EcAes.newIv(16);
        EcAesCtrAsync.encrypt(d.toJson(), newSecret, newIv, new Callback1() {
            @Override
            public void $invoke(String encryptedText) {
                v.payload = encryptedText;
                v.owner = d.owner;
                v.reader = d.reader;
                v.id = d.id;
                if (JSObjectAdapter.$get(d, "name") != null) {
                    v.name = (String) JSObjectAdapter.$get(d, "name");
                }

                if (d.owner != null) {
                    new EcAsyncHelper().each(d.owner, new Callback2() {
                        @Override
                        public void $invoke(String pk, final Callback0 arg1) {
                            insertSecret(pk, arg1, newIv, newSecret, v, failure);
                        }
                    }, new Callback1>() {
                        @Override
                        public void $invoke(Array arg0) {
                            if (d.reader != null) {
                                new EcAsyncHelper().each(d.reader, new Callback2() {
                                    @Override
                                    public void $invoke(String pk, final Callback0 arg1) {
                                        insertSecret(pk, arg1, newIv, newSecret, v, failure);
                                    }
                                }, new Callback1>() {
                                    @Override
                                    public void $invoke(Array arg0) {
                                        success.$invoke(v);
                                    }
                                });
                            } else
                                success.$invoke(v);
                        }
                    });
                }
            }
        }, failure);
    }

    private static void insertSecret(String pk, final Callback0 success, String newIv, String newSecret, final EcEncryptedValue v, Callback1 failure) {
        EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
        eSecret.iv = newIv;
        eSecret.secret = newSecret;
        if (v.secret == null) {
            v.secret = new Array();
        }
        EcRsaOaepAsync.encrypt(EcPk.fromPem(pk), eSecret.toEncryptableJson(), new Callback1() {
            @Override
            public void $invoke(String encryptedSecret) {
                v.secret.push(encryptedSecret);
                success.$invoke();
            }
        }, failure);
    }

    /**
     * Encrypts a text value with the key provided
     *
     * @param {String} text Text to encrypt
     * @param {String} id ID of the encrypted value
     * @param {EcPk}   owner Key to Encrypt
     * @return {EcEncryptedValue} Encrypted value
     * @memberOf EcEncryptedValue
     * @method encryptValueOld
     * @static
     * @deprecated
     */
    @Deprecated
    public static EcEncryptedValue encryptValueOld(String text, String id, EcPk owner) {
        EcEncryptedValue v = new EcEncryptedValue();

        String newIv = EcAes.newIv(16);
        String newSecret = EcAes.newIv(16);
        v.payload = EcAesCtr.encrypt(text, newSecret, newIv);
        v.addOwner(owner);

        for (int i = 0; i < v.owner.$length(); i++) {
            EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
            eSecret.id = util.encode64(pkcs5.pbkdf2(id, "", 1, 8));
            eSecret.iv = newIv;
            eSecret.secret = newSecret;
            if (v.secret == null) {
                v.secret = new Array();
            }
            v.secret.push(
                    EcRsaOaep.encrypt(
                            EcPk.fromPem(v.owner.$get(i)),
                            eSecret.toEncryptableJson()
                    )
            );
        }
        return v;
    }

    /**
     * Encrypts a text value with the owners and readers provided
     *
     * @param {String}   text Text to encrypt
     * @param {String}   id ID of the value to encrypt
     * @param {String[]} owners Owner keys to encrypt value with
     * @param {String[]} readers Reader keys to encrypt value with
     * @return {EcEncryptedValue} Encrypted value
     * @memberOf EcEncryptedValue
     * @method encryptValue
     * @static
     */
    public static EcEncryptedValue encryptValue(String text, String id, Array owners, Array readers) {
        EcEncryptedValue v = new EcEncryptedValue();

        String newIv = EcAes.newIv(16);
        String newSecret = EcAes.newIv(16);
        v.payload = EcAesCtr.encrypt(text, newSecret, newIv);
        if (owners != null) {
            for (int i = 0; i < owners.$length(); i++) {
                v.addOwner(EcPk.fromPem(owners.$get(i)));
            }
        }

        if (owners != null)
            if (v.owner != null) {
                for (int i = 0; i < v.owner.$length(); i++) {
                    EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
                    eSecret.id = util.encode64(pkcs5.pbkdf2(id, "", 1, 8));
                    eSecret.iv = newIv;
                    eSecret.secret = newSecret;
                    if (v.secret == null) {
                        v.secret = new Array();
                    }
                    v.secret.push(EcRsaOaep.encrypt(EcPk.fromPem(v.owner.$get(i)), eSecret.toEncryptableJson()));
                }
            }
        if (readers != null)
            if (v.reader != null) {
                for (int i = 0; i < v.reader.$length(); i++) {
                    EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
                    eSecret.id = util.encode64(pkcs5.pbkdf2(id, "", 1, 8));
                    eSecret.iv = newIv;
                    eSecret.secret = newSecret;
                    if (v.secret == null) {
                        v.secret = new Array();
                    }
                    v.secret.push(EcRsaOaep.encrypt(EcPk.fromPem(v.reader.$get(i)), eSecret.toEncryptableJson()));
                }
            }

        if (readers != null) {
            for (int i = 0; i < readers.$length(); i++) {
                v.addReader(EcPk.fromPem(readers.$get(i)));
            }
        }
        return v;
    }

    /**
     * Encrypts a text value with the owners and readers provided
     *
     * @param {String}   text Text to encrypt
     * @param {String}   id ID of the value to encrypt
     * @param {String[]} owners Owner keys to encrypt value with
     * @param {String[]} readers Reader keys to encrypt value with
     * @return {EcEncryptedValue} Encrypted value
     * @memberOf EcEncryptedValue
     * @method encryptValue
     * @static
     */
    public static void encryptValueAsync(String text, final String id, Array owners, Array readers, final Callback1 success, Callback1 failure) {
        final EcEncryptedValue v = new EcEncryptedValue();

        final String newIv = EcAes.newIv(16);
        final String newSecret = EcAes.newIv(16);
        v.payload = EcAesCtr.encrypt(text, newSecret, newIv);
        if (owners != null) {
            for (int i = 0; i < owners.$length(); i++) {
                v.addOwner(EcPk.fromPem(owners.$get(i)));
            }
        }
        if (readers != null) {
            for (int i = 0; i < readers.$length(); i++) {
                v.addReaderBasic(EcPk.fromPem(readers.$get(i)));
            }
        }

        Array pks = new Array<>();

        if (owners != null)
            if (v.owner != null)
                pks = pks.concat(v.owner);
        if (readers != null)
            if (v.reader != null)
                pks = pks.concat(v.reader);
        new EcAsyncHelper().each(pks, new Callback2() {
            @Override
            public void $invoke(String pk, final Callback0 callback0) {
                EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
                eSecret.id = util.encode64(pkcs5.pbkdf2(id, "", 1, 8));
                eSecret.iv = newIv;
                eSecret.secret = newSecret;
                if (v.secret == null) {
                    v.secret = new Array();
                }
                EcRsaOaepAsync.encrypt(EcPk.fromPem(pk), eSecret.toEncryptableJson(), new Callback1() {
                    @Override
                    public void $invoke(String s) {
                        v.secret.push(s);
                        callback0.$invoke();
                    }
                },(Callback1)callback0);
            }
        }, new Callback1>() {
            @Override
            public void $invoke(Array pks) {
                success.$invoke(v);
            }
        });
    }

    /**
     * Encrypt a value with a specific IV and secret
     *
     * @param {String}   iv Initialization Vector for encryption
     * @param {String}   secret Encryption secret
     * @param {String}   text Text to encrypt
     * @param {String}   id ID of value to encrypt
     * @param {String[]} owners Owners keys to encrypt with
     * @param {String[]} readers Reader Keys to encrypt with
     * @return {EcEncryptedValue}
     * @memberOf EcEncryptedValue
     * @method encryptValueUsingIvAndSecret
     * @static
     */
    public static EcEncryptedValue encryptValueUsingIvAndSecret(String iv, String secret, String text, String id, Array owners, Array readers) {
        EcEncryptedValue v = new EcEncryptedValue();

        v.payload = EcAesCtr.encrypt(text, secret, iv);
        if (owners != null) {
            for (int i = 0; i < owners.$length(); i++) {
                v.addOwner(EcPk.fromPem(owners.$get(i)));
            }
        }

        if (owners != null) {
            for (int i = 0; i < v.owner.$length(); i++) {
                EbacEncryptedSecret eSecret = new EbacEncryptedSecret();
                eSecret.id = util.encode64(pkcs5.pbkdf2(id, "", 1, 8));
                eSecret.iv = iv;
                eSecret.secret = secret;
                if (v.secret == null) {
                    v.secret = new Array();
                }
                v.secret.push(EcRsaOaep.encrypt(EcPk.fromPem(v.owner.$get(i)), eSecret.toEncryptableJson()));
            }
        }

        if (readers != null) {
            for (int i = 0; i < readers.$length(); i++) {
                v.addReader(EcPk.fromPem(readers.$get(i)));
            }
        }
        return v;
    }

    /**
     * Setter and getter function for encryptOnSave of an identifier,
     * encryptOnSave is used by the static save functions of a class to
     * determine whether or not to encrypt something when it is saved. This
     * value is usually set when an object is decrypted using one of the decrypt
     * functions above.
     *
     * @param {String}  id ID of the data to get/set encryptOnSave for
     * @param {boolean} [val] If passed in, sets the value, if null this
     *                  function gets the encryptOnSave value
     * @return {boolean} if val is null/ignored returns value in the map, if val
     * is passed in returns val
     * @memberOf EcEncryptedValue
     * @method encryptOnSave
     * @static
     */
    public static boolean encryptOnSave(String id, Boolean val) {
        if (encryptOnSaveMap == null) {
            encryptOnSaveMap = JSCollections.$map();
        }

        if (val == null) {
            if (encryptOnSaveMap.$get(id) != null) {
                return encryptOnSaveMap.$get(id);
            } else {
                return false;
            }
        } else {
            encryptOnSaveMap.$put(id, val);

            return val;
        }
    }

    /**
     * Decrypts this encrypted value into an object
     *
     * @return The Decrypted Object
     * @memberOf EcEncryptedValue
     * @method decryptIntoObject
     */
    public EcRemoteLinkedData decryptIntoObject() {
        String decryptRaw = decryptIntoString();
        if (decryptRaw == null) {
            return null;
        }
        if (!EcLinkedData.isProbablyJson(decryptRaw)) {
            return null;
        }
        EcRemoteLinkedData decrypted = new EcRemoteLinkedData("", "");
        decrypted.copyFrom((EcRemoteLinkedData) JSGlobal.JSON.parse(decryptRaw));
        encryptOnSave(decrypted.id, true);
        decrypted.id = this.id;
        return (EcRemoteLinkedData) decrypted.deAtify();
    }

    /**
     * Asynchronously decrypts this encrypted value into an object
     *
     * @param {Callback1} success Callback triggered on
     *                                       successful encryption, returns the decrypted object
     * @param {Callback1}            failure Callback triggered if error during
     *                                       encryption
     * @memberOf EcEncryptedValue
     * @method decryptIntoObjectAsync
     */
    public void decryptIntoObjectAsync(final Callback1 success, final Callback1 failure) {
        final String id = this.id;
        decryptIntoStringAsync(new Callback1() {
            @Override
            public void $invoke(String decryptRaw) {
                if (decryptRaw == null) {
                    failure.$invoke("Could not decrypt data.");
                }
                if (!EcLinkedData.isProbablyJson(decryptRaw)) {
                    failure.$invoke("Could not decrypt data.");
                }
                EcRemoteLinkedData decrypted = new EcRemoteLinkedData("", "");
                decrypted.copyFrom((EcRemoteLinkedData) JSGlobal.JSON.parse(decryptRaw));
                encryptOnSave(decrypted.id, true);
                decrypted.id = id;
                success.$invoke((EcRemoteLinkedData) decrypted.deAtify());
            }
        }, failure);
    }

    /**
     * Asynchronously decrypts this encrypted value into an object with a IV and
     * secret provided
     *
     * @param {String}                        iv Initialization Vector for decryption
     * @param {String}                        secret Secret for decryption
     * @param {Callback1} success Callback triggered after
     *                                        successful decryption
     * @param {Callback1}             failure Callback triggered if error during
     *                                        decryption
     * @memberOf EcEncryptedValue
     * @method decryptIntoObjectUsingIvAndSecretAsync
     */
    public void decryptIntoObjectUsingIvAndSecretAsync(String iv, String secret, final Callback1 success, final Callback1 failure) {
        decryptIntoStringUsingIvAndSecretAsync(iv, secret, new Callback1() {
            @Override
            public void $invoke(String decryptRaw) {
                if (decryptRaw == null) {
                    failure.$invoke("Could not decrypt data.");
                }
                if (!EcLinkedData.isProbablyJson(decryptRaw)) {
                    failure.$invoke("Could not decrypt data.");
                }
                EcRemoteLinkedData decrypted = new EcRemoteLinkedData("", "");
                decrypted.copyFrom((EcRemoteLinkedData) JSGlobal.JSON.parse(decryptRaw));
                encryptOnSave(decrypted.id, true);
                success.$invoke((EcRemoteLinkedData) decrypted.deAtify());
            }
        }, failure);
    }

    /**
     * Decrypts an encrypted value into a string
     *
     * @return {String} Decrypted string value
     * @memberOf EcEncryptedValue
     * @method decryptIntoString
     */
    public String decryptIntoString() {
        EbacEncryptedSecret decryptSecret = decryptSecret();
        if (decryptSecret != null) {
            return EcAesCtr.decrypt(payload, decryptSecret.secret, decryptSecret.iv);
        }
        return null;
    }

    /**
     * Decrypts an encrypted value into a string using an alternative secret.
     *
     * @return {String} Decrypted string value
     * @memberOf EcEncryptedValue
     * @method decryptIntoString
     */
    public String decryptIntoStringUsingSecret(EbacEncryptedSecret decryptSecret) {
        if (decryptSecret != null) {
            return EcAesCtr.decrypt(payload, decryptSecret.secret, decryptSecret.iv);
        }
        return null;
    }

    /**
     * Asynchronously decrypts an encrypted value into a string
     *
     * @param {Callback1} success Callback triggered after successfully
     *                            decrypted, returns decrypted string
     * @param {Callback1} failure Callback triggered if error during
     *                            decryption
     * @memberOf EcEncryptedValue
     * @method decryptIntoStringAsync
     */
    public void decryptIntoStringAsync(final Callback1 success, final Callback1 failure) {
        final EcEncryptedValue me = this;
        decryptSecretAsync(new Callback1() {
            @Override
            public void $invoke(EbacEncryptedSecret decryptSecret) {
                if (decryptSecret != null) {
                    if (me.context == Ebac.context_0_2 || me.context == Ebac.context_0_3) {
                        if (base64.decode(decryptSecret.iv).byteLength == 32)
                            decryptSecret.iv = base64.encode(base64.decode(decryptSecret.iv).slice(0, 16));
                    }
                    EcAesCtrAsync.decrypt(me.payload, decryptSecret.secret, decryptSecret.iv, success, failure);
                }
            }
        }, failure);
    }

    /**
     * Asynchronously decrypts an encrypted value into a string
     *
     * @param {Callback1} success Callback triggered after successfully
     *                            decrypted, returns decrypted string
     * @param {Callback1} failure Callback triggered if error during
     *                            decryption
     * @memberOf EcEncryptedValue
     * @method decryptIntoStringAsync
     */
    public void decryptIntoStringUsingSecretAsync(EbacEncryptedSecret decryptSecret, final Callback1 success, final Callback1 failure) {
        final EcEncryptedValue me = this;
        if (decryptSecret != null) {
            if (me.context == Ebac.context_0_2 || me.context == Ebac.context_0_3) {
                if (base64.decode(decryptSecret.iv).byteLength == 32)
                    decryptSecret.iv = base64.encode(base64.decode(decryptSecret.iv).slice(0, 16));
            }
            EcAesCtrAsync.decrypt(me.payload, decryptSecret.secret, decryptSecret.iv, success, failure);
        }
    }

    /**
     * Asynchronously decrypts an encrypted value into a string with an IV and
     * secrete provided
     *
     * @param {String}            iv Initialization Vector for decryption
     * @param {String}            secret Secret for decryption
     * @param {Callback1} success Callback triggered on successful
     *                            decryption
     * @param {Callback1} failure Callback triggered if error during
     *                            decryption
     * @memberOf EcEncryptedValue
     * @method decryptIntoStringUsingIvAndSecretAsync
     */
    public void decryptIntoStringUsingIvAndSecretAsync(String iv, String secret, final Callback1 success, final Callback1 failure) {
        if (context == Ebac.context_0_2 || context == Ebac.context_0_3) {
            if (base64.decode(iv).byteLength == 32)
                iv = base64.encode(base64.decode(iv).slice(0, 16));
        }
        EcAesCtrAsync.decrypt(payload, secret, iv, success, failure);
    }

    /**
     * Attempts to decrypt the secret by using all Identities in the Identity
     * Manager
     *
     * @return {EbacEncryptedSecret} Secret after decrypted
     * @memberOf EcEncryptedValue
     * @method decryptSecret
     */
    public EbacEncryptedSecret decryptSecret() {
        int candidateIndex = 0;
        // See if I am an owner.
        if (owner != null) {
            for (int i = 0; i < owner.$length(); i++) {
                EcPpk decryptionKey = EcIdentityManager.getPpk(EcPk.fromPem(owner.$get(i)));
                if (decryptionKey == null) {
                    candidateIndex++;
                    continue;
                }
                EbacEncryptedSecret decrypted = decryptSecretByKey(decryptionKey, candidateIndex);
                if (decrypted != null) {
                    return decrypted;
                }
            }
        }
        // See if I have read-only access.
        if (reader != null) {
            for (int i = 0; i < reader.$length(); i++) {
                EcPpk decryptionKey = EcIdentityManager.getPpk(EcPk.fromPem(reader.$get(i)));
                if (decryptionKey == null) {
                    candidateIndex++;
                    continue;
                }
                EbacEncryptedSecret decrypted = decryptSecretByKey(decryptionKey, candidateIndex);
                if (decrypted != null) {
                    return decrypted;
                }
            }
        }
        // Last resort, try all the keys I have on all the possible locks.
        for (int i = 0; i < EcIdentityManager.ids.$length(); i++) {
            EcPpk decryptionKey = EcIdentityManager.ids.$get(i).ppk;
            EbacEncryptedSecret decrypted = decryptSecretByKey(decryptionKey, -1);
            if (decrypted != null) {
                return decrypted;
            }
        }
        return null;
    }

    /**
     * Attempts to decrypt secret with a specific key
     *
     * @param {EcPpk} decryptionKey Key to attempt secret decryption
     * @return {EbacEncryptedSecret} Decrypted Secret
     * @memberOf EcEncryptedValue
     * @method decryptSecretByKey
     */
    private EbacEncryptedSecret decryptSecretByKey(EcPpk decryptionKey, int tryThisIndexFirst) {
        EbacEncryptedSecret encryptedSecret = null;
        if (this.secret != null) {
            if (tryThisIndexFirst >= 0)
                try {
                    encryptedSecret = tryDecryptSecretByKeyAndIndex(decryptionKey, tryThisIndexFirst);
                    if (encryptedSecret != null)
                        return encryptedSecret;
                } catch (Exception ex) {
                }
            for (int j = 0; j < this.secret.$length(); j++) {
                if (tryThisIndexFirst < 0 || j != tryThisIndexFirst)
                    try {
                        encryptedSecret = tryDecryptSecretByKeyAndIndex(decryptionKey, j);
                    } catch (Exception ex) {
                    }
                if (encryptedSecret != null)
                    return encryptedSecret;
            }
        }
        return null;
    }

    /**
     * Asynchronously attempts to decrypt secret using all identities in
     * Identity Manager
     *
     * @param {Callback1} success Callback triggered after
     *                                         successfully decrypting secret, returns the decrypted secret
     * @param {Callback1}              failure Callback triggered if error decrypting
     *                                         secret
     * @memberOf EcEncryptedValue
     * @method decryptSecretAsync
     */
    public void decryptSecretAsync(final Callback1 success, final Callback1 failure) {
        final Array ppks = new Array<>();
        final Array estimatedIndices = new Array<>();

        // See if I am an owner.
        if (owner != null) {
            for (int i = 0; i < owner.$length(); i++) {
                EcPpk decryptionKey = EcIdentityManager.getPpk(EcPk.fromPem(owner.$get(i)));
                if (decryptionKey != null) {
                    if (!decryptionKey.inArray(ppks)) {
                        ppks.push(decryptionKey);
                        estimatedIndices.push(i);
                    }
                }
            }
        }
        // See if I have read-only access.
        if (reader != null) {
            for (int i = 0; i < reader.$length(); i++) {
                EcPpk decryptionKey = EcIdentityManager.getPpk(EcPk.fromPem(reader.$get(i)));
                if (decryptionKey != null) {
                    if (!decryptionKey.inArray(ppks)) {
                        ppks.push(decryptionKey);
                        estimatedIndices.push(i + owner.$length());
                    }
                }
            }
        }
        final EcEncryptedValue me = this;
        final EcAsyncHelper helper = new EcAsyncHelper();
        helper.each(ppks, new Callback2() {
            @Override
            public void $invoke(EcPpk decryptionKey, final Callback0 countdown) {
                int estimatedIndex = -1;
                for (int i = 0; i < ppks.$length(); i++)
                    if (ppks.$get(i).equals(decryptionKey))
                        estimatedIndex = estimatedIndices.$get(i);
                me.decryptSecretByKeyAsync(decryptionKey, estimatedIndex, new Callback1() {
                    @Override
                    public void $invoke(EbacEncryptedSecret p1) {
                        if (helper.counter == -1) {
                            countdown.$invoke();
                            return;
                        }
                        helper.stop();
                        success.$invoke(p1);
                        countdown.$invoke();
                    }
                }, new Callback1() {
                    @Override
                    public void $invoke(String arg0) {
                        countdown.$invoke();
                    }
                });
            }
        }, new Callback1>() {
            @Override
            public void $invoke(Array arg0) {
                failure.$invoke("Could not decrypt secret.");
            }
        });
    }

    private EbacEncryptedSecret tryDecryptSecretByKeyAndIndex(EcPpk decryptionKey, int j) {
        String decryptedSecret = null;
        decryptedSecret = EcRsaOaep.decrypt(decryptionKey, this.secret.$get(j));
        if (EcLinkedData.isProbablyJson(decryptedSecret)) {
            EbacEncryptedSecret encryptedSecret = EbacEncryptedSecret.fromEncryptableJson(JSGlobal.JSON.parse(decryptedSecret));
            return encryptedSecret;
        }
        return null;
    }

    /**
     * Asynchronously attempts to decrypt secret with a specific key
     *
     * @param {EcPpk}                          decryptionKey Key to attempt secret decryption
     * @param {Callback1} success Callback triggered after
     *                                         successful decryption of secret, returns decrypted secret
     * @param {Callback1}              failure Callback triggered if error during
     *                                         secret decryption
     * @memberOf EcEncryptedValue
     * @method decryptSecretByKeyAsync
     */
    private void decryptSecretByKeyAsync(final EcPpk decryptionKey, final int estimatedIndex, final Callback1 success, final Callback1 failure) {
        EbacEncryptedSecret encryptedSecret = null;
        final EcEncryptedValue me = this;
        if (this.secret != null) {
            if (estimatedIndex < 0 || estimatedIndex >= secret.$length()) {
                decryptSecretsByKeyAsync(decryptionKey, success, failure);
            } else {
                EcRsaOaepAsync.decrypt(decryptionKey, secret.$get(estimatedIndex), new Callback1() {
                    @Override
                    public void $invoke(String decryptedSecret) {
                        if (!EcLinkedData.isProbablyJson(decryptedSecret)) {
                            me.decryptSecretsByKeyAsync(decryptionKey, success, failure);
                        } else {
                            success.$invoke(EbacEncryptedSecret.fromEncryptableJson(JSGlobal.JSON.parse(decryptedSecret)));
                        }
                    }
                }, new Callback1() {
                    @Override
                    public void $invoke(String arg0) {
                        me.decryptSecretsByKeyAsync(decryptionKey, success, failure);
                    }
                });
            }
        } else
            failure.$invoke("Secret field is empty.");
    }

    private void decryptSecretsByKeyAsync(final EcPpk decryptionKey, final Callback1 success, final Callback1 failure) {

        final EcAsyncHelper helper = new EcAsyncHelper();
        helper.each(secret, new Callback2() {
            @Override
            public void $invoke(String decryptionSecret, final Callback0 decrement) {
                EcRsaOaepAsync.decrypt(decryptionKey, decryptionSecret, new Callback1() {
                    @Override
                    public void $invoke(String decryptedSecret) {
                        if (helper.counter == -1) {
                            return;
                        }
                        if (!EcLinkedData.isProbablyJson(decryptedSecret)) {
                            decrement.$invoke();
                        } else {
                            helper.stop();
                            success.$invoke(EbacEncryptedSecret.fromEncryptableJson(JSGlobal.JSON.parse(decryptedSecret)));
                        }
                    }
                }, new Callback1() {
                    @Override
                    public void $invoke(String arg0) {
                        decrement.$invoke();
                    }
                });
            }
        }, new Callback1>() {

            @Override
            public void $invoke(Array arg0) {
                failure.$invoke("Could not find decryption key.");
            }
        });
    }

    /**
     * Checks if this encrypted value is an encrypted version of a specific
     * type, only works if the type wasn't hidden during encryption
     *
     * @param {String} type Type to compare if an encrypted type
     * @return {boolean} True if encrypted version of type, false if not or
     * can't tell
     * @memberOf EcEncryptedValue
     * @method isAnEncrypted
     */
    public boolean isAnEncrypted(String type) {
        if (this.encryptedType == null) {
            return false;
        }

        Array typeSplit = JSCollections.$castArray(type.split("/"));

        return this.encryptedType == type || this.encryptedType == typeSplit.$get(typeSplit.$length() - 1);
    }

    /**
     * Adds a reader to the object, if the reader does not exist.
     *
     * @param {EcPk} newReader PK of the new reader.
     * @memberOf EcEncryptedValue
     * @method addReader
     */
    public void addReader(EcPk newReader) {
        addReaderBasic(newReader);
        EbacEncryptedSecret payloadSecret = decryptSecret();

        if (payloadSecret == null) {
            Global.console.error("Cannot add a Reader if you don't know the secret");
            return;
        }

        EcArray.setAdd(secret, EcRsaOaep.encrypt(newReader, payloadSecret.toEncryptableJson()));
    }

    /**
     * Adds a reader to the object, if the reader does not exist.
     *
     * @param {EcPk} newReader PK of the new reader.
     * @memberOf EcEncryptedValue
     * @method addReader
     */
    public void addReaderBasic(EcPk newReader) {
        String pem = newReader.toPem();
        if (reader == null) {
            reader = new Array();
        }
        if (EcArray.has(reader, pem))
            return;
        if (owner != null)
            if (EcArray.has(owner, pem))
                return;
        EcArray.setAdd(reader, pem);
    }

    /**
     * Removes a reader from the object, if the reader does exist.
     *
     * @param {EcPk} oldReader PK of the old reader.
     * @memberOf EcEncryptedValue
     * @method removeReader
     */
    public void removeReader(EcPk oldReader) {
        EbacEncryptedSecret payloadSecret = decryptSecret();

        String pem = oldReader.toPem();
        if (reader != null) {
            EcArray.setRemove(reader, pem);
        }

        if (payloadSecret == null) {
            Global.console.error("Cannot remove a Reader if you don't know the secret");
            return;
        }

        secret = new Array<>();
        if (owner != null)
            for (int i = 0; i < owner.$length(); i++)
                EcArray.setAdd(secret, EcRsaOaep.encrypt(EcPk.fromPem(owner.$get(i)), payloadSecret.toEncryptableJson()));
        if (reader != null)
            for (int i = 0; i < reader.$length(); i++)
                EcArray.setAdd(secret, EcRsaOaep.encrypt(EcPk.fromPem(reader.$get(i)), payloadSecret.toEncryptableJson()));
    }

    /**
     * Adds a reader to the object, if the reader does not exist.
     *
     * @param {EcPk}              newReader PK of the new reader.
     * @param {Callback0}         success   Callback triggered after successful encryption
     * @param {Callback1} failure Callback triggered if error during secret decryption
     * @memberOf EcEncryptedValue
     * @method addReaderAsync
     */
    public void addReaderAsync(final EcPk newReader, final Callback0 success, final Callback1 failure) {
        final EcEncryptedValue me = this;
        decryptSecretAsync(new Callback1() {
            @Override
            public void $invoke(EbacEncryptedSecret payloadSecret) {
                EcRsaOaepAsync.encrypt(newReader, payloadSecret.toEncryptableJson(), new Callback1() {
                    @Override
                    public void $invoke(String s) {
                        String pem = newReader.toPem();
                        if (me.reader == null) {
                            me.reader = new Array();
                        }
                        for (int i = 0; i < me.reader.$length(); i++) {
                            if (me.reader.$get(i) == pem) {
                                success.$invoke();
                                return;
                            }
                        }
                        EcArray.setAdd(me.reader, pem);

                        EcArray.setAdd(me.secret, s);
                        success.$invoke();
                    }
                }, failure);
            }
        }, failure);
    }

    /**
     * Removes a reader from the object, if the reader does exist.
     *
     * @param {EcPk}              oldReader PK of the old reader.
     * @param {Callback0}         success   Callback triggered after successful encryption
     * @param {Callback1} failure Callback triggered if error during secret decryption
     * @memberOf EcEncryptedValue
     * @method removeReaderAsync
     */
    public void removeReaderAsync(final EcPk oldReader, final Callback0 success, final Callback1 failure) {
        final EcEncryptedValue me = this;
        decryptSecretAsync(new Callback1() {
            @Override
            public void $invoke(final EbacEncryptedSecret payloadSecret) {
                String pem = oldReader.toPem();
                if (me.reader != null) {
                    EcArray.setRemove(me.reader, pem);
                }
                Array ary = new Array<>();
                if (me.owner != null)
                    for (int i = 0; i < me.owner.$length(); i++)
                        EcArray.setAdd(ary, EcPk.fromPem(me.owner.$get(i)));
                if (me.reader != null)
                    for (int i = 0; i < me.reader.$length(); i++)
                        EcArray.setAdd(ary, EcPk.fromPem(me.reader.$get(i)));
                me.secret = new Array<>();
                EcAsyncHelper eah = new EcAsyncHelper<>();
                eah.each(ary, new Callback2() {
                    @Override
                    public void $invoke(EcPk ecPk, final Callback0 callback0) {
                        EcRsaOaepAsync.encrypt(oldReader, payloadSecret.toEncryptableJson(), new Callback1() {
                            @Override
                            public void $invoke(String secret) {
                                EcArray.setRemove(me.secret, secret);
                                callback0.$invoke();
                            }
                        }, failure);
                    }
                }, new Callback1>() {
                    @Override
                    public void $invoke(Array strings) {
                        success.$invoke();

                    }
                });
            }
        }, failure);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy