diff --git a/README.md b/README.md
index 4401ef3..cd2ce6b 100644
--- a/README.md
+++ b/README.md
@@ -70,55 +70,101 @@ At this point Red October should be serving an example webapp. Access it using y
The server exposes several JSON API endpoints. JSON of the prescribed
format is POSTed and JSON is returned.
- - `/create`: Create the first admin account.
- - `/delegate`: Delegate a password to Red October
- - `/create-user`: Create a user
- - `/modify`: Modify permissions
- - `/encrypt`: Encrypt
- - `/decrypt`: Decrypt
- - `/owners`: List owners of an encrypted secret.
- - `/summary`: Display summary of the delegates
- - `/password`: Change password
- - `/index`: Optionally, the server can host a static HTML file.
+| Path | Summary |
+| ---- | ------- |
+| [`/create`](#create) | Create the first admin account |
+| [`/create-user`](#create-user) | Create a user |
+| [`/summary`](#summary) | Display summary of the delegated keys and Red October users |
+| [`/delegate`](#delegate) | Delegate a key to Red October |
+| [`/purge`](#purge) | Delete all delegated keys |
+| [`/password`](#password) | Change password for the authenticating user |
+| [`/encrypt`](#encrypt) | Encrypt provided data with specified owners and predicates |
+| [`/re-encrypt`](#re-encrypt) | Change encryption parameters of already encrypted data (delegation requirements must be met) |
+| [`/decrypt`](#decrypt) | Decrypt provided data assuming necesary delegation requirements have been met |
+| [`/ssh-sign-with`](#ssh-sign-with) | Sign data as an SSH oracle without disclosing the SSH private key (delegation requirements must be met) |
+| [`/owners`](#owners) | List owners (those who can delegate to allow decryption) of a provided encrypted secret |
+| [`/modify`](#modify) | Modify an existing user (`delete`, set `admin` flag, `revoke` admin flag) |
+| [`/export`](#export) | Exports the internal vault contained encrypted user private keys, hashed passwords, public keys and other RO internal data |
+| [`/order`](#order) | Adds an `Order` request to delegate credentials with specific parameters requested |
+| [`/orderout`](#orders-outstanding) | Returns a list of `Order` structures for all outstanding orders |
+| [`/orderinfo`](#order-information) | Returns the `Order` structure for a specified `OrderNum` |
+| [`/ordercancel`](#order-cancel) | Cancel the `Order` with the specified `OrderNum` |
+| [`/restore`](#restore) | Restore delegations from a persisted state (if configured). Operates like a `/delegate` call |
+| [`/reset-persisted`](#reset-persisted) | Deletes all delegations from the persisted state (if configured) |
+| [`/status`](#status) | Returns the status of the persistent store of delegated keys (if configured) |
+| [`/index`](#web-interface) | Optionally, the server can host a static HTML file |
### Create
-Create is the necessary first call to a new vault. It creates an
-admin account.
+Create is the necessary first call to a new vault. It creates an admin account.
-Example query:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| No | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password"
+}
+```
+- `Name` must start with an alphnumeric character and then can contain any alphanumeric character, '-', or '_' after the first character (required)
+- `Password` must be at least one character long (required)
+
+#### Response:
+```json
+{
+ "Status": "ok"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+#### Assumptions:
+- This API call can **only** be called on an uninitialized vault and will fail on any call after the first user is created.
+- The user created with this call is an **Admin** account.
+- This user will use the `passvault.DefaultRecordType`, which is RSA.
+
+#### Example query:
$ curl --cacert cert/server.crt https://localhost:8080/create \
-d '{"Name":"Alice","Password":"Lewis"}'
{"Status":"ok"}
-### Delegate
-
-Delegate allows a user to delegate their decryption password to the
-server for a fixed period of time and for a fixed number of
-decryptions. If the user's account is not created, it creates it.
-Any new delegation overrides the previous delegation.
-
-Example query:
-
- $ curl --cacert cert/server.crt https://localhost:8080/delegate \
- -d '{"Name":"Bill","Password":"Lizard","Time":"2h34m","Uses":3}'
- {"Status":"ok"}
- $ curl --cacert cert/server.crt https://localhost:8080/delegate \
- -d '{"Name":"Cat","Password":"Cheshire","Time":"2h34m","Uses":3}'
- {"Status":"ok"}
- $ curl --cacert cert/server.crt https://localhost:8080/delegate \
- -d '{"Name":"Dodo","Password":"Dodgson","Time":"2h34m","Uses":3}'
- {"Status":"ok"}
-
### Create User
-Create Users creates a new user account. Allows an optional "UserType"
-to be specified which controls how the record is encrypted. This can have
-a value of either "RSA" or "ECC" and if none is provided will default to
-"RSA".
+Create User creates a new user account.
-Example query:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| No | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "UserType": "ECC",
+ "HipchatName": ""
+}
+```
+- `Name` must be unique within the RedOctober vault (required)
+- `Password` must be at least one character long (required)
+- `UserType` can be `"RSA"` or `"ECC"` (optional, will default to `"RSA"`)
+- `HipchatName` specifies the HipChat username for `Order` notifications if configured (optional)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+#### Assumptions:
+- Anyone who can access the API, can register a user with this API call.
+
+#### Example query:
$ curl --cacert cert/server.crt https://localhost:8080/create-user \
-d '{"Name":"Bill","Password":"Lizard","UserType":"ECC"}'
@@ -130,7 +176,72 @@ Summary provides a list of the users with keys on the system, and a
list of users who have currently delegated their key to the
server.
-Example query:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "State": "",
+ "Live": {
+ "User1": {
+ "Uses": 1,
+ "Labels": ["", ""],
+ "Users": ["", ""],
+ "Expiry": "",
+ "AltNames": {
+ "key1": "value1",
+ "key2": "value2"
+ },
+ "Admin": true,
+ "Type": "RSA"
+ },
+ "User1-slot1": {
+ "Uses": 1,
+ "Labels": ["", ""],
+ "Users": ["", ""],
+ "Expiry": "",
+ "AltNames": {
+ "key1": "value1",
+ "key2": "value2"
+ },
+ "Admin": true,
+ "Type": "ECC"
+ },
+ },
+ "All": {
+ "User1": {
+ "Admin": true,
+ "Type": "RSA"
+ }
+ }
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `State` could be `active`, `inactive`, or `disabled` and is the status of the persisted keycache (delegated credentials)
+- `Live` is a map of active delegations
+ - The key is a combination of the username and a slot string (if provided on delegation)
+ - The value is an object with details about the user and the specific delegation
+- `All` is a map of users with keys in Red October
+ - The key of the map is the username
+ - The value is a object that says if the user is an `Admin` and if a "RSA or "ECC" key
+ is used (`Type`)
+
+#### Assumptions:
+- None
+
+#### Example query:
$ curl --cacert cert/server.crt https://localhost:8080/summary \
-d '{"Name":"Alice","Password":"Lewis"}'
@@ -157,6 +268,150 @@ Example query:
}
}
+
+### Delegate
+
+Delegate allows a user to delegate their decryption password to the
+server for a fixed period of time and for a fixed number of
+decryptions. If the user's account is not created, it creates it.
+Any new delegation overrides the previous delegation.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes* | No |
+
+*See the first assumption for this API call
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "Uses": 1,
+ "Time": "1h10m5s",
+ "Slot": "",
+ "Users": ["User2", "User3"],
+ "Labels": ["", ""]
+}
+```
+- `Name` and `Password` are the authentication fields for this request* (required)
+- `Uses` is the number of times the delegated credentials can be used for `decryption` or otherwise and must be >= 1 (required)
+- `Time` is a duration the delegation is active and must parse with Go's `time.ParseDuration()` (required)
+- `Slot` is a string that can be used to allow multiple delegations. This value is passed to the `keycache.Cache`,
+ which stores delegated user credentails, when a user delegates their credentials. If a user wanted to delegate multiple times with different restrictions, such as allowing one user delegated credentials for `ssh-sign-with` and another user for `decrypt`,
+ then they should submit different values for `Slot` during each `/delegate` request. (optional)
+- `Users` is a list of users that can use this delegation. Values must match the `Name` field used at creation of the user. (optional)
+- `Labels` are strings used to match the delegation to whether a encrypted secret can be decrypted. If labels were used in
+ the encryption then at least one label must match in the delegation from both users. (optional)
+
+*See the first assumption for this API call
+
+#### Response:
+```json
+{
+ "Status": "ok"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+
+#### Assumptions:
+- This API call will create a user if no user matching the `User` field is found. In that case, the same `User` and `Password` validations as `/create` and `/create-user` are followed.
+- `Slot` allows for multiple delegations, but is not a validation of purpose. If two delegations are identical, but with
+ different `Slot` values, either could be used assuming:
+ - `Uses` has not reached `0`
+ - `Time` has not expired
+ - `Users` contains the user attempting to utilize the delegation
+- `Labels` is for the `Order` component of RO only and is not checked when attempting to utilize a delegation
+
+#### Example query:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/delegate \
+ -d '{"Name":"Bill","Password":"Lizard","Time":"2h34m","Uses":3}'
+ {"Status":"ok"}
+ $ curl --cacert cert/server.crt https://localhost:8080/delegate \
+ -d '{"Name":"Cat","Password":"Cheshire","Time":"2h34m","Uses":3}'
+ {"Status":"ok"}
+ $ curl --cacert cert/server.crt https://localhost:8080/delegate \
+ -d '{"Name":"Dodo","Password":"Dodgson","Time":"2h34m","Uses":3}'
+ {"Status":"ok"}
+
+
+### Purge
+
+Purge deletes all delegated keys in Red October.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | Yes |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+
+#### Response:
+```json
+{
+ "Status": "ok"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+#### Assumptions:
+- None
+
+#### Example input JSON format:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/purge \
+ -d '{"Name":"Alice","Password":"Lewis"}'
+ {"Status":"ok"}
+
+
+### Password
+
+Password allows a user to change their password. This password change
+does not require the previously encrypted files to be re-encrypted.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "NewPassword": "User1Password!NEW",
+ "HipchatName": ""
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `NewPassword` will replace the current password and must be at least 1 character long (required)
+- `HipchatName` is used for the internal HipChat ordering system (optional)
+
+#### Response:
+```json
+{
+ "Status": "ok"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+#### Assumptions:
+- None
+
+### Example Input JSON format:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/password \
+ -d '{"Name":"Bill","Password":"Lizard", "NewPassword": "theLizard"}'
+ {"Status":"ok"}
+
### Encrypt
Encrypt allows a user to encrypt a piece of data. A list of valid
@@ -165,7 +420,125 @@ decrypt. The returned data can be decrypted as long as "Minimum"
number users from the set of "Owners" have delegated their keys to the
server.
-Example query:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password",
+ "Minimum": 2,
+ "Owners": ["User4", "User3", "Users2"],
+ "LeftOwners": ["", ""],
+ "RightOwners": ["", ""],
+ "Predicate": "",
+ "Data": "V2h5IGlzIGEgcmF2ZW4gbGlrZSBhIHdyaXRpbmcgZGVzaz8K",
+ "Labels": ["", ""],
+ "Usages": ["", ""]
+}
+```
+- `Name` and `Password` authenticate the request (required)
+- `Minimum` is the number of delegations required to decrypt. Should only be 1 or 2 unless using `LeftOwners` and `RightOwners` or
+ a `Predicate` string. (optional)
+- `Owners` is a list of users that if delegated allows decryption of the secret. Need at least `Minimum` users. (required1)
+- `LeftOwners` and `RightOwners` are list of users that require delegation of at least one left
+ and one right (required1)
+- `Predicate` is a string that maps to an arbitrary monotone access structure. Please see [MSP Package](https://github.com/cloudflare/redoctober/tree/master/msp) (required1)
+- `Data` is the base64 encoded data to be encrypted (required)
+- `Labels` are strings that will mark if a decryption can occure based on matching labels in the delegation. If `Labels` is not
+ empty, then at least one value of `Labels` in the `/delegate` request must match at least one value in this request's `Labels`. (optional)
+- `Usages` are strings that define how the secret can be used. Such as if it can be returned by `/decrypt` ["`decrypt`"] or used
+ to with `/ssh-sign-with` [`"ssh-sign-with"`] (optional)
+
+1 Either `Owners`, `LeftOwners` and `RightOwners`, or `Predicate` is required. If one of the three is used, the other two should not be provided in the request.
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "nWY13Rx8d7Tov0AFGu...Hok3DlNnDs8FcrU5/PrxuExq"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` is the base64 encoded JSON object
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "Version": -1,
+ "Data": "c5UHBZ5KYk9K4WIf94Os/n6gtm...hu2aM1+yQO0iwhLC",
+ "Signature": "Hsd88NiDaA9Ech2uHzDjLA=="
+}
+```
+- `Version` is always -1 as it is used when HMAC-ing the encrypted data
+- `Data` is a base64 encoded JSON object
+- `Signature` is the HMAC signature of `Data`
+
+If you base64 decode `Data`, you will get:
+```json
+{
+ "Version": 1,
+ "VaultId": 12905318,
+ "Labels": ["", ""],
+ "Usages": ["", ""],
+ "Predicate": "",
+ "KeySet": [
+ {
+ "Name": ["User1", "User2"],
+ "Key": "OnfKLlXjk0swCtdc3/GPcQ=="
+ },
+ {
+ "Name": ["User2", "User3"],
+ "Key": "BRJ1ZtL0IS1g6fuPAgKkGA=="
+ },
+ ...
+ ],
+ "KeySetRSA": [
+ "User1": {
+ "Key": "ZTXz2KehhQ+02umuhSK9pmv3q155FW6BtqDUctz1k0NOI9e9WrL+vg=="
+ },
+ "User2": {
+ "Key": "R87vNqb7GhxZ8Bl+hd2osnQWVmgs68KmQ8LqoXg/3M3dvfyPBxyujw=="
+ },
+ "User3": {
+ "Key": "uocoMcwt00sbMM2aqUhKnDdwcyWfj8AxZjUGib8pDSBYl2XfU4gM/A=="
+ },
+ ...
+ ],
+ "ShareSet": [
+ "User1": ["5tJHlh2P+x9pNwOrjQsqxJMuTN9PdRqiFJ...OFYw/0="],
+ "User2": ["Hg06yJnUUAjerytV6/iHr/7...bxUB/M8U7FpYDzw="],
+ ...
+ ],
+ "IV": "xyjda++X7+8wQ0VH6Dnt/w==",
+ "Data": "k9kezvCRTe6x/Fl8pVBxb...Wup5ESrQw553IxIRbY=",
+ "Signature": "uUXOkuCAbGUSO4u81/ampQ=="
+}
+```
+- `Version` is the version number of the `EncryptedData` structure
+- `VaultId` will be a random 32 bit, non-negative integer identifying the vault
+- `Labels` are the same as the request `Labels`
+- `Usages` are the same as the request `Usages`
+- `Predicate` are the same as the request `Predicate`
+- `KeySet` is an array of objects with a `Name` and `Key` attribute. Each object in the array is a encrypted value using
+ intermediary keys from two users. All validate two user key delegations are stored in this array to allow decryption.
+ - `Name` is an array of two users that were used to encrypt the `Data` encryption key
+ - `Key` is a base64 encoded byte array that represents a doubly encrypted key, that was used to encrypt `Data`
+- `KeySetRSA` is an map where the key is a user and the value is an object with one attribute `Key`.
+ - `Key` is the base64 encoded byte array that represents the intermediary key encrypted with a users RSA or ECC public key.
+ Once decrypted, the intermediary key for two users can be used to decrypt the `Data` encryption key stored in the `KeySet` map.
+- `ShareSet` is a map of base64 encoded byte strings representing the distributed shares generated from the requested `Predicate`
+ - The map key is the user and the value is that users portion of the share
+- `IV` is the AES initial vector used
+- `Signature` is the internal HMAC signature of this whole structure
+
+#### Assumptions:
+- `KeySet` and `KeySetRSA` will always be populated
+- `ShareSet` will only be populated when using `Predicate`
+
+#### Example query:
$ echo "Why is a raven like a writing desk?" | openssl base64
V2h5IGlzIGEgcmF2ZW4gbGlrZSBhIHdyaXRpbmcgZGVzaz8K
@@ -183,6 +556,69 @@ Example query with a predicate:
The data expansion is not tied to the size of the input.
+
+### Re-Encrypt
+
+Re-encrypt allows for modification of encrypted data, without having to call Decrypt and then Encrypt, thus exposing the
+secret to the caller. Enough delegation to Decrypt are required for this call to run. A call is very similar to except
+instead of base64 encoded data in the `Data` field, provide the `Response` value from a previous `/encrypt` call.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password",
+ "Minimum": 2,
+ "Owners": ["User4", "User3", "Users2", "User5"],
+ "LeftOwners": ["", ""],
+ "RightOwners": ["", ""],
+ "Predicate": "",
+ "Data": "nWY13Rx8d7Tov.../PrxuExq",
+ "Labels": ["", ""],
+ "Usages": ["", ""],
+}
+```
+- `Name` and `Password` authenticate the request (required)
+- `Data` is the base64 encoded response from `/encrypt` (required)
+- All other fields work similar to a `/encrypt` call and will replace the original calls values
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "QIsDoh/jp3sky...JfOTePLRAMp7k="
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` is the base64 encoded JSON object
+- The internal structure of `Response` is identical to the response of an `/encrypt` call
+
+#### Assumptions:
+- Necessary delegations to decrypt the provided encrypted value should already be delegated.
+
+#### Example query:
+
+ $ echo "Why is a raven like a writing desk?" | openssl base64
+ V2h5IGlzIGEgcmF2ZW4gbGlrZSBhIHdyaXRpbmcgZGVzaz8K
+
+ $ curl --cacert cert/server.crt https://localhost:8080/re-encrypt \
+ -d '{"Name":"Alice","Password":"Lewis","Minimum":2, "Owners":["Alice","Bill","Cat","Dodo","Greg"],"Data":"eyJWZXJzaW9uIj...NSSllzPSJ9"}'
+ {"Status":"ok","Response":"J6rv0zlJ7l8stG6Oz...lQu8gXKSoIR6U="}
+
+Example query with a predicate:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/re-encrypt \
+ -d '{"Name":"Alice","Password":"Lewis","Predicate":"Alice & (Bob | Carl | Dodo)",
+ Data":"eyJWZXJzaW9uIj...NSSllzPSJ9"}'
+ {"Status":"ok","Response":"J6rv0zlJ7l8stG6Oz...lQu8gXKSoIR6U="}
+
+The data expansion is not tied to the size of the input.
+
+
### Decrypt
Decrypt allows a user to decrypt a piece of data. As long as
@@ -190,7 +626,38 @@ Decrypt allows a user to decrypt a piece of data. As long as
keys to the server, a base64 encoded object with the clear data and the
set of "Owners" whose private keys were used is returned.
-Example query:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password",
+ "Data": "nWY13Rx8.../PrxuExq",
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `Data` is the encrypted secret returned be a call to `/encrypt` (required)
+
+#### Response:
+```json
+{
+ "Data": "V2h5IGlzIGEgcmF2ZW4gbGlrZSBhIHdyaXRpbmcgZGVzaz8K",
+ "Secure": true,
+ "Delegates": ["User2", "User3"]
+}
+```
+- `Data` is the response from an encrypt call to be decrypted
+- `Secure` this is a flag that states if the payload was properly HMAC protected
+- `Delegates` notes which user's delegated keys were used to decrypt this blob
+
+#### Assumptions:
+- None
+
+#### Example query:
$ curl --cacert cert/server.crt https://localhost:8080/decrypt \
-d '{"Name":"Alice","Password":"Lewis","Data":"eyJWZXJzaW9uIj...NSSllzPSJ9"}'
@@ -200,27 +667,107 @@ If there aren't enough keys delegated you'll see:
{"Status":"need more delegated keys"}
+
+### SSH Sign With
+
+SSHSignWith signs a message with an SSH key previously encrypted with Red October and with a "Usages" containing `ssh-sign-with`
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "Data": "W2Y4QfAd5pf+sqFkvEzEm...p8/Zbo21YWzCNuStzyXfUcBk=",
+ "TBSData": "fjELOIwcZsloJY...PPIutXm7fBLJHOJwQ8ht+8Mo="
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `Data` is the encrypted SSH private key (required)
+- `TBSData` is the SSH message to be signed by the SSH private key (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "rePs8L+l7xtRY50uuuFZ4As4UQZWgzfLd...q808VFaHZNJ9AIfK0vZ9c="
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+If you base64 decode the Response, you will get:
+```json
+{
+ "SignatureFormat": "ssh-rsa-cert-v01@openssh.com",
+ "Signature": "M02dHSCbE/35H8RrxZmHAA==",
+ "Secure": true, # boolean
+ "Delegates": ["User1", "User2"]
+}
+```
+- `SignatureFormat` is the SSH signature type
+- `Signature` is a base64 encoded byte array, which is the signature of the SSH signing operation
+- `Secure` whether the HMAC validated or not
+- `Delegates` are the user keys used to temporary decrypt the SSH key for signing
+
+#### Assumptions:
+- If a `Usages` value of `ssh-sign-with` was not provided, this function will not work
+- If a `Usages` value of `decrypt` is not provided with `ssh-sign-with`, then the SSH key cannot be decrypted via
+ a `/decrypt` call
+ - Note that while not providing `decrypt` avoids `/decrypt` returning the unencrypted SSH private key, `/re-encrypt` could
+ be used to change that with the necessary delegation.
+
+#### Example query:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/ssh-sign-with \
+ -d '{"Name":"Alice","Password":"Lewis","Data":"eyJWZXJzaW9uIj...NSSllzPSJ9","TBSData":"eyJEYXRhI...FuMiJdfQ=="}'
+ {"Status":"ok","Response":"rePs8L+l7xtRY50uuuFZ4As4UQZWg...FaHZNJ9AIfK0vZ9c="}
+
+
### Owners
Owners allows users to determine which delegations are needed to decrypt
a piece of data.
-Example query:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| No | No |
+
+
+#### Request:
+```json
+{
+ "Data": "V2h5IGlzIGEgcmF2ZW4gbGlrZSB...hIHdyaXRpbmcgZGVzaz8K=="
+}
+```
+- `Data` is the response from an encrypt call to be decrypted (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Owners": ["User1", "User2", "User3"],
+ "Labels": ["", ""],
+ "Predicate": ""
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Owners` will be all the users that were noted in `Owners`, `LeftOwners`, `RightOwners`, or within the `Predicate`
+- `Labels` will match any that were provided in the `/encrypt` call or not be present if empty
+- `Predicate` will be the value provied to `/encrypt` or not present if not used in the encryption
+
+#### Assumptions:
+- Anyone with access to an encrypted secret can look this data up in the JSON structure anyway, so it is not an authenticated request
+
+#### Example query:
$ curl --cacert cert/server.crt https://localhost:8080/owners \
-d '{"Data":"eyJWZXJzaW9uIj...NSSllzPSJ9"}'
{"Status":"ok","Owners":["Alice","Bill","Cat","Dodo"]}
-### Password
-
-Password allows a user to change their password. This password change
-does not require the previously encrypted files to be re-encrypted.
-
-Example Input JSON format:
-
- $ curl --cacert cert/server.crt https://localhost:8080/password \
- -d '{"Name":"Bill","Password":"Lizard", "NewPassword": "theLizard"}'
- {"Status":"ok"}
### Modify
@@ -231,102 +778,523 @@ There are 3 commands:
- `admin`: grants admin status to a user
- `delete`: removes the account of a user
-Example input JSON format:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | Yes |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "ToModify": "User2",
+ "Command": "admin"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `ToModify` username to apply the `Command` against (required)
+- `Command` can be `revoke`, `admin`, `delete` (required)
+
+#### Response:
+```json
+{
+ "Status": "ok"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+
+#### Assumptions:
+- If a user is deleted, their key can never be delegated again because the key is deleted. If you recreate the user,
+ delegation will still fail as the keys will be different.
+
+#### Example input JSON format:
$ curl --cacert cert/server.crt https://localhost:8080/modify \
-d '{"Name":"Alice","Password":"Lewis","ToModify":"Bill","Command":"admin"}'
{"Status":"ok"}
-### Purge
-Purge deletes all delegates for an encryption key.
+### Export
-Example input JSON format:
+Export Red October's internal password vault, which contains users' hashed passwords, salts, encrypted private keys and their
+public keys.
- $ curl --cacert cert/server.crt https://localhost:8080/purge \
- -d '{"Name":"Alice","Password":"Lewis"}'
- {"Status":"ok"}
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | Yes |
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "ewogICAgIlZlcnNpb24iOiAxLCAjIG51bWJlcgogI...ICAgIH0KfQo="
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` is a base64 encoded JSON object
+
+If you were to base64 decode `Response`, you would get:
+```json
+{
+ "Version": 1,
+ "VaultId": 12905318,
+ "HmacKey": "JaEu/PQTkpFjp2rML8QUbQ==",
+ "Passwords": {
+ "User1": {
+ "Type": "",
+ "PasswordSalt": "rFQLnQj+5+I6/YPOteDSNw==",
+ "HashedPassword": "1WCgv8d9uXC9CcPQBQv9qw==",
+ "KeySalt": "gVDCnhwOMP3obXgv0avBlQ==",
+ "RSAKey": {
+ "RSAExp": "jfqESm+...+nMgQM3x34wQmGswfF62MhDk2sTw==",
+ "RSAExpIV": "gA0/0sS...iKNXVxXGCKXFyBlyGo4g==",
+ "RSAPrimeP": "xZnZ32YvEhhOV+...boElV9EA9OJvxsyODjQ==",
+ "RSAPrimePIV": "LneAiwSzMGbym7e7...nL12tcOy/LN4l8uAV8Khw==",
+ "RSAPrimeQ": "s/cnhyihrUS...sfnyLuxRIb1eL3aYB18dzjn0D0g==",
+ "RSAPrimeQIV": "S+8+9Q51Ig6/8in31J9y...4GD2BSig==",
+ "RSAPublic": {"N":707958947...4076221507,"E":65537}
+ },
+ "ECKey": {
+ "ECPriv":"vzrbL2pj8wxldTb6vYws7cAjIzGdh39TxepIb71GcB0=",
+ "ECPrivIV": "LPXav82KMgI3pdg30VPvI2cBsk4BQLaylg45NCYR0Bc=",
+ "ECPublic":{
+ "Curve":{
+ "P":11579208921035...308867097853951,
+ "N":11579208925624...259061068512369,
+ "B":41058363725114...725554835251291,
+ "Gx":4843956129391...807170824035286,
+ "Gy":3613425095677...253568414405109,
+ "BitSize":256,
+ "Name":"P-256"
+ },
+ "X":1288875298858438030...5394564151993378,
+ "Y":3728055919617037354...6025371017809918
+ },
+ "AltNames": {
+ "": "",
+ ...
+ },
+ "Admin": true
+ },
+ "User2": {
+ ...
+ },
+ ...
+ }
+}
+```
+
+#### Assumptions:
+- Only RSAKey or ECKey would be populated in an actual response
+- While the user passwords are hashed and and the various internal values of the ECKey and RSAKey are encrypted with the
+ user's cleartext password, these values are still sensitive and should be handled as such.
+
+#### Example query:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/export \
+ -d '{"Name":"Alice","Password":"Lewis"}'
+ {"Status":"ok","Response":"ewogICAgIlZlcn...ICAgIH0KfQo="}
+
### Order
Order creates a new order and lets other users know delegations are needed.
-Example input JSON format:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "Duration": "1h10m5s",
+ "Uses": 2,
+ "Users": ["User2", "User3"],
+ "EncryptedData": "nWY13Rx8d7Tov0AFGuf3IINzdHIFD.../PrxuExq",
+ "Labels": ["", ""]
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `Duration` must be parsable by Go's `time.ParseDuration()` and is the amount of time this `Order` is valid for (required)
+- `Uses` is the number of `Uses` required of each user delegation (required)
+- `Users` lists the specific user delegations being requested (required)
+- `EncryptedData` is the secret that the user delegations will be used against (required)
+- `Labels` are specific label requirements for the delegation requests, likely required for `/decrypt` of this specific secret (optional)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "ewogICJDcmVhdG9yIjogIlVz...gICAgIiIKICBdCn0K"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be a base64 encoded JSON object
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "Creator": "User1",
+ "Users": ["User2", "User3"],
+ "Num": "2ab99e07ff0405961f5e0d10",
+ "TimeRequested": "2009-11-10T23:00:00Z",
+ "DurationRequested": 4205000000000,
+ "Delegated": 1,
+ "OwnersDelegated": ["User3"],
+ "Owners": ["User4", "User3", "Users2"],
+ "Labels": ["", ""]
+}
+```
+- `Creator` is the user that created the `Order`
+- `Users` are the list of users in the `Order` request
+- `Num` is a hex encoded random 12 byte value, which is hte `OrderId`
+- `TimeRequested` is the time the `Order` was created
+- `DurationRequested` is the parsed value of `Duration` in 64 bit number form
+- `Delegated` is the number of user key delegations that already exist from those requested in the original request
+- `OwnersDelegated` is the users that were found already delegated (this array length will be the same as `Delegated`)
+- `Owners` are the users listed as `Owners` in the `EncryptedData` provided in the original request
+- `Labels` are the labels included in the original request
+
+#### Assumptions:
+- Orders work with the Hipchat components of Red October, so those should be configured and user details for Hipchat registered
+
+#### Example input JSON format:
$ curl --cacert server/server.crt https://localhost:8080/order \
-d '{"Name":"Alice","Password":"Lewis","Labels": ["Blue","Red"],\
"Duration":"1h","Uses":5,"EncryptedData":"ABCDE=="}'
- {
- "Admins": [
- "Bob",
- "Eve"
- ],
- "AdminsDelegated": null,
- "Delegated": 0,
- "DurationRequested": 3.6e+12,
- "Labels": [
- "blue",
- "red"
- ],
- "Name": "Alice",
- "Num": "77da1cfd8962fb9685c15c84",
- "TimeRequested": "2016-01-25T15:58:41.961906679-08:00",
- }
+ {"Status": "ok","Response": "ewogICJDcmVhdG9yIjogIlVzZXIxIiwKICAiVXNlcnMiOiBbCiAgICAiVXNlcjIiLAogICAgIlVzZXIzIgogIF0sCiAgIk51bSI6ICIyYWI5OWUwN2ZmMDQwNTk2MWY1ZTBkMTAiLAogICJUaW1lUmVxdWVzdGVkIjogIjIwMDktMTEtMTBUMjM6MDA6MDBaIiwKICAiRHVyYXRpb25SZXF1ZXN0ZWQiOiA0MjA1MDAwMDAwMDAwLAogICJEZWxlZ2F0ZWQiOiAxLAogICJPd25lcnNEZWxlZ2F0ZWQiOiBbCiAgICAiVXNlcjMiCiAgXSwKICAiT3duZXJzIjogWwogICAgIlVzZXI0IiwKICAgICJVc2VyMyIsCiAgICAiVXNlcnMyIgogIF0sCiAgIkxhYmVscyI6IFsKICAgICIiLAogICAgIiIKICBdCn0K"}
### Orders Outstanding
Orders Outstanding will return a list of current order numbers
-Example input JSON format:
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "ewogICIyYWI5OWUwN2Z...AgIF0KICB9Cn0K"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be a base64 encoded JSON object
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "2ab99e07ff0405961f5e0d10": {
+ "Creator": "User1",
+ "Users": ["User2", "User3"],
+ "Num": "2ab99e07ff0405961f5e0d10",
+ "TimeRequested": "2009-11-10T23:00:00Z",
+ "DurationRequested": 4205000000000,
+ "Delegated": 1,
+ "OwnersDelegated": ["User3"],
+ "Owners": ["User4", "User3", "Users2"],
+ "Labels": ["", ""]
+ },
+ ...
+}
+```
+- The object is a map where the `key` is each `Order`'s `Num` (a.k.a. `OrderNum`) and the `value` is the serialized `Order` object
+ - `Creator` is the user that created the `Order`
+ - `Users` are the list of users in the `Order` request
+ - `Num` is a hex encoded random 12 byte value, which is hte `OrderId`
+ - `TimeRequested` is the time the `Order` was created
+ - `DurationRequested` is the parsed value of `Duration` in 64 bit number form
+ - `Delegated` is the number of user key delegations that already exist from those requested in the original request
+ - `OwnersDelegated` is the users that were found already delegated (this array length will be the same as `Delegated`)
+ - `Owners` are the users listed as `Owners` in the `EncryptedData` provided in the original request
+ - `Labels` are the labels included in the original request
+
+#### Assumptions:
+- None
+
+#### Example input JSON format:
$ curl --cacert server/server.crt https://localhost:8080/orderout
-d '{"Name":"Alice","Password":"Lewis"}'
- {
- "77da1cfd8962fb9685c15c84":{
- "Name":"Alice",
- "Num":"77da1cfd8962fb9685c15c84",
- "TimeRequested":"2016-01-25T15:58:41.961906679-08:00",
- "DurationRequested":3600000000000,
- "Delegated":0,"
- AdminsDelegated":null,
- "Admins":["Bob, Eve"],
- "Labels":["Blue","Red"]
- }
- }
+ {"Status": "ok","Response": "ewogICI3N2RhMWNmZDg5NjJmYjk2ODVjMTVjODQiOiB7CiAgICAiTmFtZSI6ICJBbGljZSIsCiAgICAiVXNlcnMiOiBbCiAgICAgICJCb2IiLAogICAgICAiRXZlIgogICAgXSwKICAgICJOdW0iOiAiNzdkYTFjZmQ4OTYyZmI5Njg1YzE1Yzg0IiwKICAgICJUaW1lUmVxdWVzdGVkIjogIjIwMTYtMDEtMjVUMTU6NTg6NDEuOTYxOTA2Njc5LTA4OjAwIiwKICAgICJEdXJhdGlvblJlcXVlc3RlZCI6IDM2MDAwMDAwMDAwMDAsCiAgICAiRGVsZWdhdGVkIjogMCwKICAgICJPd25lcnNEZWxlZ2F0ZWQiOiBbXSwKICAgICJPd25lcnMiOiBbCiAgICAgICJCb2IiLAogICAgICAiRXZlIgogICAgXSwKICAgICJMYWJlbHMiOiBbCiAgICAgICJCbHVlIiwKICAgICAgIlJlZCIKICAgIF0KICB9Cn0K"}
### Order Information
-Example input JSON format:
+Returns the order information for a specific Order number.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "OrderNum": "2ab99e07ff0405961f5e0d10"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `OrderNum` is the same value returned as `Num` under and `Order` object (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "ewogICJDcmVhdG9yIjogIlVzZXIxIiwKICAiV...AogICAgIiIKICBdCn0K"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be a base64 encoded JSON object
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "Creator": "User1",
+ "Users": ["User2", "User3"],
+ "Num": "2ab99e07ff0405961f5e0d10",
+ "TimeRequested": "2009-11-10T23:00:00Z",
+ "DurationRequested": 4205000000000,
+ "Delegated": 1,
+ "OwnersDelegated": ["User3"],
+ "Owners": ["User4", "User3", "Users2"],
+ "Labels": ["", ""]
+}
+```
+- `Creator` is the user that created the `Order`
+- `Users` are the list of users in the `Order` request
+- `Num` is a hex encoded random 12 byte value, which is hte `OrderId`
+- `TimeRequested` is the time the `Order` was created
+- `DurationRequested` is the parsed value of `Duration` in 64 bit number form
+- `Delegated` is the number of user key delegations that already exist from those requested in the original request
+- `OwnersDelegated` is the users that were found already delegated (this array length will be the same as `Delegated`)
+- `Owners` are the users listed as `Owners` in the `EncryptedData` provided in the original request
+- `Labels` are the labels included in the original request
+
+#### Assumptions:
+- None
+
+#### Example input JSON format:
$ curl --cacert server/server.crt https://localhost:8080/orderinfo
-d '{"Name":"Alice","Password":"Lewis", \
"OrderNum":"77da1cfd8962fb9685c15c84"}'
- {
- "Admins": [
- "Bob",
- "Eve"
- ],
- "AdminsDelegated": null,
- "Delegated": 0,
- "DurationRequested": 3.6e+12,
- "Labels": [
- "blue",
- "red"
- ],
- "Name": "Alice",
- "Num": "77da1cfd8962fb9685c15c84",
- "TimeRequested": "2016-01-25T15:58:41.961906679-08:00"
- }
+ {"Status": "ok","Response": "ewogICJDcmVhdG9yIjogIlVzZXIxIiwKICAiVXNlcnMiOiBbCiAgICAiVXNlcjIiLAogICAgIlVzZXIzIgogIF0sCiAgIk51bSI6ICIyYWI5OWUwN2ZmMDQwNTk2MWY1ZTBkMTAiLAogICJUaW1lUmVxdWVzdGVkIjogIjIwMDktMTEtMTBUMjM6MDA6MDBaIiwKICAiRHVyYXRpb25SZXF1ZXN0ZWQiOiA0MjA1MDAwMDAwMDAwLAogICJEZWxlZ2F0ZWQiOiAxLAogICJPd25lcnNEZWxlZ2F0ZWQiOiBbCiAgICAiVXNlcjMiCiAgXSwKICAiT3duZXJzIjogWwogICAgIlVzZXI0IiwKICAgICJVc2VyMyIsCiAgICAiVXNlcnMyIgogIF0sCiAgIkxhYmVscyI6IFsKICAgICIiLAogICAgIiIKICBdCn0K"}
+
### Order Cancel
-Example input JSON format:
+Removes a given order from Red October's Orderer.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!",
+ "OrderNum": "2ab99e07ff0405961f5e0d10"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+- `OrderNum` is the same value returned as `Num` under and `Order` object (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "U3VjY2Vzc2Z1bGx5IHJlbW92ZWQgb3JkZXIK"
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be the base64 encoded string `"Successfully removed order"` if successful
+
+#### Assumptions:
+- None
+
+#### Example input JSON format:
$ curl --cacert server/server.crt https://localhost:8080/orderinfo
-d '{"Name":"Alice","Password":"Lewis", \
"OrderNum":"77da1cfd8962fb9685c15c84"}'
- {"Status":"ok"}
+ {"Status":"ok","Response": "U3VjY2Vzc2Z1bGx5IHJlbW92ZWQgb3JkZXIK"}
+
+
+
+### Restore
+
+Restore is functionally almost identical to Delegate, but for restoring the persisted delegation cache. Once enough calls
+to restore are completed to meet the delegation requirements of the persistence configuration, the current delegation cache
+will be overwritten with the persisted one.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password",
+ "Time": "",
+}
+```
+- `Name` and `Password` are the authentication fields for this request* (required)
+- `Time` is a duration the delegation is active and must parse with Go's `time.ParseDuration()` (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "e1N0YXR1czogYWN0aXZlfQo="
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be a base64 encoded JSON object if not empty
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "Status": "active"
+}
+```
+- `Status` could be `active`, `inactive`, or `disabled`
+
+#### Assumptions:
+- This function only does something if the persist module has been configured.
+
+#### Example query:
+On succesful restore
+
+ $ curl --cacert cert/server.crt https://localhost:8080/restore \
+ -d '{"Name":"Alice","Password":"Lewis","Time":"1h10m"}'
+ {"Status":"ok","Response":"e1N0YXR1czogYWN0aXZlfQo="}
+
+Need more calls to restore to delegate more credentials
+
+ $ curl --cacert cert/server.crt https://localhost:8080/restore \
+ -d '{"Name":"Alice","Password":"Lewis","Time":"1h10m"}'
+ {"Status":"need more delegated keys","Response":""}
+
+### Reset-Persisted
+
+Reset-Persisted will clear the persisted delegation data if configured.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | Yes |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "e1N0YXR1czogYWN0aXZlfQo="
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be a base64 encoded JSON object if not empty
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "Status": "active"
+}
+```
+- `Status` could be `active`, `inactive`, or `disabled`
+
+#### Assumptions:
+- None
+
+#### Example query:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/reset-persisted \
+ -d '{"Name":"Alice","Password":"Lewis"}'
+ {"Status":"ok","Response":"e1N0YXR1czogYWN0aXZlfQo="}
+
+
+### Status
+
+Status returns the current delegation persistence state.
+
+| Requires Authentication | Requires Admin |
+| ----------------------- | -------------- |
+| Yes | No |
+
+
+#### Request:
+```json
+{
+ "Name": "User1",
+ "Password": "User1Password!"
+}
+```
+- `Name` and `Password` are used to authenticate the request (required)
+
+#### Response:
+```json
+{
+ "Status": "ok",
+ "Response": "e1N0YXR1czogYWN0aXZlfQo="
+}
+```
+- `Status` will be `"ok"` if successful or an error string if not.
+- `Response` will be a base64 encoded JSON object if not empty
+
+If you base64 decode `Response`, you will get:
+```json
+{
+ "Status": "active"
+}
+```
+- `Status` could be `active`, `inactive`, or `disabled`
+
+#### Assumptions:
+- None
+
+#### Example query:
+
+ $ curl --cacert cert/server.crt https://localhost:8080/status \
+ -d '{"Name":"Alice","Password":"Lewis"}'
+ {"Status":"ok","Response":"e1N0YXR1czogYWN0aXZlfQo="}
+
+
### Web interface