SeaSalt_Keychain


SeaSalt_Keychain

Keychain Management and Storage Engine based on LocalStorage and AEAD encryption

Keychain Introduction

This class is designed to generate and maintain cryptographic data and optionally store it in localStorage. It supports multiple encryption key configurations, recovery keys, 256-bit XChaCha20-Poly1305 AEAD encryption, and more.

Basic Features

  1. Store and manage multiple key configurations
  2. Provide recovery keys and codes to restore access when a password or key is lost
  3. Storage is namespaced and will not overlap with other keys
  4. Storage keys and data are fully encrypted with an encryption key and salt not accessible by looking at stored configuration
  5. Encryption password changeable without rewriting encrypted storage
  6. Storage is optional and keychain can be used purely for temporary data/message encryption

Recipes

First Time Key Creation

Uses: keychain.create(), SeaSalt_Tools.randomString()

//  first let's initiate the keychain
//  it will attempt to locate keychain data; otherwise it creates a new keychain
let keychain = new SeaSalt_Keychain();

//  okay, now let's make a new encryption key configuration
//  first we need a good, strong password. here we will generate one using SeaSalt_Tools.randomString
let password = keychain.tools.randomString(32); // returns a password like: ]IK4,=Qa?3,@@?@AW`N&or_l#eTy+K^:

//  next let's create a keychain and provide it a good name
let result = keychain.create('My Keychain', password);

//  result should now contain an object with the key signature and recovery codes
//  in the event you lose the password or encryption key data, the recovery codes and keys can be used to restore access

Encrypting and Decrypting Data

Uses: keychain.encrypt(), keychain.decrypt()

//  now that you've got your key setup, let's use it.
let original = 'My original message or string';
let ciphertext = keychain.encrypt(original);

//  ciphertext should look like: 4bd7b37447fb3495349f82e8d0da1fe14b27f3182102cf9cc1c5d7ec9c638c11...
//  later on you decide to decrypt it
let decrypted = keychain.decrypt(ciphertext);

//  decrypted should contain: My original message or string

Encrypting and Decrypting LocalStorage

//  basically the same as above but using different methods to facilitate the storage part
let storagekey = keychain.write('my storage key', original);

//  the storage key name you specify is encrypted according to keychain.key(key)
//  in this case, the key might look like seasalt:keychain:DpfXY1eKueSUXt4a:0:17771942b10497dc9d3a7f81b46e7cee2149f08dfed943a61562c7b69886d1b5...
//  and the storage contents might look like: ae47fab647c46d3693c287cee088f9997d57996e73155a6d58191775611f22c4...

//  later you decide to read the file
let decrypted = keychain.read('my storage key');

//  storage key names are unique to each key configuration. if you were to load a different key config and read the same storage key the result would be empty or an entirely different result.

Security for the Paranoid

//  okay, so let's say you want to make it really difficult for an attacker break in

//  first, without changing your password you could instead repackage the secret box
//  the secret box contains the encryption key and salt
//  you can regenerate its secret box easily without changing the password
keychain.update();

//  this time we'll change the password and generate new secret box
let newPassword = keychain.tools.randomString(32);
keychain.update(newPassword);

//  but what if that isn't enough? you can instead rekey the encryption key
//  this will generate a new encryption key, secret box, and update any locally encrypted data with the new key
//  this will also invalidate all existing recovery tokens and history data!
keychain.rekey(true);

Using Recovery Codes

Uses: keychain.find_recovery(), keychain.restore_recovery(), keychain.read(), SeaSalt_Tools.randomString()

//  first, let's restore from a recovery key presently in the configuration
//  let's test our recovery token using a key signature a recovery code we saved from keychain.create()
let keychain = new SeaSalt_Keychain();
let recoverykeys = keychain.find_recovery('P9WgA8taa9nc', '147918090902');

//  recoverykeys should now be an array with at least one recovery key stored inside it
//  we can restore it directly
let newPassword = keychain.tools.randomString(32);
keychain.restore_recovery('P9WgA8taa9nc', '147918090902', recoverykeys, newPassword);

//  alternatively, if the keychain is missing you could instead provide the recoverykey yourself
keychain.restore_recovery('P9WgA8taa9nc', '147918090902', '6ccc93ea0679919986391ec327f7414587ff75641157532ddb736dcbca4498f4...', newPassword);

//  if successful the key will be restored and automatically opened
//  we should be able to read its storage keys again
keychain.read('my storage key');

Constructor

new SeaSalt_Keychain(sigopt, passwordopt, configopt)

Parameters
sig:Object|string (optional)

Key signature or initial config

password:string (optional)

Key password or keychain data

config:Object (optional)

User configuration

Properties
config:Object (optional)

Keychain configuration

config.debug:boolean = false (optional)

Whether or not SeaSalt_Keychain is running in debugging mode

config.hash:string = sha512 (optional)

Hashing algorithm to use (sha256, sha512)

config.keysaltLength:number = 8 (optional)

Length of the private key salt for hashing

config.lock:boolean = true (optional)

Lock open keychains to try preventing write conflicts

config.log:function (optional)

Function for handling logging calls

config.maxRecoveryAge:number (optional)

Maximum length of time in milliseconds to keep a recovery key before automatically deleting it

config.maxRecoveryPoints:number = 5 (optional)

Maximum number of recovery keys to keep per signature

config.minimumEntropy:number = 6 (optional)

Minimum password entropy required

config.minimumKeyLength:number = 6 (optional)

Minimum password character length

config.minimumStrength:number = 1 (optional)

Minimum password strength

config.recoveryTokenCount:number = 2 (optional)

Number of recovery tokens to generate when creating a new key

config.recoveryTokenLength:number = 12 (optional)

Length of the recovery code

config.saltLength:number = 8 (optional)

Length of the public salt for hashing

config.signatureLength:number = 12 (optional)

Length of generates key signatures

config.readonly:boolean = false (optional)

Operate in read-only mode

config.storage:Object (optional)

Storage API bindings and settings (custom methods must interface localStorage)

config.storage.checksums:boolean = true (optional)

Whether or not to record storage content checksums

config.storage.enabled:boolean = true (optional)

Whether or not to enable localStorage.

Setting this to false will disable keychain.read, keychain.write, keychain.purge, and keychain.rekey.

config.storage.includeMeta:string = false (optional)

Include metadata in keychain.read() responses

config.storage.prefix:string = seasalt:keychain: (optional)

Prefix for all storage keys

config.storage.read:function = localStorage.getItem (optional)

Function for reading from storage keys

config.storage.write:function = localStorage.setItem (optional)

Function for writing to storage keys

config.storage.delete:function = localStorage.removeItem (optional)

Function for deleting storage keys

config.storage.list:function = Object.keys(localStorage) (optional)

Lists all found storage keys

ready:boolean

Keychain state

keys:Object

Keychain configuration data keychain.keyconf

history:Object

Stored secret boxes whose configuration has otherwise been deleted

runtimeId:string

Runtime ID for the new keychain instance

Examples

Basic Usage

let keychain = new SeaSalt_Keychain();

Provide a Configuration

let keychain = new SeaSalt_Keychain({
    hash: sha256,
    recoveryTokenCount: 3,
    storage: {
        prefix: 'mycustom:prefix:'
    }
});

Open key configuration on construction

let keychain = new SeaSalt_Keychain('DpfXY1eKueSUXt4a', 'mypassword');

Methods

backup(sigsopt, configopt):JSZip|boolean

Generates a backup of all Keychain data or just the specified sigs. Requires JSZip dependency.

If you're looking to backup just the key configuration see keychain.export_key.

Parameters
sigs:string|Array|function|Object = keychain.listkeys (optional)

Key signature or array of signatures to backup data for. Can also be a shortcut for config or config.callback.

config:Object|function (optional)

Backup configuration or shortcut for config.callback

config.all:boolean = false (optional)

Include all keys when running decrypt mode

config.callback:function (optional)

Callback to send generates Zip file

function(content) {
    //  content is zip file
}
config.sigs:Object (optional)

Decrypt storage data for supplied keys.

let config = {sigs: {
    sig1: 'password',
    sig2: 'password',
    ...
}}
Returns
Type
:JSZip|boolean

Returns an instance of JSZip if no callback is provided in the backup configuration.

If a callback is provided, the backup zip file is sent to it.

Examples

Basic Usage

let keychain = new SeaSalt_Keychain();
let zip = keychain.backup();

//  see JSZip for more information on what to do with the return data

Custom Callback

//  FileSaver.js is a very handy tool for starting save file dialogs
keychain.backup(function(content) {
   saveAs(content, 'mybackup.zip');
});

Decrypt Mode

//  generate a backup of just the provided signatures
keychain.backup({
   sigs: {
      sig1: 'password',
      sig2: 'password'
   }
});

//  generate a decrypted backup for the provided signatures but include all other data
keychain.backup({
   sigs: {
      sig1: 'password',
      sig2: 'password'
   },
   all: true
});

check(sig, userPassword):boolean

An alias for keychain.open(sig, userPassword, true);

Parameters
sig:string

Key signature to check

userPassword:string

User password to check

Returns
Type
:boolean

Returns true or false

clean(config, scanopt):Object

Searches for orphaned data in the keychain and storage.

Parameters
config:Array|string

String or array containing areas to check.

Possible values are: history, mode, storage, all

scan:boolean = false (optional)

Scan only; do not delete matches

Returns
Type
:Object

Returns an object identifying orphaned items

{
    "history": [],
    "mode": [],
    "storage": []
}
Examples

Clean a Single Item

let keychain = new SeaSalt_Keychain();
keychain.clean('storage');

Clean a Multiple Items

let keychain = new SeaSalt_Keychain();
keychain.clean(['storage', 'mode']);

//  or all items
keychain.clean('all');

close():boolean

Close an opened key and erase all loaded settings

Returns
Type
:boolean

create(userPassword, configopt):Object|boolean

Create a new key configuration.

Recovery Codes

The recovery codes are used to access the recovery keys.

The recovery keys contain a copy of the original encryption key. A few are generated at creation and are stored in the keychain. You can create more later on.

If you have they keychain, then you only need the recovery codes to use the keys. If you lose the keychain entirely, then you can provide both the code and key at restoration time.

Modes

The key configuration can be set to either hash or aead encrypt the storage key name.

Mode 0 uses a salted SHA256 hash for the encrypted storage keys.

Mode 1 uses a salted SHA512 hash for the encrypted storage keys.

Parameters
userPassword:string

Password for the key configuration

config:Object (optional)

User configuration data

config.mode:string = 1 (optional)

Storage key encryption mode (sha256=0, sha512=1)

config.name:string = Date (optional)

Name for this key configuration

Returns
Type
:Object|boolean

Returns an object containing the key signature and recovery codes/keys or false on failure

Example
let keychain = new SeaSalt_Keychain();
keychain.create('mymuchbetterpassword');

//  returns an object like
{
     "sig": "DpfXY1eKueSUXt4a",
     "codes": {
          "333400752028": "8db9a168853bfa778b510f93b99f8783ba48f0b7a8a7c31eaf317437dc9f22b4...",
          "686119075418": "6ccc93ea0679919986391ec327f7414587ff75641157532ddb736dcbca4498f4..."
     }
}

create_recovery(sig, userPasswordopt, boxopt):Object

Generates a random code and corresponding secret box

Parameters
sig:string

Key configuration signature

userPassword:string (optional)

Hashed passphrase of the userPassword

box:string (optional)

Secret box to use for creating the recovery object

Returns
Type
:Object

Returns an object containing a recovery code and recovery key

Examples

Basic Usage

let keychain = new SeaSalt_Keychain();
keychain.create_recovery('DpfXY1eKueSUXt4a', 'mygreatestpasswordyet')

//  returns an object like
{
     "code": "482559516047",
     "token": "cd052b20c5dabb034975baae5c17e2b65521c5a9971fac10022874b557653e2f..."
}

Providing an Alternate Secret Box

//  let's say we want to use the first key stored in the key history
let keychain = new SeaSalt_Keychain();
keychain.create_recovery('DpfXY1eKueSUXt4a', 'mygreatestpasswordyet', keychain.keys.DpfXY1eKueSUXt4a.history[0])

decrypt(ciphertext):string

Decrypt a provided ciphertext using the loaded key

Parameters
ciphertext:string

Ciphertext to decrypt

Returns
Type
:string

Returns the decrypted string

Example
let keychain = new SeaSalt_Keychain();
keychain.open('DpfXY1eKueSUXt4a', 'mygreatpassword33');
keychain.decrypt('1a0a8965c5995dbfbb9e32706805e0d7c3bcdd080107d8929cb36f358af25825eb82cab9e2ce38363ed6f3e999b785e1823185a8ce4272ac');

//  using the example from keychain.encrypt the result would be: My secret string

destroy(sigopt, historyopt, purgeopt)

Destroys a key signature by deleting it from the keychain

Parameters
sig:string|boolean (optional)

Key configuration signature to destroy (treated as history if boolean)

history:boolean (optional)

Whether or not to delete history data for this key configuration

purge:boolean (optional)

Whether or not to purge all key data storage

Examples

Basic Usage

//  destroy the key configuration but do not delete the history or storage data
let keychain = new SeaSalt_Keychain();
keychain.destroy('DpfXY1eKueSUXt4a');

Advanced Usage

//  destroy the key configuration, history and storage data
keychain.destroy('DpfXY1eKueSUXt4a', true, true);

//  destroy all key configurations and data
keychain.destroy(true, true);

encrypt(string):string

Encrypt a string using the loaded key configuration encryption key.

Encryption utilizes AEAD with nonces. The same string and key will never result in the same ciphertext twice.

Parameters
string:string

String to encrypt

Returns
Type
:string

Returns the encrypted ciphertext

Example
let keychain = new SeaSalt_Keychain();
keychain.open('DpfXY1eKueSUXt4a', 'mygreatpassword33');
keychain.encrypt('My secret string');

//  returns a string like: 1a0a8965c5995dbfbb9e32706805e0d7c3bcdd080107d8929cb36f358af25825eb82cab9e2ce38363ed6f3e999b785e1823185a8ce4272ac

error(message)

Forward an error and throw it

Parameters
message:string|exception

Error message to throw

export_key(sigopt, rawopt):string

Export the current sig, specified sig, or all sigs keychain data

Parameters
sig:string|boolean (optional)

Specific key signature, true for all, or none for currently opened key

raw:boolean = false (optional)

Return the config objects instead of JSON

Returns
Type
:string

Returns a JSON object of the requested key configuration data

Example
let keychain = new SeaSalt_Keychain();

//  export all key configurations
keychain.export_key();

//  export a specific key configuration
keychain.export_key('DpfXY1eKueSUXt4a');

find_recovery(sig, code, boxesopt):Array

Check supplied code against all known or provided secret boxes

Parameters
sig:string

Key configuration signature

code:string

Recovery code to attempt validating

boxes:string|Array (optional)

Box or boxes to try validating against

Returns
Type
:Array

Returns an array of all matching secret boxes

Examples

Basic Usage

let keychain = new SeaSalt_Keychain();
keychain.find_recovery('DpfXY1eKueSUXt4a', '482559516047');

Providing an Alternate Secret Box

//  in this case, let's say you are able to provide a copy of the recovery code and token
let keychain = new SeaSalt_Keychain();
keychain.find_recovery('DpfXY1eKueSUXt4a', '482559516047', 'cd052b20c5dabb034975baae5c17e2b65521c5a9971fac10022874b557653e2f...');

get_keysalt():string

Get the private keysalt for the currently open key configuration

Returns
Type
:string

Returns the found keysalt value

hash(string, saltopt, hashopt):string

Hash an input using the configured hashing algorithm with optional salt

Parameters
string:string

String to hash

salt:string = Empty (optional)

Salt to hash with

hash:string = config.hash (optional)

Hashing algorithm to use

Returns
Type
:string

Returns a hash of the input

import_key(data, sigopt, confirmopt, forceopt):Object

Imports a key configuration to the keychain

Parameters
data:string|Object

JSON object of the key configuration data

sig:string (optional)

Key configuration signature

confirm:boolean = false (optional)

Confirm overwrite of existing key configuration

force:boolean = false (optional)

Force use of custom key signature

Returns
Type
:Object

Returns an object with the key signature and save status

Example
let keychain = new SeaSalt_Keychain();

//  import the configuration and return a new key signature
keychain.import_key('{"format":0,"mode":1,"box":"158754f8531b75aa1a9f40bbdf45551ef8144c48224bed5aa1bc083a7ab2a8ed...');

//  import the configuration and overwrite an existing key signature
keychain.import_key('{"format":0,"mode":1,"box":"158754f8531b75aa1a9f40bbdf45551ef8144c48224bed5aa1bc083a7ab2a8ed...', 'DpfXY1eKueSUXt4a', true);

key(key):string

Converts a key to the encrypted-format key with format: prefix:key signature:type:format:mode:encrypted storage key

Example: seasalt:keychain:DpfXY1eKueSUXt4a:storage:0:1:a8c0b162cacd0cb76803da4c5eb0269e...

The encrypted storage key is a hash of the provided key and the private keysalt.

Parameters
key:string

Storage key name to modify

Returns
Type
:string

Returns a string containing the new storage key name

Example
let keychain = new SeaSalt_Keychain();
keychain.key('my storage key');

//  returns a string like: seasalt:keychain:DpfXY1eKueSUXt4a:storage:0:1:aaac25a086bc3a30063ff2fd9fb4e3df5a9fbca2da9bfa02e0d0d216d447c0c7...

keyconf(configopt):object

Returns a new key configuration (generally for creation and restoration of key configurations)

Parameters
config:Object (optional)

Keychain key configuration values

config.box:string (optional)

Secret box for the key

config.name:string (optional)

Key configuration name

config.salt:string (optional)

Key configuration public salt

config.mode:number = 1 (optional)

Storage key encryption mode

config.format:number = 0 (optional)

Key configuration format

config.history:Array = [] (optional)

Previous secret box strings

config.recovery:Object = {} (optional)

Recovery code objects

Returns
Type
:object
Example
let keychain = new SeaSalt_Keychain();

//  the keys name, box, and salt are required
keychain.keyconf({
   name: 'My config name',
   box: 'Secret box string',
   salt: 'Public salt'
});

listkeys():Array

Return a list of key configuration signatures

Returns
Type
:Array

lock(state, forceopt)

Change the lock state of the key signature in localStorage

Parameters
state:boolean

Lock (true) or unlock (false) the keychain

force:boolean = false (optional)

Force the action

log(…arg)

Forwards logging requests to the configured log handler

Parameters
arg:* (repeatable)

Arguments to forward to logging handler

modify(sigopt, configopt, confirmopt):boolean

Modify the key configuration with the provided user config.

Note - If modifying any restricted keys make sure to have a backup if you value your data if you're not sure what you are doing.

Parameters
sig:string|Object (optional)

Key signature to modify

config:Object|boolean (optional)

User's configuration modifications. See keychain.keyconf for a complete list of config keys.

Read-only Keys - The following keys cannot be modified with this tool:

  1. format
  2. history
  3. recovery

Restricted Keys - The following keys require confirmation to be modified with this tool:

  1. box
  2. mode
  3. salt
confirm:boolean (optional)

Confirmation to override restricted configuration keys

Returns
Type
:boolean

Returns true or false

open(sig, userPasswordopt, checkopt):boolean

Attempt to read and parse a keychain entry (or open the only existing key if sig omitted)

Parameters
sig:string

Key configuration signature

userPassword:string (optional)

Password for the key configuration

check:boolean = false (optional)

Whether or not to just check if the userPassword is valid

Returns
Type
:boolean

Returns true or false

Examples

Basic Usage

let keychain = new SeaSalt_Keychain();
keychain.open('DpfXY1eKueSUXt4a', 'mygreatpassword');

Usage with a Single Key Config

let keychain = new SeaSalt_Keychain();
keychain.open('mygreatpassword');

purge(sigopt, allopt, confirm, reverseopt, historyopt):object|undefined

Purge data from the keychain and storage

Parameters
sig:string (optional)

Key configuration signature

all:boolean = false (optional)

Whether or not to purge all data and not just storage data

confirm:boolean

Whether or not you confirm this command

reverse:boolean = false (optional)

Whether or not to erase all data that doesn't match key configuration signature

history:boolean = false (optional)

Whether or not to erase all associated history data

Returns
Type
:object|undefined

Returns an object listing all deleted key signatures and storage keys

Examples

Basic Usage

//  delete all storage data for specified key configuration
let keychain = new SeaSalt_Keychain();
keychain.purge('DpfXY1eKueSUXt4a', false, true);

Advanced Usage

//  delete storage and key configuration data
keychain.purge('DpfXY1eKueSUXt4a', true, true);

//  delete storage, key configuration, and history data
keychain.purge('DpfXY1eKueSUXt4a', true, true, false, true);

//  delete all storage data for keys other than the one specified
keychain.purge('DpfXY1eKueSUXt4a', false, true, true);

//  delete all data
keychain.purge(undefined, true, true);

read(key, metaopt):string|undefined

Read and decrypt a string from storage

Parameters
key:string

Storage key to read

meta:boolean = false (optional)

Include metadata

Returns
Type
:string|undefined

Returns the decryption result data

Example

Basic Usage

let keychain = new SeaSalt_Keychain('DpfXY1eKueSUXt4a', 'mygreatpassword33');
keychain.read('mystoragekey1');

//  using the example from keychain.write the result would be: Hello world

read_meta(key, nameopt):string|boolean

Read the file meta data for a provided storage key.

Parameters
key:string

Storage key to read. Can be either a plaintext key name or encrypted storage key name

name:string = all (optional)

Meta key to read

Returns
Type
:string|boolean

Returns the decrypted meta key data from the storage key or false on error

rekey(confirm):object|undefined

Create a new encryption key and rekeys associated objects in localStorage and recovery codes for currently opened key configuration.

Warning

This action renders all prior recovery and history keys unusable.

If a problem is encountered saving the new storage then their data could be lost.

If you value your data then definitely create a backup prior to executing this command.

Parameters
confirm:boolean = false

Whether or not to confirm the action

Returns
Type
:object|undefined

Returns an object containing the key signature and new recovery codes

restore(file)

Not yet implemented

Parameters
file:string

Restoration file contents

restore_recovery(sig, code, boxes, newPassword):boolean

Installs the provided secret box (or the first valid in an array) to the specified key and resets a new password

Parameters
sig:string

Key configuration signature

code:string

Recovery code to utilize

boxes:string|Array

Box or boxes to try recovering from

newPassword:string

New password for the key configuration

Returns
Type
:boolean

Returns true or false

Example
let keychain = new SeaSalt_Keychain();
let recoveryboxes = keychain.find_recovery('DpfXY1eKueSUXt4a', '482559516047');
keychain.restore_recovery('DpfXY1eKueSUXt4a', '482559516047', recoveryboxes, 'mysuperdupernewpassword');

save():boolean

Store the keychain to localStorage

Returns
Type
:boolean

True or false depending on save status

scan(userPassword, newPasswordopt):Object|boolean

Scans all keys and recovery keys testing if the password can open it.

If a single keychain key is matched it will be opened automatically.

If a single recovery key is matched and newPassword is provided it will be automatically restored.

Parameters
userPassword:string

Password or recovery code to use when scanning.

newPassword:string (optional)

Password to use when restoring a matched recovery code.

Returns
Type
:Object|boolean

Returns false if no results are found.

Returns the found signature if a single key signature is detected.

Returns the status of keychain.restore_recovery if a single recovery code is matched and newPassword is provided.

If multiple key signatures are found or if a recovery code matches with no newPassword then this will return an object of those matches.

store_history(sigopt, skipSaveopt):boolean

Stores the specified key signature or loaded key signature's secret box in the recovery chain.

Parameters
sig:string (optional)

Key configuration signature

skipSave:boolean = false (optional)

Skip automatic save

Returns
Type
:boolean

Returns true or false

update(newPasswordopt):boolean

Repackages currently opened secret box optionally with a new password

Parameters
newPassword:string (optional)

New password for the key configuration

Returns
Type
:boolean

Returns true or false

Examples

Repackage a secret box without changing the password

let keychain = new SeaSalt_Keychain();
keychain.open('DpfXY1eKueSUXt4a', 'mygreatpassword');
keychain.update();

Repackage a secret box and change the password

let keychain = new SeaSalt_Keychain();
keychain.open('DpfXY1eKueSUXt4a', 'mygreatpassword');
keychain.update('mysuperbetterpassword1');

write(key, value, metaopt):string|boolean

Encrypts and writes data to storage.

Storage key names are unique to each key configuration. Thus storage key names do not need to be unique between key configurations.

See keychain.key for information regarding the returned key format.

Parameters
key:string

Storage key to write

value:string

String to be encrypted and stored

meta:object (optional)

File metadata object

Returns
Type
:string|boolean

Returns the storage key name or false

Examples

Basic Usage

let keychain = new SeaSalt_Keychain('DpfXY1eKueSUXt4a', 'mygreatpassword33');
keychain.write('mystoragekey1', 'Hello world');

//  returns a string like: seasalt:keychain:DpfXY1eKueSUXt4a:0:44f3ef019ab2cd691a7d102cf8471d0d43f95d7c5582568885a4975c91a0ecb9...

Setting Metadata

keychain.write('mystoragekey1', '"{"mykey":true}"', {filetype: 'text/json'});