# ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) Keystore

**Authors(s):**
- [whyrusleeping](github.com/whyrusleeping)
- [Hector Sanjuan](github.com/hsanjuan)

**Abstract**

This spec provides definitions and operations for the keystore feature in IPFS.

# Table of Contents

- [Goals](#goals)
- [Planned Implementation](#planned-implementation)
  - [Key storage](#key-storage)
  - [Interface](#interface)
  - [Code changes and additions](#code-changes-and-additions)
  - [Structures](#structures)

## Goals

To have a secure, simple and user-friendly way of storing and managing keys
for use by ipfs. As well as the ability to share these keys, encrypt, decrypt,
sign and verify data.

## Planned Implementation

### Key storage

Storage layout and format is defined in the [`repository_fs`](repository_fs.md) part of the spec.

### Interface

#### ipfs key

```
USAGE
  ipfs key - Create and list IPNS name keypairs

  ipfs key

  'ipfs key gen' generates a new keypair for usage with IPNS and 'ipfs name
  publish'.
  
    > ipfs key gen --type=rsa --size=2048 mykey
    > ipfs name publish --key=mykey QmSomeHash
  
  'ipfs key list' lists the available keys.
  
    > ipfs key list
    self
    mykey
  		

SUBCOMMANDS
  ipfs key export <name>           - Export a keypair
  ipfs key gen <name>              - Create a new keypair
  ipfs key import <name> <key>     - Import a key and prints imported key id
  ipfs key list                    - List all local keypairs.
  ipfs key rename <name> <newName> - Rename a keypair.
  ipfs key rm <name>...            - Remove a keypair.
  ipfs key rotate                  - Rotates the IPFS identity.

  For more information about each command, use:
  'ipfs key <subcmd> --help'
```

#### ipfs crypt

**NOTE:** as of 2023 Q4, `ipfs crypt` commands are not implemented yet.

```
    ipfs crypt - Perform cryptographic operations using ipfs keypairs

SUBCOMMANDS:

    ipfs crypt sign <data>          - Generates a signature for the given data with a specified key
    ipfs crypt verify <data> <sig>  - Verify that the given data and signature match
    ipfs crypt encrypt <data>       - Encrypt the given data
    ipfs crypt decrypt <data>       - Decrypt the given data

DESCRIPTION:

    `ipfs crypt` is a command used to perform various cryptographic operations
    using ipfs keypairs, including: signing, verifying, encrypting and decrypting.
```

#### Some subcommands:

##### ipfs key Gen


```
USAGE
  ipfs key gen <name> - Create a new keypair

SYNOPSIS
  ipfs key gen [--type=<type> | -t] [--size=<size> | -s]
               [--ipns-base=<ipns-base>] [--] <name>

ARGUMENTS

  <name> - name of key to create

OPTIONS

  -t, --type   string - type of the key to create: rsa, ed25519. Default:
                        ed25519.
  -s, --size   int    - size of the key to generate.
  --ipns-base  string - Encoding used for keys: Can either be a multibase
                        encoded CID or a base58btc encoded multihash. Takes
                        {b58mh|base36|k|base32|b...}. Default: base36.
```

* * *

##### Key Send

```
USAGE
  ipfs key - Create and list IPNS name keypairs

SYNOPSIS
  ipfs key

DESCRIPTION

  'ipfs key gen' generates a new keypair for usage with IPNS and 'ipfs name
  publish'.
  
    > ipfs key gen --type=rsa --size=2048 mykey
    > ipfs name publish --key=mykey QmSomeHash
  
  'ipfs key list' lists the available keys.
  
    > ipfs key list
    self
    mykey
  		

SUBCOMMANDS
  ipfs key export <name>           - Export a keypair
  ipfs key gen <name>              - Create a new keypair
  ipfs key import <name> <key>     - Import a key and prints imported key id
  ipfs key list                    - List all local keypairs.
  ipfs key rename <name> <newName> - Rename a keypair.
  ipfs key rm <name>...            - Remove a keypair.
  ipfs key rotate                  - Rotates the IPFS identity.

  For more information about each command, use:
  'ipfs key <subcmd> --help'
```

##### Comments:

Ensure that the user knows the implications of sending a key.

* * *

##### Crypt Encrypt

```
    ipfs crypt encrypt <data> - Encrypt the given data with a specified key

ARGUMENTS:

    data                        - The filename of the data to be encrypted ("-" for stdin)

OPTIONS:

    -k, -key        string        - The name of the key to use for encryption (default: localkey)
    -o, -output     string        - The name of the output file (default: stdout)
    -c, -cipher     string        - The cipher to use for the operation
    -m, -mode       string        - The block cipher mode to use for the operation

DESCRIPTION:

    'ipfs crypt encrypt' is a command used to encypt data so that only holders of a certain
    key can read it.
```

##### Comments:

This should probably just operate on raw data and not on DAGs.

* * *

##### Other Interface Changes

We will also need to make additions to support keys in other commands, these changes are as follows:

- `ipfs add`
    - Support for a `-encrypt-key` option, for block encrypting the file being added with the key
        - also adds an 'encrypted' node above the root unixfs node
    - Support for a `-sign-key` option to attach a signature node above the root unixfs node

- `ipfs block put`
    - Support for a `-encrypt-key` option, for encrypting the block before hashing and storing

- `ipfs object put`
    - Support for a `-encrypt-key` option, for encrypting the object before hashing and storing

- `ipfs name publish`
    - Support for a `-key` option to select which keyspace to publish to

### Code changes and additions

This sections outlines code organization around this feature.

#### Keystore package

The fsrepo carries a `keystore` that can be used to load/store keys. The keystore is implemented following this interface:

```go
// Keystore provides a key management interface
type Keystore interface {
	// Has returns whether or not a key exist in the Keystore
	Has(string) (bool, error)
	// Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists
	Put(string, ci.PrivKey) error
	// Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey
	// otherwise.
	Get(string) (ci.PrivKey, error)
	// Delete removes a key from the Keystore
	Delete(string) error
	// List returns a list of key identifier
	List() ([]string, error)
}
```

Note: Never store passwords as strings, strings cannot be zeroed out after they are used.
using a byte array allows you to write zeroes over the memory so that the users password
does not linger in memory.

#### Unixfs

- new node types, 'encrypted' and 'signed', probably shouldn't be in unixfs, just understood by it
- if new node types are not unixfs nodes, special consideration must be given to the interop

- DagReader needs to be able to access keystore to seamlessly stream encrypted data we have keys for
    - also needs to be able to verify signatures

#### Importer

- DagBuilderHelper needs to be able to encrypt blocks
    - Dag Nodes should be generated like normal, then encrypted, and their parents should
        link to the hash of the encrypted node
- DagBuilderParams should have extra parameters to accommodate creating a DBH that encrypts the blocks

#### New 'Encrypt' package

Should contain code for crypto operations on dags.

Encryption of dags should work by first generating a symmetric key, and using
that key to encrypt all the data. That key should then be encrypted with the
public key chosen and stored in the Encrypted DAG structure.

Note: One option is to simply add it to the key interface.

### Structures
Some tentative mockups (in json) of the new DAG structures for signing and encrypting

Signed DAG:
```
{
    "Links" : [
        {
            "Name":"@content",
            "Hash":"QmTheContent",
        }
    ],
    "Data": protobuf{
        "Type":"Signed DAG",
        "Signature": "thesignature",
        "PubKeyID": "QmPubKeyHash",
    }
}
```

Encrypted DAG:
```
{
    "Links" : [
        {
            "Name":"@content",
            "Hash":"QmRawEncryptedDag",
        }
    ],
    "Data": protobuf{
        "Type":"Encrypted DAG",
        "PubKeyID": "QmPubKeyHash",
        "Key": "ephemeral symmetric key, encrypted with public key",
    }
}
```
