Skip to content
Snippets Groups Projects
Select Git revision
  • ad142fee84b74ae8f168df8273cc76fb342f85e8
  • main default protected
  • release/1.1
  • encrypt_comments
  • mnemonic_dewif
  • authors_rules
  • 0.14
  • rtd
  • 1.2.1 protected
  • 1.2.0 protected
  • 1.1.1 protected
  • 1.1.0 protected
  • 1.0.0 protected
  • 1.0.0rc1 protected
  • 1.0.0rc0 protected
  • 1.0.0-rc protected
  • 0.62.0 protected
  • 0.61.0 protected
  • 0.60.1 protected
  • 0.58.1 protected
  • 0.60.0 protected
  • 0.58.0 protected
  • 0.57.0 protected
  • 0.56.0 protected
  • 0.55.1 protected
  • 0.55.0 protected
  • 0.54.3 protected
  • 0.54.2 protected
28 results

index.rst

Blame
  • crypto-services.js 27.54 KiB
    //var Base58, Base64, scrypt_module_factory = null, nacl_factory = null;
    
    angular.module('cesium.crypto.services', ['cesium.utils.services'])
    
      .factory('CryptoUtils', function($q, $timeout, ionicReady) {
        'ngInject';
    
        function test(regexpContent) {
          return new RegExp(regexpContent);
        }
    
        /**
         * CryptoAbstract, abstract class with useful methods
         * @type {number}
         */
        function CryptoAbstractService() {
          this.loaded = false;
          var that = this;
    
          this.copy = function(source) {
            _.forEach(_.keys(source), function(key) {
              that[key] = source[key];
            });
          };
    
          this.isLoaded = function() {
            return this.loaded;
          };
    
          this.util = this.util || {};
    
          /**
           * Converts an array buffer to a string
           *
           * @private
           * @param {ArrayBuffer} buf The buffer to convert
           * @param {Function} callback The function to call when conversion is complete
           */
          this.util.array_to_string = function(buf, callback) {
            var bb = new Blob([new Uint8Array(buf)]);
            var f = new FileReader();
            f.onload = function(e) {
              callback(e.target.result);
            };
            f.readAsText(bb);
          };
        }
    
        CryptoAbstractService.prototype.constants = {
          crypto_sign_BYTES: 64,
          crypto_secretbox_NONCEBYTES: 24,
          crypto_box_MACBYTES: 16,
          SEED_LENGTH: 32, // Length of the key
          SCRYPT_PARAMS:{
            SIMPLE: {
              N: 2048,
              r: 8,
              p: 1,
              memory: -1 // default
            },
            DEFAULT: {
              N: 4096,
              r: 16,
              p: 1,
              memory: -1 // default
            },
            SECURE: {
              N: 16384,
              r: 32,
              p: 2,
              memory: -1 // default
            },
            HARDEST: {
              N: 65536,
              r: 32,
              p: 4,
              memory: -1 // default
            },
            EXTREME: {
              N: 262144,
              r: 64,
              p: 8,
              memory: -1 // default
            }
          },
          REGEXP: {
            PUBKEY: '[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}',
            SECKEY: '[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{86,88}',
            FILE: {
              TYPE_LINE: '^Type: ([a-zA-Z0-9]+)\n',
              PUB: '\npub: ([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44})\n',
              SEC: '\nsec: ([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{86,88})\n?$'
            }
          }
        };
    
        CryptoAbstractService.prototype.regexp = {
          FILE: {
            TYPE_LINE: test(CryptoAbstractService.prototype.constants.REGEXP.FILE.TYPE_LINE),
            PUB: test(CryptoAbstractService.prototype.constants.REGEXP.FILE.PUB),
            SEC: test(CryptoAbstractService.prototype.constants.REGEXP.FILE.SEC)
          }
        };
    
        CryptoAbstractService.prototype.async_load_base58 = function(on_ready) {
          var that = this;
          if (Base58 !== null){return on_ready(Base58);}
          else {$timeout(function(){that.async_load_base58(on_ready);}, 100);}
        };
    
        CryptoAbstractService.prototype.async_load_scrypt = function(on_ready, options) {
          var that = this;
          if (scrypt_module_factory !== null){
            on_ready(scrypt_module_factory(options.requested_total_memory));
            that.scrypt.requested_total_memory = options.requested_total_memory;
            //console.log('inside async_load_scrypt', that); // TODO manage memory changes
          }
          else {$timeout(function(){that.async_load_scrypt(on_ready, options);}, 100);}
        };
    
        CryptoAbstractService.prototype.async_load_nacl_js = function(on_ready, options) {
          var that = this;
          if (nacl_factory !== null) {nacl_factory.instantiate(on_ready, options);}
          else {$timeout(function(){that.async_load_nacl_js(on_ready, options);}, 100);}
        };
    
        CryptoAbstractService.prototype.async_load_base64 = function(on_ready) {
          var that = this;
          if (Base64 !== null) {on_ready(Base64);}
          else {$timetout(function(){that.async_load_base64(on_ready);}, 100);}
        };
    
        CryptoAbstractService.prototype.async_load_sha256 = function(on_ready) {
          var that = this;
          if (sha256 !== null){return on_ready(sha256);}
          else {$timeout(function(){that.async_load_sha256(on_ready);}, 100);}
        };
    
        // TODO pub file in file.* functions
        //CryptoAbstractService.prototype.file = {};
    
        CryptoAbstractService.prototype.readKeyFile = function(file, withSecret) {
          var that = this;
    
          if (file && file.content) {
            return  that.parseKeyFileContent(file.content, withSecret);
          }
    
          return $q(function(resolve, reject) {
            if (!file) {
              return reject('Argument [file] is missing');
            }
    
            console.debug('[crypto] [keypair] reading file: ', file);
            var reader = new FileReader();
            reader.onload = function (event) {
              that.parseKeyFileContent(event.target.result, withSecret)
                .then(function (res) {
                  resolve(res);
                })
                .catch(function (err) {
                  reject(err);
                });
            };
            reader.readAsText(file, 'utf8');
          });
        };
    
        CryptoAbstractService.prototype.parseKeyFileContent = function(content, withSecret, defaultType) {
          var that = this;
          return $q(function(resolve, reject) {
            if (!content) {
              return reject('Argument [content] is missing');
            }
            var typeMatch = that.regexp.FILE.TYPE_LINE.exec(content);
    
            // no Type (first line)
            if (!typeMatch) {
              // Add default type then retry
              return resolve(that.parseKeyFileContent('Type: PubSec\n' + content, withSecret, true));
            }
    
            var type = typeMatch[1];
    
            // Type: PubSec
            if (type == 'PubSec') {
              var pubMatch = that.regexp.FILE.PUB.exec(content);
              if (!pubMatch) {
                return reject('Missing [pub] field in file, or invalid public key value');
              }
              if (!withSecret) {
                return resolve({
                  signPk: that.base58.decode(pubMatch[1])
                });
              }
              var secMatch = that.regexp.FILE.SEC.exec(content);
              if (!secMatch) {
                return reject('Missing [sec] field in file, or invalid secret key value');
              }
              return resolve({
                signPk: that.base58.decode(pubMatch[1]),
                signSk: that.base58.decode(secMatch[1])
              });
            }
            else {
              if (defaultType) {
                return reject('Bad file format: missing Type field');
              }
              else {
                return reject('Bad file format, unknown type [' + type + ']');
              }
            }
          });
        };
    
        // Web crypto API - see https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
        var crypto =  window.crypto || window.msCrypto || window.Crypto;
        if (crypto && crypto.getRandomValues) {
          CryptoAbstractService.prototype.crypto = crypto;
          CryptoAbstractService.prototype.util = {};
          CryptoAbstractService.prototype.util.random_nonce = function() {
            var nonce = new Uint8Array(crypto_secretbox_NONCEBYTES);
            this.crypto.getRandomValues(nonce);
            return $q.when(nonce);
          };
        }
        else {
          // TODO: add a default function ?
          //CryptoAbstractService.prototype.random_nonce = function() {
          // var nonce = new Uint8Array(crypto_secretbox_NONCEBYTES);
          // var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
          // for(var i = 0; i < length; i++) {
          //   text += possible.charAt(Math.floor(Math.random() * possible.length));
          // }
          //}
        }
    
        function FullJSServiceFactory() {
          this.id = 'FullJS';
    
          // libraries handlers
          this.scrypt = null;
          this.nacl = null;
          this.base58 = null;
          this.base64 = null;
          var that = this;
    
          this.util = this.util || {};
          this.util.decode_utf8 = function (s) {
            var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
            for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
            return b;
          };
          this.util.encode_utf8 = function (s) {
            return that.nacl.encode_utf8(s);
          };
          this.util.encode_base58 = function (a) { // TODO : move to abstract factory
            return that.base58.encode(a);
          };
          this.util.decode_base58 = function (a) { // TODO : move to abstract factory
            var i;
            a = that.base58.decode(a);
            var b = new Uint8Array(a.length);
            for (i = 0; i < a.length; i++) b[i] = a[i];
            return b;
          };
          this.util.decode_base64 = function (a) {
            return that.base64.decode(a);
          };
          this.util.encode_base64 = function (b) {
            return that.base64.encode(b);
          };
    
          this.util.hash_sha256 = function (message) {
            return $q(function (resolve) {
              var msg = that.util.decode_utf8(message);
              var hash = that.nacl.to_hex(that.nacl.crypto_hash_sha256(msg));
              resolve(hash.toUpperCase());
            });
          };
          this.util.random_nonce = function () {
            if (that.crypto && that.crypto.getRandomValues) {
              var nonce = new Uint8Array(that.constants.crypto_secretbox_NONCEBYTES);
              that.crypto.getRandomValues(nonce);
              return $q.when(nonce);
            }
            else {
              return $q.when(that.nacl.crypto_box_random_nonce());
            }
          };
    
          /**
           * Compute the box key pair, from a sign key pair
           */
          this.box_keypair_from_sign = function (signKeyPair) {
            if (signKeyPair.bokSk && signKeyPair.boxPk) return $q.when(signKeyPair);
            return $q.when(that.nacl.crypto_box_keypair_from_sign_sk(signKeyPair.signSk));
          };
    
          /**
           * Compute the box public key, from a sign public key
           */
          this.box_pk_from_sign = function (signPk) {
            return $q.when(that.nacl.crypto_box_pk_from_sign_pk(signPk));
          };
    
          this.box_sk_from_sign = function (signSk) {
            return $q.when(that.nacl.crypto_box_sk_from_sign_sk(signSk));
          };
    
          /**
           * Encrypt a message, from a key pair
           */
          this.box = function(message, nonce, recipientPk, senderSk) {
            return $q(function (resolve, reject) {
              if (!message) {
                resolve(message);
                return;
              }
              var messageBin = that.util.decode_utf8(message);
              if (typeof recipientPk === "string") {
                recipientPk = that.util.decode_base58(recipientPk);
              }
    
              //console.debug('Original message: ' + message);
              try {
                var ciphertextBin = that.nacl.crypto_box(messageBin, nonce, recipientPk, senderSk);
                var ciphertext = that.util.encode_base64(ciphertextBin);
    
                //console.debug('Encrypted message: ' + ciphertext);
                resolve(ciphertext);
              }
              catch (err) {
                reject(err);
              }
            });
          };
    
          /**
           * Decrypt a message, from a key pair
           */
          this.box_open = function (cypherText, nonce, senderPk, recipientSk) {
            return $q(function (resolve, reject) {
              if (!cypherText) {
                resolve(cypherText);
                return;
              }
              var ciphertextBin = that.util.decode_base64(cypherText);
              if (typeof senderPk === "string") {
                senderPk = that.util.decode_base58(senderPk);
              }
    
              try {
                var message = that.nacl.crypto_box_open(ciphertextBin, nonce, senderPk, recipientSk);
                that.util.array_to_string(message, function (result) {
                  //console.debug('Decrypted text: ' + result);
                  resolve(result);
                });
              }
              catch (err) {
                reject(err);
              }
    
            });
          };
    
          /**
           * Create key pairs (sign and box), from salt+password (Scrypt auth)
           */
          this.scryptKeypair = function(salt, password, scryptParams) {
            return $q(function(resolve, reject) {
              var seed = that.scrypt.crypto_scrypt(
                that.util.encode_utf8(password),
                that.util.encode_utf8(salt),
                scryptParams && scryptParams.N || that.constants.SCRYPT_PARAMS.DEFAULT.N,
                scryptParams && scryptParams.r || that.constants.SCRYPT_PARAMS.DEFAULT.r,
                scryptParams && scryptParams.p || that.constants.SCRYPT_PARAMS.DEFAULT.p,
                that.constants.SEED_LENGTH);
              var signKeypair = that.nacl.crypto_sign_seed_keypair(seed);
              var boxKeypair = that.nacl.crypto_box_seed_keypair(seed);
              resolve({
                signPk: signKeypair.signPk,
                signSk: signKeypair.signSk,
                boxPk: boxKeypair.boxPk,
                boxSk: boxKeypair.boxSk
              });
            });
          };
    
          /**
           * Get sign pk from salt+password (scrypt auth)
           */
          this.scryptSignPk = function(salt, password, scryptParams) {
            return $q(function(resolve, reject) {
              try {
                var seed = that.scrypt.crypto_scrypt(
                  that.util.encode_utf8(password),
                  that.util.encode_utf8(salt),
                  scryptParams && scryptParams.N || that.constants.SCRYPT_PARAMS.DEFAULT.N,
                  scryptParams && scryptParams.r || that.constants.SCRYPT_PARAMS.DEFAULT.r,
                  scryptParams && scryptParams.p || that.constants.SCRYPT_PARAMS.DEFAULT.p,
                  that.constants.SEED_LENGTH);
                var signKeypair = that.nacl.crypto_sign_seed_keypair(seed);
                resolve(signKeypair.signPk);
              }
              catch(err) {
                reject(err);
              }
            });
          };
    
          /**
           * Verify a signature of a message, for a pubkey
           */
          this.verify = function (message, signature, pubkey) {
            return $q(function(resolve, reject) {
              var msg = that.util.decode_utf8(message);
              var sig = that.util.decode_base64(signature);
              var pub = that.util.decode_base58(pubkey);
              var sm = new Uint8Array(that.constants.crypto_sign_BYTES + msg.length);
              var i;
              for (i = 0; i < that.constants.crypto_sign_BYTES; i++) sm[i] = sig[i];
              for (i = 0; i < msg.length; i++) sm[i+that.constants.crypto_sign_BYTES] = msg[i];
    
              // Call to verification lib...
              var verified = that.nacl.crypto_sign_open(sm, pub) !== null;
              resolve(verified);
            });
          };
    
          /**
           * Sign a message, from a key pair
           */
          this.sign = function(message, keypair) {
            return $q(function(resolve, reject) {
              var m = that.util.decode_utf8(message);
              var sk = keypair.signSk;
              var signedMsg = that.nacl.crypto_sign(m, sk);
              var sig = new Uint8Array(that.constants.crypto_sign_BYTES);
              for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
              var signature = that.base64.encode(sig);
              resolve(signature);
            });
          };
    
          this.load = function() {
            var deferred = $q.defer();
            var naclOptions = {};
            var scryptOptions = {};
            if (ionic.Platform.grade.toLowerCase()!='a') {
              console.info('Reduce NaCl memory because plateform grade is not [a] but [' + ionic.Platform.grade + ']');
              naclOptions.requested_total_memory = 16 * 1048576; // 16 Mo
            }
            var loadedLib = 0;
            var checkAllLibLoaded = function() {
              loadedLib++;
              if (loadedLib === 4) {
                that.loaded = true;
                deferred.resolve();
              }
            };
            this.async_load_nacl_js(function(lib) {
              that.nacl = lib;
              checkAllLibLoaded();
            }, naclOptions);
            this.async_load_scrypt(function(lib) {
              that.scrypt = lib;
              checkAllLibLoaded();
            }, scryptOptions);
            this.async_load_base58(function(lib) {
              that.base58 = lib;
              checkAllLibLoaded();
            });
            this.async_load_base64(function(lib) {
              that.base64 = lib;
              checkAllLibLoaded();
            });
            return deferred.promise;
          };
    
          // Shortcuts
          this.util.hash = that.util.hash_sha256;
          this.box = {
            keypair: {
              fromSignKeypair: that.box_keypair_from_sign,
              skFromSignSk: that.box_sk_from_sign,
              pkFromSignPk: that.box_pk_from_sign
            },
            pack: that.box,
            open: that.box_open
          };
        }
        FullJSServiceFactory.prototype = new CryptoAbstractService();
    
    
        /* -----------------------------------------------------------------------------------------------------------------
         * Service that use Cordova MiniSodium plugin
         * ----------------------------------------------------------------------------------------------------------------*/
    
        /***
         * Factory for crypto, using Cordova plugin
         */
        function CordovaServiceFactory() {
    
          this.id = 'MiniSodium';
    
          // libraries handlers
          this.nacl = null; // the cordova plugin
          this.base58= null;
          this.sha256= null;
          var that = this;
    
          // functions
          this.util = this.util || {};
          this.util.decode_utf8 = function(s) {
            return that.nacl.to_string(s);
          };
          this.util.encode_utf8 = function(s) {
            return that.nacl.from_string(s);
          };
          this.util.encode_base58 = function(a) {
            return that.base58.encode(a);
          };
          this.util.decode_base58 = function(a) {
            var i;
            a = that.base58.decode(a);
            var b = new Uint8Array(a.length);
            for (i = 0; i < a.length; i++) b[i] = a[i];
            return b;
          };
          this.util.decode_base64 = function (a) {
            return that.nacl.from_base64(a);
          };
          this.util.encode_base64 = function (b) {
            return that.nacl.to_base64(b);
          };
          this.util.hash_sha256 = function(message) {
            return $q.when(that.sha256(message).toUpperCase());
          };
          this.util.random_nonce = function() {
            var nonce = new Uint8Array(that.constants.crypto_secretbox_NONCEBYTES);
            that.crypto.getRandomValues(nonce);
            return $q.when(nonce);
          };
    
          /**
           * Create key pairs (sign and box), from salt+password (Scrypt), using cordova
           */
          this.scryptKeypair = function(salt, password, scryptParams) {
            var deferred = $q.defer();
    
            that.nacl.crypto_pwhash_scryptsalsa208sha256_ll(
              that.nacl.from_string(password),
              that.nacl.from_string(salt),
              scryptParams && scryptParams.N || that.constants.SCRYPT_PARAMS.DEFAULT.N,
              scryptParams && scryptParams.r || that.constants.SCRYPT_PARAMS.DEFAULT.r,
              scryptParams && scryptParams.p || that.constants.SCRYPT_PARAMS.DEFAULT.p,
              that.constants.SEED_LENGTH,
              function (err, seed) {
                if (err) { deferred.reject(err); return;}
    
                that.nacl.crypto_sign_seed_keypair(seed, function (err, signKeypair) {
                  if (err) { deferred.reject(err); return;}
                  var result = {
                    signPk: signKeypair.pk,
                    signSk: signKeypair.sk
                  };
                  that.box_keypair_from_sign(result)
                    .then(function(boxKeypair) {
                      result.boxPk = boxKeypair.pk;
                      result.boxSk = boxKeypair.sk;
                      deferred.resolve(result);
                    })
                    .catch(function(err) {
                      deferred.reject(err);
                    });
                });
    
              }
            );
    
            return deferred.promise;
          };
    
          /**
           * Get sign PK from salt+password (Scrypt), using cordova
           */
          this.scryptSignPk = function(salt, password, scryptParams) {
            var deferred = $q.defer();
    
            that.nacl.crypto_pwhash_scryptsalsa208sha256_ll(
              that.nacl.from_string(password),
              that.nacl.from_string(salt),
              scryptParams && scryptParams.N || that.constants.SCRYPT_PARAMS.DEFAULT.N,
              scryptParams && scryptParams.r || that.constants.SCRYPT_PARAMS.DEFAULT.r,
              scryptParams && scryptParams.p || that.constants.SCRYPT_PARAMS.DEFAULT.p,
              that.constants.SEED_LENGTH,
              function (err, seed) {
                if (err) { deferred.reject(err); return;}
    
                that.nacl.crypto_sign_seed_keypair(seed, function (err, signKeypair) {
                  if (err) { deferred.reject(err); return;}
                  deferred.resolve(signKeypair.pk);
                });
    
              }
            );
    
            return deferred.promise;
          };
    
          /**
           * Verify a signature of a message, for a pubkey
           */
          this.verify = function (message, signature, pubkey) {
            var deferred = $q.defer();
            that.nacl.crypto_sign_verify_detached(
              that.nacl.from_base64(signature),
              that.nacl.from_string(message),
              that.nacl.from_base64(pubkey),
              function(err, verified) {
                if (err) { deferred.reject(err); return;}
                deferred.resolve(verified);
              });
            return deferred.promise;
          };
    
          /**
           * Sign a message, from a key pair
           */
          this.sign = function(message, keypair) {
            var deferred = $q.defer();
    
            that.nacl.crypto_sign(
              that.nacl.from_string(message), // message
              keypair.signSk, // sk
              function(err, signedMsg) {
                if (err) { deferred.reject(err); return;}
                var sig;
                if (signedMsg.length > that.constants.crypto_sign_BYTES) {
                  sig = new Uint8Array(that.constants.crypto_sign_BYTES);
                  for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
                }
                else {
                  sig = signedMsg;
                }
                var signature = that.nacl.to_base64(sig);
                deferred.resolve(signature);
              });
    
            return deferred.promise;
          };
    
          /**
           * Compute the box key pair, from a sign key pair
           */
          this.box_keypair_from_sign = function(signKeyPair) {
            if (signKeyPair.bokSk && signKeyPair.boxPk) return $q.when(signKeyPair);
            var deferred = $q.defer();
            var result = {};
            that.nacl.crypto_sign_ed25519_pk_to_curve25519(signKeyPair.signPk, function(err, boxPk) {
              if (err) { deferred.reject(err); return;}
              result.boxPk = boxPk;
              if (result.boxSk) deferred.resolve(result);
            });
            that.nacl.crypto_sign_ed25519_sk_to_curve25519(signKeyPair.signSk, function(err, boxSk) {
              if (err) { deferred.reject(err); return;}
              result.boxSk = boxSk;
              if (result.boxPk) deferred.resolve(result);
            });
    
            return deferred.promise;
          };
    
          /**
           * Compute the box public key, from a sign public key
           */
          this.box_pk_from_sign = function(signPk) {
            var deferred = $q.defer();
            that.nacl.crypto_sign_ed25519_pk_to_curve25519(signPk, function(err, boxPk) {
              if (err) { deferred.reject(err); return;}
              deferred.resolve(boxPk);
            });
            return deferred.promise;
          };
    
          /**
           * Compute the box secret key, from a sign secret key
           */
          this.box_sk_from_sign = function(signSk) {
            var deferred = $q.defer();
            that.nacl.crypto_sign_ed25519_sk_to_curve25519(signSk, function(err, boxSk) {
              if (err) { deferred.reject(err); return;}
              deferred.resolve(boxSk);
            });
            return deferred.promise;
          };
    
          /**
           * Encrypt a message, from a key pair
           */
          this.box = function(message, nonce, recipientPk, senderSk) {
            if (!message) {
              return $q.reject('No message');
            }
            var deferred = $q.defer();
    
            var messageBin = that.nacl.from_string(message);
            if (typeof recipientPk === "string") {
              recipientPk = that.util.decode_base58(recipientPk);
            }
    
            that.nacl.crypto_box_easy(messageBin, nonce, recipientPk, senderSk, function(err, ciphertextBin) {
              if (err) { deferred.reject(err); return;}
              var ciphertext = that.util.encode_base64(ciphertextBin);
              //console.debug('Encrypted message: ' + ciphertext);
              deferred.resolve(ciphertext);
            });
            return deferred.promise;
          };
    
          /**
           * Decrypt a message, from a key pair
           */
          this.box_open = function(cypherText, nonce, senderPk, recipientSk) {
            if (!cypherText) {
              return $q.reject('No cypherText');
            }
            var deferred = $q.defer();
    
            var ciphertextBin = that.nacl.from_base64(cypherText);
            if (typeof senderPk === "string") {
              senderPk = that.util.decode_base58(senderPk);
            }
    
            // Avoid crash if content has not the minimal length - Fix #346
            if (ciphertextBin.length < that.constants.crypto_box_MACBYTES) {
              deferred.reject('Invalid cypher content length');
              return;
            }
    
            that.nacl.crypto_box_open_easy(ciphertextBin, nonce, senderPk, recipientSk, function(err, message) {
              if (err) { deferred.reject(err); return;}
              that.util.array_to_string(message, function(result) {
                //console.debug('Decrypted text: ' + result);
                deferred.resolve(result);
              });
            });
    
            return deferred.promise;
          };
    
          this.load = function() {
            var deferred = $q.defer();
            if (!window.plugins || !window.plugins.MiniSodium) {
              deferred.reject("Cordova plugin 'MiniSodium' not found. Please load Full JS implementation instead.");
            }
            else {
              that.nacl = window.plugins.MiniSodium;
              var loadedLib = 0;
              var checkAllLibLoaded = function() {
                loadedLib++;
                if (loadedLib == 2) {
                  that.loaded = true;
                  deferred.resolve();
                }
              };
              that.async_load_base58(function(lib) {
                that.base58 = lib;
                checkAllLibLoaded();
              });
              that.async_load_sha256(function(lib) {
                that.sha256 = lib;
                checkAllLibLoaded();
              });
            }
    
            return deferred.promise;
          };
    
          // Shortcuts
          this.util.hash = that.util.hash_sha256;
          this.box = {
            keypair: {
              fromSignKeypair: that.box_keypair_from_sign,
              skFromSignSk: that.box_sk_from_sign,
              pkFromSignPk: that.box_pk_from_sign
            },
            pack: that.box,
            open: that.box_open
          };
        }
        CordovaServiceFactory.prototype = new CryptoAbstractService();
    
        /* -----------------------------------------------------------------------------------------------------------------
         * Create service instance
         * ----------------------------------------------------------------------------------------------------------------*/
    
    
        var service = new CryptoAbstractService();
    
        var isDevice = true;
        // removeIf(android)
        // removeIf(ios)
        isDevice = false;
        // endRemoveIf(ios)
        // endRemoveIf(android)
    
        //console.debug("[crypto] Created CryptotUtils service. device=" + isDevice);
    
        ionicReady().then(function() {
          console.debug('[crypto] Starting...');
          var now = new Date().getTime();
    
          var serviceImpl;
    
          // Use Cordova plugin implementation, when exists
          if (isDevice && window.plugins && window.plugins.MiniSodium && crypto && crypto.getRandomValues) {
            console.debug('[crypto] Loading Cordova MiniSodium implementation...');
            serviceImpl = new CordovaServiceFactory();
          }
          else {
            console.debug('[crypto] Loading FullJS implementation...');
            serviceImpl = new FullJSServiceFactory();
          }
    
          // Load (async lib)
          serviceImpl.load()
            .catch(function(err) {
              console.error(err);
              throw err;
            })
            .then(function() {
              service.copy(serviceImpl);
              console.debug('[crypto] Loaded \'{0}\' implementation in {1}ms'.format(service.id, new Date().getTime() - now));
            });
    
        });
    
    
        return service;
      })
    ;