Skip to content

Latest commit

 

History

History
306 lines (252 loc) · 6.69 KB

spec.md

File metadata and controls

306 lines (252 loc) · 6.69 KB

Keystore Spec

Problem

We need a keystore service that supports non-custodial applications. It will make the process of stellarizing any applications easier as they don't have to implement the logic to create a stellar account and handle the encrypted private key themselves.

It is also intended to be the service that wallet SDK talks to.

Authentication

For simplicity we will have each application spin up their own keystore server, so there won’t be any routing logic in the keystore server that directs requests to the correct client server to authenticate. Since we don’t anticipate a lot of requests to the keystore from each user, we should be able to tolerate having another round trip for relaying the auth token to the client server.

Keystore will forward two header fields, Authorization and Cookie, to the designated endpoint on the client server with an extra header field X-Forwarded-For specifying the request's origin. At this moment, keystore forwards incoming requests by using HTTP GET method. We plan on adding the support for clients who use GraphQL to authenticate in the future.

Clients are expected to put their auth tokens in one of the request header fields. For example, those who use a bearer token to authenticate should have an Authorization header in the following format:

Authorization: Bearer <token>

As mentioned above, clients will have to configure a API endpoint on their servers used for authentication when booting up the keystore. For those who choose to autheticate via a REST endpoint, keystore expects to receive a response in the following json format:

{
	"userID": "some-user-id"
}

Requests that the keystore is not able to derive a userID from will receive the following error:

not_authorized:

{
	"type": "not_authorized",
	"title": "Not Authorized",
	"status": 401,
	"detail": "The request is not authorized."
}

Raw Key Data

RawKeyData Object:

interface RawKeyData {
	keyType: string;
	publicKey: string;
	privateKey: string;
	path?: string;
	extra?: any;
}

Encrypted Key Data

EncryptedKeysData Object:

interface EncryptedKeyData {
	id: string;
	encrypterName: string;
	salt: string;
	encryptedBlob: string;
}

Clients will encrypt each RawKeyData they want to store on the keystore with a salt based on the encrypter they use. Clients should assign the resulting base64-encoded string to the field encryptedBlob in the EncryptedKeyData. Please refer to this encrypt function in our wallet sdk for more details.

Encrypted Keys

type EncryptedKeys = EncryptedKeyData[]

Clients will have to convert EncryptedKeys as a base64 URL encoded string before sending it to the keystore.

We support three different kinds of HTTP methods to manipulate keys:

Encrypted Keys Data

interface EncryptedKeysData {
	keysBlob: string;
	creationTime: number;
	modifiedTime: number;
}

Note that keysBlob has one global creation time and modified time even though there could be multiple keys in the blob.

PUT /keys

Put Keys Request:

interface PutKeysRequest {
	keysBlob: string;
}

where the value of the keysBlob field is base64_url_encode(EncryptedKeys).

Put Keys Response:

type PutKeysResponse = EncryptedKeysData;
Errors

bad_request:

{
	"keysBlob": "",
}
{
	"type": "bad_request",
	"title": "Bad Request",
	"status": 400,
	"detail": "The request you sent was invalid in some way.",
	"extras": {
		"invalid_field": "keysBlob",
		"reason": "field value cannot be empty"
	}
}

bad_request:

{
	"keysBlob": "some-encrypted-key-data-with-no-salt",
}
{
	"type": "bad_request",
	"title": "Bad Request",
	"status": 400,
	"detail": "The request you sent was invalid in some way.",
	"extras": {
		"invalid_field": "keysBlob",
		"reason": "salt is required for all the encrypted key data"
	}
}

bad_request:

{
	"keysBlob": "some-encrypted-key-data-with-no-encryptername",
}
{
	"type": "bad_request",
	"title": "Bad Request",
	"status": 400,
	"detail": "The request you sent was invalid in some way.",
	"extras": {
		"invalid_field": "keysBlob",
		"reason": "encrypterName is required for all the encrypted key data"
	}
}

bad_request:

{
	"keysBlob": "some-encrypted-key-data-with-no-encryptedblob",
}
{
	"type": "bad_request",
	"title": "Bad Request",
	"status": 400,
	"detail": "The request you sent was invalid in some way.",
	"extras": {
		"invalid_field": "keysBlob",
		"reason": "encryptedBlob is required for all the encrypted key data"
	}
}

bad_request:

{
	"keysBlob": "some-encrypted-key-data-with-no-id",
}
{
	"type": "bad_request",
	"title": "Bad Request",
	"status": 400,
	"detail": "The request you sent was invalid in some way.",
	"extras": {
		"invalid_field": "keysBlob",
		"reason": "id is required for all the encrypted key data"
	}
}

invalid_keys_blob:

{
	"keysBlob": "some-badly-encoded-blob",
}
{
	"type": "invalid_keys_blob",
	"title": "Invalid Keys Blob",
	"status": 400,
	"detail": "The keysBlob in your request body is not a valid base64-URL-encoded string or
		the decoded content cannt be mapped to EncryptedKeys type. Please encode the
		keysBlob in your request body as a base64-URL string properly or make sure the
		encoded content matches EncryptedKeys type specified in the spec and try again."
}

GET /keys

Get Keys Request:

This endpoint will return the keys blob corresponding to the auth token in the request header, if the token is valid. This endpoint does not take any parameter.

Get Keys Response:

type GetKeysResponse = EncryptedKeysData;
Errors

not_found:

The keystore cannot find any keys assocaited with the derived userID.

{
	"type": "not_found",
	"title": "Resourse Missing",
	"status": 404,
	"detail": "The resource at the url requested was not found. This
		usually occurs for one of two reasons:  The url requested is not valid,
		or no data in our database could be found with the parameters
		provided."
}

DELETE /keys

Delete Keys Request:

This endpoint will delete the keys blob corresponding to the auth token in the request header, if the token is valid. This endpoint does not take any parameter.

Delete Keys Response:

Success:

interface Success {
	message: "ok";
}
Errors