encryptedstorage.js

1       var argscheck = require('cordova/argscheck'),
2           exec = require("cordova/exec");
3       
4       /**
5        * The EncryptedStorage class is used as a secure local store.  The EncryptedStorage API is based on the
6        * W3C web storage API, but has two major differences: it is asynchronous, and it has a constructor with
7        * a password.<br/>
8        * <br/>
9        * Note: There is a security flaw on some versions of Android with the Pseudo Random Number Generation.
10       * The first time the native code of this plugin runs it applies the fix for this issue.  However, the
11       * fix needs to be applied before any use of Java Cryptography Architecture primitives.  Therefore, it
12       * is a good idea to run this plugin (call a function that has a native component: length, key, getItem,
13       * setItem, removeItem, clear) before using any other security-related plugin, to protect yourself
14       * against the possibility that the other plugin does not apply this fix. No other Kapsel plugins are
15       * affected, so you need not do this on their behalf.  For more details about the security flaw, see
16       * <a href="http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html">
17       * http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html</a><br/>
18       * <br/>
19       * <b>Adding and Removing the EncryptedStorage Plugin</b><br/>
20       * The EncryptedStorage plugin is added and removed using the
21       * <a href="http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface">Cordova CLI</a>.<br/>
22       * <br/>
23       * To add the EncryptedStorage plugin to your project, use the following command:<br/>
24       * cordova plugin add <path to directory containing Kapsel plugins>\encryptedstorage<br/>
25       * <br/>
26       * To remove the EncryptedStorage plugin from your project, use the following command:<br/>
27       * cordova plugin rm com.sap.mp.cordova.plugins.encryptedstorage
28       * @namespace
29       * @alias EncryptedStorage
30       * @memberof sap
31       * @param {String} storeName The name of the store to create.  All stores with different names
32       * act independently, while stores with the same name (and same password) act as the same store.
33       * If null or undefined is passed, an empty string is used.
34       * @param {String} password The password of the store to create.  If null or undefined is passed,
35       * an empty string is used.
36       */
37      EncryptedStorage = function (storeName, password) {
38          // private variables
39          var that = this;
40          var storagePassword = password ? password : "";
41          var storageName = storeName ? storeName : "";
42      
43          // privileged functions
44      
45          /**
46           * This function gets the length of the store.  The length of a store
47           * is the number of key/value pairs that are in the store.
48           * @param {sap.EncryptedStorage~lengthSuccessCallback} successCallback If successful,
49           * the successCallback is invoked with the length of the store as
50           * the parameter.
51           * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
52           * the errorCallback is invoked with an ErrorInfo object as the parameter.
53      	 * @memberof sap.EncryptedStorage
54      	 * @function length
55      	 * @instance
56           * @example
57           * var store = new sap.EncryptedStorage("storeName", "storePassword");
58           * var successCallback = function(length) {
59           *    alert("Length is " + length);
60           * }
61           * var errorCallback = function(error) {
62           *    alert("An error occurred: " + JSON.stringify(error));
63           * }
64           * store.length(successCallback, errorCallback);
65           */
66          this.length = function (successCallback, errorCallback) {
67      		try{
68      			argscheck.checkArgs('FF', 'EncryptedStorage.length', arguments);
69      		}catch(ex){
70      			errorCallback(this.ERROR_INVALID_PARAMETER);
71      			return;
72      		}
73      
74              cordova.exec(successCallback, errorCallback, "EncryptedStorage",
75                  "length", [storageName, storagePassword]);
76          }
77      
78          /**
79           * This function gets the key corresponding to the given index.
80           * @param {number} index The index of the store for which to get the key.
81           * Valid indices are integers from zero (the first index), up to, but not including,
82           * the length of the store.  If the index is out of bounds, then the success
83           * callback is invoked with null as the parameter.
84           * @param {sap.EncryptedStorage~keySuccessCallback} successCallback If successful,
85           * the successCallback is invoked with the key as the parameter.
86           * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
87           * the errorCallback is invoked with an ErrorInfo object as the parameter.
88      	 * @memberof sap.EncryptedStorage
89      	 * @function key
90      	 * @instance
91           * @example
92           * // This example shows how to get the key for the last item.
93           * var store = new sap.EncryptedStorage("storeName", "storePassword");
94           * var errorCallback = function( error ){
95           *    alert("An error occurred: " + JSON.stringify(error));
96           * }
97           * var keySuccessCallback = function(key) {
98           *    alert("Last key is " + key);
99           * }
100          * var lengthSuccessCallback = function(length) {
101          *    store.key(length - 1, keySuccessCallback, errorCallback);
102          * }
103          * store.length(lengthSuccessCallback, errorCallback);
104          */
105         this.key = function (index, successCallback, errorCallback) {
106     		try{
107     			argscheck.checkArgs('NFF', 'EncryptedStorage.key', arguments);
108     		}catch(ex){
109     			errorCallback(this.ERROR_INVALID_PARAMETER);
110     			return;
111     		}
112     
113             cordova.exec(successCallback, errorCallback, "EncryptedStorage",
114                 "key", [storageName, storagePassword, index]);
115         }
116     
117         /**
118          * This function gets the value corresponding to the given key.  If there is no
119          * item with the given key, then the success callback is invoked with null as
120          * the parameter.
121          * @param {String} key The key of the item for which to get the value.  If null or undefined is
122          * passed, "null" is used.
123          * @param {sap.EncryptedStorage~getItemSuccessCallback} successCallback If successful,
124          * the successCallback is invoked with the value as the parameter (or null if the key
125          * did not exist).
126          * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
127          * the errorCallback is invoked with an ErrorInfo object as the parameter.
128     	 * @memberof sap.EncryptedStorage
129     	 * @function getItem
130     	 * @instance
131          * @example
132          * var store = new sap.EncryptedStorage("storeName", "storePassword");
133     	 * var successCallback = function(value) {
134     	 *    alert("Value is " + value);
135     	 * }
136     	 * var errorCallback = function(error) {
137     	 *    alert("An error occurred: " + JSON.stringify(error));
138     	 * }
139          * store.getItem("theKey", successCallback, errorCallback);
140          */
141         this.getItem = function (key, successCallback, errorCallback) {
142     		try{
143     			argscheck.checkArgs('SFF', 'EncryptedStorage.getItem', arguments);
144     		}catch(ex){
145     			errorCallback(this.ERROR_INVALID_PARAMETER);
146     			return;
147     		}
148     		
149             cordova.exec(successCallback, errorCallback, "EncryptedStorage",
150                 "getItem", [storageName, storagePassword, key]);
151         }
152     
153         /**
154          * This function sets an item with the given key and value.  If no item exists with
155          * the given key, then a new item is created.  If an item does exist with the
156          * the given key, then its value is overwritten with the given value.<br/>
157     	 * <br/>
158     	 * Note: On Android there is a size limit on the string to be stored.  See
159     	 * {@link sap.EncryptedStorage#SIMPLE_STRING_MAXIMUM_LENGTH} and {@link sap.EncryptedStorage#COMPLEX_STRING_MAXIMUM_LENGTH}
160     	 * for more details.
161          * @param {String} key The key of the item to set.  If null or undefined is passed,
162          * "null" is used.
163          * @param {String} value The value of the item to set.  If null or undefined is passed,
164          * "null" is used.
165          * @param {sap.EncryptedStorage~successCallback} successCallback If successful,
166          * the successCallback is invoked with no parameters.
167          * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
168          * the errorCallback is invoked with an ErrorInfo object as the parameter.
169     	 * @memberof sap.EncryptedStorage
170     	 * @function setItem
171     	 * @instance
172          * @example
173          * var store = new sap.EncryptedStorage("storeName", "storePassword");
174     	 * var successCallback = function() {
175     	 *    alert("Item has been set.");
176     	 * }
177     	 * var errorCallback = function(error) {
178     	 *    alert("An error occurred: " + JSON.stringify(error));
179     	 * }
180          * store.setItem("somekey", "somevalue", successCallback, errorCallback);
181          */
182         this.setItem = function (key, value, successCallback, errorCallback) {
183     		try{
184     			argscheck.checkArgs('SSFF', 'EncryptedStorage.setItem', arguments);
185     		}catch(ex){
186     			errorCallback(this.ERROR_INVALID_PARAMETER);
187     			return;
188     		}
189     		
190             cordova.exec(successCallback, errorCallback, "EncryptedStorage",
191                 "setItem", [storageName, storagePassword, key, value]);
192         }
193     
194         /**
195          * This function removes the item corresponding to the given key.  If there is no
196          * item with the given key in the first place, that is still counted as a success.
197          * @param {String} key The key of the item to remove.  If null or undefined is
198          * passed, "null" is used.
199          * @param {sap.EncryptedStorage~successCallback} successCallback If successful,
200          * the successCallback is invoked with no parameters.
201          * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
202          * the errorCallback is invoked with an ErrorInfo object as the parameter.
203     	 * @memberof sap.EncryptedStorage
204     	 * @function removeItem
205     	 * @instance
206          * @example
207          * var store = new sap.EncryptedStorage("storeName", "storePassword");
208          * var successCallback = function() {
209          *    alert("Value removed");
210          * }
211          * var errorCallback = function(error) {
212          *    alert("An error occurred: " + JSON.stringify(error));
213          * }
214          * store.removeItem("somekey", successCallback, errorCallback);
215          */
216         this.removeItem = function (key, successCallback, errorCallback) {
217     		try{
218     			argscheck.checkArgs('SFF', 'EncryptedStorage.removeItem', arguments);
219     		}catch(ex){
220     			errorCallback(this.ERROR_INVALID_PARAMETER);
221     			return;
222     		}
223     		
224             cordova.exec(successCallback, errorCallback, "EncryptedStorage",
225                 "removeItem", [storageName, storagePassword, key]);
226         }
227     
228         /**
229          * This function removes all items from the store.  If there are no
230          * items in the store in the first place, that is still counted as a success.
231          * @param {sap.EncryptedStorage~successCallback} successCallback If successful,
232          * the successCallback is invoked with no parameters.
233          * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
234          * the errorCallback is invoked with an ErrorInfo object as the parameter.
235     	 * @memberof sap.EncryptedStorage
236     	 * @function clear
237     	 * @instance
238          * @example
239          * var store = new sap.EncryptedStorage("storeName", "storePassword");
240          * var successCallback = function() {
241          *    alert("Store cleared!");
242          * }
243          * var errorCallback = function(error) {
244          *    alert("An error occurred: " + JSON.stringify();
245          * }
246          * store.clear(successCallback, errorCallback);
247          */
248         this.clear = function (successCallback, errorCallback) {
249     		try{
250     			argscheck.checkArgs('FF', 'EncryptedStorage.clear', arguments);
251     		}catch(ex){
252     			errorCallback(this.ERROR_INVALID_PARAMETER);
253     			return;
254     		}
255     		
256             cordova.exec(successCallback, errorCallback, "EncryptedStorage",
257                 "clear", [storageName, storagePassword]);
258         }
259     
260         /**
261          * This function deletes a store that has been created with EncryptedStorage.
262          * This allows for the creation of a new store with the same name and a different password.
263          * 
264          * @param {sap.EncryptedStorage~successCallback} successCallback If successful,
265          * the successCallback is invoked with no parameters.
266          * @param {sap.EncryptedStorage~errorCallback} errorCallback If there is an error,
267          * the errorCallback is invoked with an ErrorInfo object as the parameter.
268          * @memberof sap.EncryptedStorage
269          * @function deleteStore
270          * @example
271          * var successCallback = function() {
272          *    alert("Store deleted!");
273          * }
274          * var errorCallback = function(error) {
275          *    alert("An error occurred: " + JSON.stringify();
276          * }
277          * ks = new sap.EncryptedStorage("storename", "password");
278          * ks.deleteStore(successCallback, errorCallback);
279          */
280         this.deleteStore = function (successCallback, errorCallback) {
281         	try{
282         		argscheck.checkArgs('FF', 'EncryptedStorage.deleteStore', arguments);
283         	}catch(ex){
284         		errorCallback(this.ERROR_INVALID_PARAMETER);
285         		return;
286         	}
287         	
288         	cordova.exec(successCallback, errorCallback, "EncryptedStorage",
289         		"deleteStore", [storageName, storagePassword]);
290         }
291     };
292     
293     // Error codes
294     /**
295      * This error code indicates an unknown error occurred.
296      * @memberof sap.EncryptedStorage
297      * @name sap.EncryptedStorage#ERROR_UNKNOWN
298      * @constant
299      */
300     EncryptedStorage.prototype.ERROR_UNKNOWN = 0;
301     /**
302      * This error code indicates an invalid parameter was provided.
303      * (eg: a string given where a number was required).
304      * @memberof sap.EncryptedStorage
305      * @name sap.EncryptedStorage#ERROR_INVALID_PARAMETER
306      * @constant
307      */
308     EncryptedStorage.prototype.ERROR_INVALID_PARAMETER = 1;
309     /**
310      * This error code indicates that the operation failed due to an incorrect password.  The password is
311      * set by the constructor of {@link EncryptedStorage}.
312      * @memberof sap.EncryptedStorage
313      * @name sap.EncryptedStorage#ERROR_BAD_PASSWORD
314      * @constant
315      */
316     EncryptedStorage.prototype.ERROR_BAD_PASSWORD = 2;
317     /**
318      * This error indicates that the string was too large to store. Only applies to Android.  
319      * For iOS, no hard limit is imposed, but be aware of device memory constraints.
320      * @memberof sap.EncryptedStorage
321      * @name sap.EncryptedStorage#ERROR_GREATER_THAN_MAXIMUM_SIZE
322      * @constant
323      */
324     EncryptedStorage.prototype.ERROR_GREATER_THAN_MAXIMUM_SIZE = 3;
325     /**
326      * This constant is the length of the largest string that can successfully be stored on Android.  Only if all the
327      * characters in the string are encoded in 1 byte in UTF-8 can a string actually be this big.  Since
328      * characters in UTF-8 can take up to 4 bytes, if you do not know the contents of a string, the maximum
329      * length that is guaranteed to be successful is {@link EncryptedStorage.COMPLEX_STRING_MAXIMUM_LENGTH}, which is
330      * {@link EncryptedStorage.SIMPLE_STRING_MAXIMUM_LENGTH}/4.  Note that this size restriction is present only on
331      * Android and not iOS.
332      * @memberof sap.EncryptedStorage
333      * @name sap.EncryptedStorage#SIMPLE_STRING_MAXIMUM_LENGTH
334      * @constant
335      */
336     EncryptedStorage.prototype.SIMPLE_STRING_MAXIMUM_LENGTH = 1048527;
337     /**
338      * This constant is the length of the largest string that is guaranteed to be successfully stored on Android.  The
339      * limit depends on how many bytes the string takes up when encoded with UTF-8 (under which encoding
340      * characters can take up to 4 bytes).  This is the maximum length of a string for which every character
341      * takes all 4 bytes.  Note that this size restriction is present only on Android and not iOS.
342      * @memberof sap.EncryptedStorage
343      * @name sap.EncryptedStorage#COMPLEX_STRING_MAXIMUM_LENGTH
344      * @constant
345      */
346     EncryptedStorage.prototype.COMPLEX_STRING_MAXIMUM_LENGTH = 262131;
347     
348     module.exports = EncryptedStorage;
349     
350     
351     /**
352      * Callback function that is invoked on a successful call to a function that does
353      * not need to return anything.
354      *
355      * @callback sap.EncryptedStorage~successCallback
356      */
357     
358     /**
359      * Callback function that is invoked on a successful call to {@link EncryptedStorage.length}.
360      *
361      * @callback sap.EncryptedStorage~lengthSuccessCallback
362      *
363      * @param {number} length The number of key/value pairs in the store.
364      */
365     
366     /**
367      * Callback function that is invoked on a successful call to {@link EncryptedStorage.key}.
368      * If the key returned is null that means the index passed to {@link EncryptedStorage.key} was out of bounds.
369      *
370      * @callback sap.EncryptedStorage~keySuccessCallback
371      *
372      * @param {String} key The key corresponding to the given index.  Will be null if the index passed to
373      * {@link EncryptedStorage.key} was out of bounds.
374      */
375     
376     /**
377      * Callback function that is invoked on a successful call to {@link EncryptedStorage.getItem}.
378      * If the returned value is null, that means the key passed to {@link EncryptedStorage.getItem} did not exist.
379      *
380      * @callback sap.EncryptedStorage~getItemSuccessCallback
381      *
382      * @param {String} value The value of the item with the given key.  Will be null if the key passed to
383      * {@link EncryptedStorage.getItem} did not exist.
384      */
385     
386     /**
387      * Callback function that is invoked in case of an error.
388      *
389      * @callback sap.EncryptedStorage~errorCallback
390      *
391      * @param {number} errorCode An error code indicating what went wrong.  Will be one of {@link sap.EncryptedStorage#ERROR_UNKNOWN},
392      * {@link sap.EncryptedStorage#ERROR_INVALID_PARAMETER}, {@link sap.EncryptedStorage#ERROR_BAD_PASSWORD}, or
393      * {@link sap.EncryptedStorage#ERROR_GREATER_THAN_MAXIMUM_SIZE}.
394      *
395      * @example
396      * function errorCallback(errCode) {
397      *    //Set the default error message. Used if an invalid code is passed to the
398      *    //function (just in case) but also to cover the
399      *    //sap.EncryptedStorage.ERROR_UNKNOWN case as well.
400      *    var msg = "Unkown Error";
401      *    switch (errCode) {
402      *       case sap.EncryptedStorage.ERROR_INVALID_PARAMETER:
403      *          msg = "Invalid parameter passed to method";
404      *          break;
405      *       case sap.EncryptedStorage.ERROR_BAD_PASSWORD :
406      *          msg = "Incorrect password";
407      *          break;
408      *       case sap.EncryptedStorage.ERROR_GREATER_THAN_MAXIMUM_SIZE:
409      *          msg = "Item (string) value too large to write to store";
410      *          break;
411      *    };
412      *    //Write the error to the log
413      *    console.error(msg);
414      *    //Let the user know what happened
415      *    navigator.notification.alert(msg, null, "EncryptedStorage Error", "OK");
416      * };
417      */
418