Keystore API - Description

The Keystore API is a library for working with DCP, the Distributed Compute Protocol, to perform operations related to Addresses, Keystores, and Wallets.

Record of Issue

Date Author Change
May 24 2019 Wes Garland Added DataTypes
May 23 2019 Wes Garland Initial Revision

Intended Audience

This document is intended for both the KDS DCP dev team and external developers writing DCP applications.

Definitions

Address

A unique identifier in DCP which can be used as a Bank Account identifier (account number), Address on the Ethereum network, or as an identifier for any other arbitrary entity (eg. a user, the bank, an administrator, etc.).

Wallet

A data structure which stores a (public) Address and a private key. The Wallet is used by one party to sign transactions over DCP in a way which allows the other party to know (within cryptographic certainty) that the first party has the private key, and hence is authorized to move funds associated with the Address.

Keystore

A data structure which stores an encrypted wallet. Generally speaking, the Wallet will be encrypted with a passphrase. It is possible to encrypt a Wallet with no passphrase.

Keystore File

A file which stores a JSON-encoded Keystore. This is the preferred method to private keys.

Core API

Introduction

DCP Addresses can be used as a Bank Account identifier (account number), an Address on the Ethereum network, or as an identifier for any other arbitrary entity (eg. a user, the bank, an administrator, etc.).

With the notable exception of Bank Accounts and Ethereum block chain requests using the same Address, it is considered very bad practice use the same Address to identify multiple entities. It is important to remember that

Addresses may be used to identify access realms; conceptually, we simply tie the access realm to the Bank Account (whether it really exists or not). For example, we might nominate a particular Address to represent “scheduler maintenance functions”, and require messages which are signed with the private key corresponding to the Address in order to access these functions.

DataTypes

Address

Addresses are represented by Strings which contain 20-byte hexadecimal numbers (no prefix). All comparisons between Addresses must be case-insensitive. An Address must never be programmatically altered, as the alteration could damage the case-encoded checksum, which will be used by future versions of the Keystore API.

Keystore

Keystores are stored in Keystore objects. These objects are instanceof require('keystore').Keystore. There is currently no guarantee that the Keystore objects’ constructor is also require('keystore').Keystore.

Wallet

Wallets are stored in Wallet objects. These objects are instanceof require('keystore').Wallet. There is currently no guarantee that the Keystore objects’ constructor is also require('keystore').Wallet.

User Interface

passphrasePrompt

keystore.passphrasePrompt(string)
This function accepts a string, which is displayed to the user, and returns a string which was entered by the user.

In NodeJS applications, this function solicits input from the standard input, and does not display what the user is typing.

In browser applications, this function solicits input from the active browser window, using an <INPUT type="password"> element.

The keystore module will always use its passphrasePrompt export internally when it needs a passphrase, for example, to unlock a Keystore object. This means that developers can monkey-patch the module as necessary to produce a user experience which is consistent with their application.

Keystore and Wallet Management

NodeJS Platforms

Keystore Files are loaded, by default, from the .dcp directory in the user’s home directory. They are stored in files ending in .keystore. The first part of the filename corresponds, by default, to the name assigned to the Keystore in the DCP Web Portal.

Web Platforms

Keystore Files can be either files on the user’s local filesystem, browser local storage, or within the Distributed Computer’s Wallet Manager.

Unified Platform

The next version of this specification will allow NodeJS Platforms to use the Distributed Computer’s Wallet Manager.

Unified and Web Platform implementation are currently out-of-scope – wait for later version of spec

loadSync

This function accepts a Wallet name or filename, reads the contents of the associated file, and returns an object, which has the following properties:
keystore - an object corresponding to the Keystore that was loaded
safe - a boolean flag which, when true, indicates that the keystore was read from a secure location.

form 1: keystore.loadSync(filename) - NodeJS only

filename must begin with path.sep, '.' + path.sep, or '..' + path.sep.

The safe flag is set to true to indicate that the Keystore file was stored safely; this happens if and only if the following conditions are true:

  1. The containing directory and all its ancestors, up to and including the root directory, is not world-writable
  2. The Keystore File is neither world-writable nor world-readable
form 2: keystore.loadSync(name, options)

This form creates a Keystore File filename based on its arguments, performing the operations described in form 1 once the filename has been determined.

name must not begin with path.sep, '.' + path.sep, nor '..' + path.sep. If a user creates a Wallet that happens to be named like this, they will either need to rename the file, or specify the full pathname, invoking form 1

The options object can be used to override the default filename and directory for the Keystore File.

option       behaviour
name override the default Wallet name, normally “default”
dir override default Wallet directory, normally the “.dcp” directory in the home directory belonging to the user invoking the program (NodeJS only)

unlock

This function accepts a Keystore object and returns a Wallet object. If it cannot return a Wallet object, it will throw an Error.

form 1: keystore.unlock(keystore)
This form accepts a Keystore object, and returns a wallet. The initial attempt to decrypt the Keystore will be to try empty passphrase; if that fails, the user will be prompted for the passphrase via keystore.passphasePrompt.
form 2: keystore.unlock(keystore, options)
This form accepts both a Keystore object and an options object. It behaves exactly the same as form 1, except where the options object is used to override behaviour. The options object currently supports the following properties:
  • checkEmpty: when this property is falsey, there will be no initial attempted to decrypt the Keystore with the empty passphrase.

getWallet

This function examines parameters and accepts options relating to its environment, uses this information to select a Keystore file, load it via keystore.loadSync, and return a Wallet object. If it cannot return a Wallet object, this function will throw an Error.

form 1: keystore.getWallet(options)

This form loads a Keystore form a Keystore File via keystore.loadSync and returns a Wallet object. The options object is used to override default parameters which help to identify and locate the Keystore file.

option       behaviour
name override the default Wallet name, normally “default”
dir override default Wallet directory, normally the “.dcp” directory in the home directory belonging to the user invoking the program (NodeJS only)
checkEmpty When specified (is an ownProperty), this value is passed as the checkEmpty option value when keystore.unlock is invoked.

Tip: the options objects for getWallet(), loadSync(), and unlock() are compatible.

The Wallet will be extracted using the keystore.unlock function. If loadSync does not indicate that the Wallet was stored safely, the empty passphrase will not be tried during the call to unlock. This behaviour is to ensure that end users do not accidentally leave Keystore Files with empty passphrases lying around in their filesystems with world-readable permissions.

form 2: keystore.getWallet()

This form is equivalent to keystore.getWallet({}).

form 3: keystore.getWallet(string)

This form is equivalent to keystore.getWallet({name: string})

form 4: keystore.getWallet(array)

This form accepts an Array-like object, formatted like process.argv, to specify options for invoking form 1. Its purpose is to unify Keystore, Wallet, and Address-related options for programs written in NodeJS.

argument       behaviour
–private-key Generate a wallet corresponding to the specified private key, ignoring all other considerations
-i name Override the name option
-p Set the checkEmpty option to false
form 5: keystore.getWallet(array, options)

This form accepts an Array-like object, as described in form 4, and an options object as described in form 1. Its behaviour is to generate an options object from the array, merge the passed options object into this options object, and invoke form 1.

To be clear, the options argument has a higher precedence than the array argument.

form 6: keystore.getWallet(array, string)

This form is equivalent to keystore.getWallet(array, {name: string})

generateWallet

This function generates Wallet objects.

form 1: keystore.generateWallet()
This form accepts no arguments and returns a wallet that corresponds to a randomly-selected private key.
form 2: keystore.generateWallet(privateKey)
This form accepts, as its sole argument, a private key and returns a wallet that corresponds to that private key.

Future API / Functionality