@cubist-labs/cubesigner-sdk 0.2.2 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +68 -0
- package/dist/src/api.d.ts +493 -0
- package/dist/src/api.js +1166 -0
- package/dist/src/client.d.ts +432 -335
- package/dist/src/client.js +195 -863
- package/dist/src/ethers/index.d.ts +33 -6
- package/dist/src/ethers/index.js +59 -12
- package/dist/src/index.d.ts +31 -26
- package/dist/src/index.js +51 -32
- package/dist/src/key.d.ts +28 -21
- package/dist/src/key.js +17 -10
- package/dist/src/mfa.d.ts +7 -7
- package/dist/src/mfa.js +20 -32
- package/dist/src/org.d.ts +37 -279
- package/dist/src/org.js +48 -194
- package/dist/src/paginator.js +1 -1
- package/dist/src/response.d.ts +101 -0
- package/dist/src/response.js +164 -0
- package/dist/src/role.d.ts +11 -9
- package/dist/src/role.js +1 -1
- package/dist/src/schema.d.ts +586 -10
- package/dist/src/schema.js +1 -1
- package/dist/src/schema_types.d.ts +6 -0
- package/dist/src/schema_types.js +1 -1
- package/dist/src/session/cognito_manager.d.ts +15 -3
- package/dist/src/session/cognito_manager.js +23 -5
- package/dist/src/session/session_manager.d.ts +1 -1
- package/dist/src/session/session_manager.js +3 -11
- package/dist/src/session/session_storage.js +1 -1
- package/dist/src/session/signer_session_manager.d.ts +3 -7
- package/dist/src/session/signer_session_manager.js +2 -8
- package/dist/src/signer_session.d.ts +8 -266
- package/dist/src/signer_session.js +15 -221
- package/dist/src/user_export.d.ts +52 -0
- package/dist/src/user_export.js +129 -0
- package/dist/src/util.d.ts +15 -0
- package/dist/src/util.js +33 -11
- package/package.json +12 -10
- package/src/api.ts +1395 -0
- package/src/client.ts +216 -1025
- package/src/ethers/index.ts +70 -12
- package/src/index.ts +59 -43
- package/src/key.ts +19 -12
- package/src/mfa.ts +16 -28
- package/src/org.ts +49 -204
- package/src/response.ts +196 -0
- package/src/role.ts +5 -3
- package/src/schema.ts +586 -10
- package/src/schema_types.ts +7 -0
- package/src/session/cognito_manager.ts +33 -6
- package/src/session/session_manager.ts +2 -8
- package/src/session/signer_session_manager.ts +3 -10
- package/src/signer_session.ts +13 -261
- package/src/user_export.ts +116 -0
- package/src/util.ts +29 -10
package/src/schema_types.ts
CHANGED
|
@@ -78,6 +78,13 @@ export type BtcSignatureKind = schemas["BtcSignatureKind"];
|
|
|
78
78
|
export type MfaType = schemas["MfaType"];
|
|
79
79
|
export type MfaRequestInfo = schemas["MfaRequestInfo"];
|
|
80
80
|
|
|
81
|
+
export type UserExportInitRequest = schemas["UserExportInitRequest"];
|
|
82
|
+
export type UserExportInitResponse = schemas["UserExportInitResponse"];
|
|
83
|
+
export type UserExportCompleteRequest = schemas["UserExportCompleteRequest"];
|
|
84
|
+
export type UserExportCompleteResponse = schemas["UserExportCompleteResponse"];
|
|
85
|
+
export type UserExportListResponse = schemas["PaginatedUserExportListResponse"];
|
|
86
|
+
export type UserExportKeyMaterial = schemas["JsonKeyPackage"];
|
|
87
|
+
|
|
81
88
|
/** Options for a new OIDC user */
|
|
82
89
|
export interface CreateOidcUserOptions {
|
|
83
90
|
/** The role of an OIDC user, default is "Alien" */
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { Client } from "../api";
|
|
2
3
|
import { EnvInterface } from "../env";
|
|
3
|
-
import { HasEnv,
|
|
4
|
-
import { SessionStorage } from "./session_storage";
|
|
4
|
+
import { HasEnv, OrgSessionManager } from "./session_manager";
|
|
5
|
+
import { JsonFileSessionStorage, SessionStorage } from "./session_storage";
|
|
6
|
+
import { configDir } from "../util";
|
|
5
7
|
|
|
6
8
|
/** JSON representation of our "management session" file format */
|
|
7
9
|
export interface CognitoSessionObject {
|
|
10
|
+
/** The organization ID */
|
|
11
|
+
org_id: string;
|
|
8
12
|
/** The email address of the user */
|
|
9
13
|
email: string;
|
|
10
14
|
/** The ID token */
|
|
@@ -23,7 +27,7 @@ export interface CognitoSessionInfo extends CognitoSessionObject, HasEnv {}
|
|
|
23
27
|
export type CognitoSessionStorage = SessionStorage<CognitoSessionInfo>;
|
|
24
28
|
|
|
25
29
|
/** The session manager for cognito (management) sessions */
|
|
26
|
-
export class CognitoSessionManager extends
|
|
30
|
+
export class CognitoSessionManager extends OrgSessionManager<CognitoSessionInfo> {
|
|
27
31
|
#client: Client;
|
|
28
32
|
|
|
29
33
|
/**
|
|
@@ -116,19 +120,42 @@ export class CognitoSessionManager extends SessionManager<CognitoSessionInfo> {
|
|
|
116
120
|
const sessionInfo = await storage.retrieve();
|
|
117
121
|
return new CognitoSessionManager(
|
|
118
122
|
sessionInfo.env["Dev-CubeSignerStack"],
|
|
123
|
+
sessionInfo.org_id,
|
|
119
124
|
sessionInfo.id_token,
|
|
120
125
|
storage,
|
|
121
126
|
);
|
|
122
127
|
}
|
|
123
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Loads an existing management session and creates a Cognito session manager for it.
|
|
131
|
+
*
|
|
132
|
+
* @param {CognitoSessionStorage} storage Optional session storage to load
|
|
133
|
+
* the session from. If not specified, the management session from the config
|
|
134
|
+
* directory will be loaded.
|
|
135
|
+
* @return {Promise<CognitoSessionManager>} Cognito session manager
|
|
136
|
+
*/
|
|
137
|
+
static async loadManagementSession(
|
|
138
|
+
storage?: CognitoSessionStorage,
|
|
139
|
+
): Promise<CognitoSessionManager> {
|
|
140
|
+
return await CognitoSessionManager.loadFromStorage(
|
|
141
|
+
storage ?? new JsonFileSessionStorage(path.join(configDir(), "management-session.json")),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
124
145
|
/**
|
|
125
146
|
* Constructor.
|
|
126
147
|
* @param {EnvInterface} env The environment of the session
|
|
148
|
+
* @param {string} orgId The id of the org associated with this session
|
|
127
149
|
* @param {string} token The current token of the session
|
|
128
150
|
* @param {CognitoSessionStorage} storage The storage back end to use
|
|
129
151
|
*/
|
|
130
|
-
private constructor(
|
|
131
|
-
|
|
152
|
+
private constructor(
|
|
153
|
+
env: EnvInterface,
|
|
154
|
+
orgId: string,
|
|
155
|
+
token: string,
|
|
156
|
+
storage: CognitoSessionStorage,
|
|
157
|
+
) {
|
|
158
|
+
super(env, orgId, storage);
|
|
132
159
|
this.#client = this.createClient(token);
|
|
133
160
|
}
|
|
134
161
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { SessionStorage } from "..";
|
|
2
2
|
import { EnvInterface } from "../env";
|
|
3
|
-
import { Client,
|
|
4
|
-
import createClient from "openapi-fetch";
|
|
3
|
+
import { Client, createHttpClient } from "../api";
|
|
5
4
|
|
|
6
5
|
const DEFAULT_EXPIRATION_BUFFER_SECS = 30;
|
|
7
6
|
|
|
@@ -83,12 +82,7 @@ export abstract class SessionManager<U> {
|
|
|
83
82
|
* @return {Client} The new REST client
|
|
84
83
|
*/
|
|
85
84
|
protected createClient(token: string): Client {
|
|
86
|
-
return
|
|
87
|
-
baseUrl: this.env.SignerApiRoot,
|
|
88
|
-
headers: {
|
|
89
|
-
Authorization: token,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
85
|
+
return createHttpClient(this.env.SignerApiRoot, token);
|
|
92
86
|
}
|
|
93
87
|
|
|
94
88
|
/**
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
NewSessionResponse,
|
|
5
5
|
RefreshSignerSessionRequest,
|
|
6
6
|
} from "../schema_types";
|
|
7
|
-
import { Client } from "../
|
|
7
|
+
import { Client } from "../api";
|
|
8
8
|
import { HasEnv, OrgSessionManager } from "./session_manager";
|
|
9
9
|
import { MemorySessionStorage, SessionStorage } from "./session_storage";
|
|
10
10
|
import { assertOk } from "../util";
|
|
@@ -62,13 +62,6 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
62
62
|
return this.#client;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
* @return {Client} A client using the current session (without attempting to refresh it).
|
|
67
|
-
*/
|
|
68
|
-
clientNoRefresh(): Client {
|
|
69
|
-
return this.#client;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
65
|
/** Revokes the session. */
|
|
73
66
|
async revoke(): Promise<void> {
|
|
74
67
|
const client = await this.client();
|
|
@@ -158,9 +151,9 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
158
151
|
* Constructor.
|
|
159
152
|
*
|
|
160
153
|
* @param {SignerSessionData} sessionData Session data
|
|
161
|
-
* @param {SignerSessionStorage} storage The session storage to use
|
|
154
|
+
* @param {SignerSessionStorage} storage The session storage to use.
|
|
162
155
|
*/
|
|
163
|
-
constructor(sessionData: SignerSessionData, storage: SignerSessionStorage) {
|
|
156
|
+
private constructor(sessionData: SignerSessionData, storage: SignerSessionStorage) {
|
|
164
157
|
super(sessionData.env["Dev-CubeSignerStack"], sessionData.org_id, storage);
|
|
165
158
|
this.#client = this.createClient(sessionData.token);
|
|
166
159
|
}
|
package/src/signer_session.ts
CHANGED
|
@@ -1,180 +1,7 @@
|
|
|
1
|
-
import assert from "assert";
|
|
2
|
-
import { CubeSigner, toKeyInfo, MfaReceipt, KeyInfo } from ".";
|
|
3
1
|
import { CubeSignerClient } from "./client";
|
|
4
|
-
import {
|
|
2
|
+
import { KeyInfo, toKeyInfo } from "./key";
|
|
5
3
|
import { SignerSessionManager, SignerSessionStorage } from "./session/signer_session_manager";
|
|
6
4
|
|
|
7
|
-
type Response<U> = U | AcceptedResponse;
|
|
8
|
-
type RequestFn<U> = (headers?: HeadersInit) => Promise<Response<U>>;
|
|
9
|
-
type MapFn<U, V> = (u: U) => V;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Takes a {@link Response<U>} and a {@link MapFn<U, V>} function and returns
|
|
13
|
-
* a {@link Response<V>} that maps the value of the original response when its status code is 200.
|
|
14
|
-
*
|
|
15
|
-
* @param {Response<U>} resp Original response
|
|
16
|
-
* @param {Map<U, V>} mapFn Map to apply to the response value when its status code is 200.
|
|
17
|
-
* @return {Response<V>} Response whose value for status code 200 is mapped from U to V
|
|
18
|
-
*/
|
|
19
|
-
export function mapResponse<U, V>(resp: Response<U>, mapFn: MapFn<U, V>): Response<V> {
|
|
20
|
-
if ((resp as AcceptedResponse).accepted?.MfaRequired) {
|
|
21
|
-
return resp as AcceptedResponse;
|
|
22
|
-
} else {
|
|
23
|
-
return mapFn(resp as U);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface MfaRequired {
|
|
28
|
-
/** Org id */
|
|
29
|
-
org_id: string;
|
|
30
|
-
/** MFA request id */
|
|
31
|
-
id: string;
|
|
32
|
-
/** Optional MFA session */
|
|
33
|
-
session?: NewSessionResponse | null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* A response of a CubeSigner request.
|
|
38
|
-
*/
|
|
39
|
-
export class CubeSignerResponse<U> {
|
|
40
|
-
readonly #requestFn: RequestFn<U>;
|
|
41
|
-
readonly #resp: U | AcceptedResponse;
|
|
42
|
-
/**
|
|
43
|
-
* Optional MFA id. Only set if there is an MFA request associated with the
|
|
44
|
-
* signing request
|
|
45
|
-
*/
|
|
46
|
-
readonly #mfaRequired?: MfaRequired;
|
|
47
|
-
|
|
48
|
-
/** @return {string} The MFA id associated with this request */
|
|
49
|
-
mfaId(): string {
|
|
50
|
-
return this.#mfaRequired!.id;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** @return {boolean} True if this request requires an MFA approval */
|
|
54
|
-
requiresMfa(): boolean {
|
|
55
|
-
return this.#mfaRequired !== undefined;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Returns session information to use for any MFA approval requests (if any was included in the response).
|
|
60
|
-
* @return {ClientSessionInfo | undefined}
|
|
61
|
-
*/
|
|
62
|
-
mfaSessionInfo(): NewSessionResponse | undefined {
|
|
63
|
-
return (this.#resp as AcceptedResponse).accepted?.MfaRequired?.session ?? undefined;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** @return {U} The response data, if no MFA is required */
|
|
67
|
-
data(): U {
|
|
68
|
-
if (this.requiresMfa()) {
|
|
69
|
-
throw new Error("Cannot call `data()` while MFA is required");
|
|
70
|
-
}
|
|
71
|
-
return this.#resp as U;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Approves the MFA request using a given session and a TOTP code.
|
|
76
|
-
*
|
|
77
|
-
* @param {SignerSession} session Signer session to use
|
|
78
|
-
* @param {string} code 6-digit TOTP code
|
|
79
|
-
* @return {CubeSignerResponse<U>} The result of signing with the approval
|
|
80
|
-
*/
|
|
81
|
-
async approveTotp(session: SignerSession, code: string): Promise<CubeSignerResponse<U>> {
|
|
82
|
-
assert(this.requiresMfa());
|
|
83
|
-
const mfaId = this.mfaId();
|
|
84
|
-
const mfaOrgId = this.#mfaRequired!.org_id;
|
|
85
|
-
const mfaApproval = await session.totpApprove(mfaId, code);
|
|
86
|
-
assert(mfaApproval.id === mfaId);
|
|
87
|
-
const mfaConf = mfaApproval.receipt?.confirmation;
|
|
88
|
-
|
|
89
|
-
if (!mfaConf) {
|
|
90
|
-
return this;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return await this.signWithMfaApproval({ mfaId, mfaOrgId, mfaConf });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Approves the MFA request using a given `CubeSignerClient` instance (i.e., its session).
|
|
98
|
-
*
|
|
99
|
-
* @param {CubeSigner} cs CubeSigner whose session to use
|
|
100
|
-
* @return {CubeSignerResponse<U>} The result of signing with the approval
|
|
101
|
-
*/
|
|
102
|
-
async approve(cs: CubeSigner): Promise<CubeSignerResponse<U>> {
|
|
103
|
-
assert(this.requiresMfa());
|
|
104
|
-
const mfaId = this.#mfaRequired!.id;
|
|
105
|
-
const mfaOrgId = this.#mfaRequired!.org_id;
|
|
106
|
-
|
|
107
|
-
const mfaApproval = await cs.mfaApprove(mfaOrgId, mfaId);
|
|
108
|
-
assert(mfaApproval.id === mfaId);
|
|
109
|
-
const mfaConf = mfaApproval.receipt?.confirmation;
|
|
110
|
-
|
|
111
|
-
if (!mfaConf) {
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return await this.signWithMfaApproval({ mfaId, mfaOrgId, mfaConf });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @param {MfaReceipt} mfaReceipt The MFA receipt
|
|
120
|
-
* @return {Promise<CubeSignerResponse<U>>} The result of signing after MFA approval
|
|
121
|
-
*/
|
|
122
|
-
async signWithMfaApproval(mfaReceipt: MfaReceipt): Promise<CubeSignerResponse<U>> {
|
|
123
|
-
const headers = CubeSignerResponse.getMfaHeaders(mfaReceipt);
|
|
124
|
-
return new CubeSignerResponse(this.#requestFn, await this.#requestFn(headers));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// --------------------------------------------------------------------------
|
|
128
|
-
// -- INTERNAL --------------------------------------------------------------
|
|
129
|
-
// --------------------------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Constructor.
|
|
133
|
-
*
|
|
134
|
-
* @param {RequestFn} requestFn
|
|
135
|
-
* The signing function that this response is from.
|
|
136
|
-
* This argument is used to resend requests with different headers if needed.
|
|
137
|
-
* @param {U | AcceptedResponse} resp The response as returned by the OpenAPI client.
|
|
138
|
-
*/
|
|
139
|
-
constructor(requestFn: RequestFn<U>, resp: U | AcceptedResponse) {
|
|
140
|
-
this.#requestFn = requestFn;
|
|
141
|
-
this.#resp = resp;
|
|
142
|
-
this.#mfaRequired = (this.#resp as AcceptedResponse).accepted?.MfaRequired;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Static constructor.
|
|
147
|
-
* @param {RequestFn} requestFn
|
|
148
|
-
* The request function that this response is from.
|
|
149
|
-
* This argument is used to resend requests with different headers if needed.
|
|
150
|
-
* @param {MfaReceipt} mfaReceipt Optional MFA receipt
|
|
151
|
-
* @return {Promise<CubeSignerResponse<U>>} New instance of this class.
|
|
152
|
-
*/
|
|
153
|
-
static async create<U>(
|
|
154
|
-
requestFn: RequestFn<U>,
|
|
155
|
-
mfaReceipt?: MfaReceipt,
|
|
156
|
-
): Promise<CubeSignerResponse<U>> {
|
|
157
|
-
const seed = await requestFn(this.getMfaHeaders(mfaReceipt));
|
|
158
|
-
return new CubeSignerResponse(requestFn, seed);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Returns HTTP headers containing a given MFA receipt.
|
|
163
|
-
*
|
|
164
|
-
* @param {MfaReceipt} mfaReceipt MFA receipt
|
|
165
|
-
* @return {HeadersInit} Headers including that receipt
|
|
166
|
-
*/
|
|
167
|
-
static getMfaHeaders(mfaReceipt?: MfaReceipt): HeadersInit | undefined {
|
|
168
|
-
return mfaReceipt
|
|
169
|
-
? {
|
|
170
|
-
"x-cubist-mfa-id": mfaReceipt.mfaId,
|
|
171
|
-
"x-cubist-mfa-org-id": mfaReceipt.mfaOrgId,
|
|
172
|
-
"x-cubist-mfa-confirmation": mfaReceipt.mfaConf,
|
|
173
|
-
}
|
|
174
|
-
: undefined;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
5
|
/** Signer session info. Can only be used to revoke a token, but not for authentication. */
|
|
179
6
|
export class SignerSessionInfo {
|
|
180
7
|
readonly #csc: CubeSignerClient;
|
|
@@ -206,93 +33,9 @@ export class SignerSessionInfo {
|
|
|
206
33
|
|
|
207
34
|
/**
|
|
208
35
|
* Signer session.
|
|
209
|
-
*
|
|
210
|
-
* @deprecated Use {@link CubeSignerClient} instead.
|
|
36
|
+
* Extends {@link CubeSignerClient} and provides a few convenience methods on top.
|
|
211
37
|
*/
|
|
212
|
-
export class SignerSession {
|
|
213
|
-
readonly #csc: CubeSignerClient;
|
|
214
|
-
|
|
215
|
-
/** Deprecated */
|
|
216
|
-
get sessionMgr() {
|
|
217
|
-
return this.#csc.sessionMgr;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Org id */
|
|
221
|
-
get orgId() {
|
|
222
|
-
return this.#csc.orgId;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Returns the list of keys that this token grants access to.
|
|
227
|
-
* @return {KeyInfo[]} The list of keys.
|
|
228
|
-
*/
|
|
229
|
-
async keys(): Promise<KeyInfo[]> {
|
|
230
|
-
const keys = await this.#csc.sessionKeysList();
|
|
231
|
-
return keys.map((k) => toKeyInfo(k));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** Approve a pending MFA request using TOTP. */
|
|
235
|
-
get totpApprove() {
|
|
236
|
-
return this.#csc.mfaApproveTotp.bind(this.#csc);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/** Initiate approval of an existing MFA request using FIDO. */
|
|
240
|
-
get fidoApproveStart() {
|
|
241
|
-
return this.#csc.mfaApproveFidoInit.bind(this.#csc);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/** Get a pending MFA request by its id. */
|
|
245
|
-
get getMfaInfo() {
|
|
246
|
-
return this.#csc.mfaGet.bind(this.#csc);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** Submit an EVM sign request. */
|
|
250
|
-
get signEvm() {
|
|
251
|
-
return this.#csc.signEvm.bind(this.#csc);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** Submit an 'eth2' sign request. */
|
|
255
|
-
get signEth2() {
|
|
256
|
-
return this.#csc.signEth2.bind(this.#csc);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/** Sign a stake request. */
|
|
260
|
-
get stake() {
|
|
261
|
-
return this.#csc.signStake.bind(this.#csc);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/** Sign an unstake request. */
|
|
265
|
-
get unstake() {
|
|
266
|
-
return this.#csc.signUnstake.bind(this.#csc);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/** Sign a raw blob.*/
|
|
270
|
-
get signBlob() {
|
|
271
|
-
return this.#csc.signBlob.bind(this.#csc);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/** Sign a bitcoin message. */
|
|
275
|
-
get signBtc() {
|
|
276
|
-
return this.#csc.signBtc.bind(this.#csc);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** Sign a solana message. */
|
|
280
|
-
get signSolana() {
|
|
281
|
-
return this.#csc.signSolana.bind(this.#csc);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/** Sign an Avalanche P- or X-chain message. */
|
|
285
|
-
get signAva() {
|
|
286
|
-
return this.#csc.signAva.bind(this.#csc);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Obtain a proof of authentication.
|
|
291
|
-
*/
|
|
292
|
-
get proveIdentity() {
|
|
293
|
-
return this.#csc.identityProve.bind(this.#csc);
|
|
294
|
-
}
|
|
295
|
-
|
|
38
|
+
export class SignerSession extends CubeSignerClient {
|
|
296
39
|
/**
|
|
297
40
|
* Loads an existing signer session from storage.
|
|
298
41
|
* @param {SignerSessionStorage} storage The session storage to use
|
|
@@ -309,6 +52,15 @@ export class SignerSession {
|
|
|
309
52
|
* @internal
|
|
310
53
|
*/
|
|
311
54
|
constructor(sessionMgr: SignerSessionManager) {
|
|
312
|
-
|
|
55
|
+
super(sessionMgr);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns the list of keys that this token grants access to.
|
|
60
|
+
* @return {KeyInfo[]} The list of keys.
|
|
61
|
+
*/
|
|
62
|
+
async keys(): Promise<KeyInfo[]> {
|
|
63
|
+
const keys = await this.sessionKeysList();
|
|
64
|
+
return keys.map((k) => toKeyInfo(k));
|
|
313
65
|
}
|
|
314
66
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { UserExportCompleteResponse, UserExportKeyMaterial } from "./schema_types";
|
|
2
|
+
import { decodeBase64 } from "./util";
|
|
3
|
+
import type { CipherSuite } from "@hpke/core";
|
|
4
|
+
|
|
5
|
+
/** Get the HPKE ciphersuite for user-export decryption.
|
|
6
|
+
*
|
|
7
|
+
* @return {any} The HPKE ciphersuite for user export.
|
|
8
|
+
*/
|
|
9
|
+
export async function userExportCipherSuite(): Promise<CipherSuite> {
|
|
10
|
+
const hpke = await import("@hpke/core"); // eslint-disable-line @typescript-eslint/no-var-requires
|
|
11
|
+
const suite = new hpke.CipherSuite({
|
|
12
|
+
kem: new hpke.DhkemP256HkdfSha256(),
|
|
13
|
+
kdf: new hpke.HkdfSha256(),
|
|
14
|
+
aead: new hpke.Aes256Gcm(),
|
|
15
|
+
});
|
|
16
|
+
return suite;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a key pair for user export.
|
|
21
|
+
*
|
|
22
|
+
* @return {Promise<CryptoKeyPair>} The newly generated key pair.
|
|
23
|
+
*/
|
|
24
|
+
export async function userExportKeygen(): Promise<CryptoKeyPair> {
|
|
25
|
+
return (await userExportCipherSuite()).kem.generateKeyPair();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the ArrayBuffer slice represented by a Buffer
|
|
30
|
+
*
|
|
31
|
+
* @param {Uint8Array} b The buffer to convert
|
|
32
|
+
* @return {ArrayBuffer} The resulting ArrayBuffer
|
|
33
|
+
*/
|
|
34
|
+
function toArrayBuffer(b: Uint8Array): ArrayBuffer {
|
|
35
|
+
return b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Decrypt a user export.
|
|
40
|
+
*
|
|
41
|
+
* @param {CryptoKey} recipientKey The NIST P-256 secret key corresponding to the `publicKey` argument to the `userExportComplete` invocation that returned `response`.
|
|
42
|
+
* @param {UserExportCompleteResponse} response The response from a successful `userExportComplete` request.
|
|
43
|
+
* @return {Promise<UserExportKeyMaterial>} The decrypted key material.
|
|
44
|
+
*/
|
|
45
|
+
export async function userExportDecrypt(
|
|
46
|
+
recipientKey: CryptoKey,
|
|
47
|
+
response: UserExportCompleteResponse,
|
|
48
|
+
): Promise<UserExportKeyMaterial> {
|
|
49
|
+
// The ciphersuite we use for decryption
|
|
50
|
+
const suite = await userExportCipherSuite();
|
|
51
|
+
|
|
52
|
+
// decrypt the export ciphertext using the HPKE one-shot API
|
|
53
|
+
const tenc = new TextEncoder();
|
|
54
|
+
const tdec = new TextDecoder();
|
|
55
|
+
const info = toArrayBuffer(tenc.encode(`cubist-signer::UserExportOwner::${response.user_id}`));
|
|
56
|
+
const public_key = toArrayBuffer(decodeBase64(response.ephemeral_public_key));
|
|
57
|
+
const ctxt = toArrayBuffer(decodeBase64(response.encrypted_key_material));
|
|
58
|
+
const decrypted: UserExportKeyMaterial = JSON.parse(
|
|
59
|
+
tdec.decode(
|
|
60
|
+
await suite.open(
|
|
61
|
+
{
|
|
62
|
+
recipientKey,
|
|
63
|
+
enc: public_key,
|
|
64
|
+
info: info,
|
|
65
|
+
},
|
|
66
|
+
ctxt,
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return decrypted;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Figure out how to load SubtleCrypto in the current environment.
|
|
76
|
+
*
|
|
77
|
+
* This functionality is reproduced from the hpke-js package,
|
|
78
|
+
* https://github.com/dajiaji/hpke-js/
|
|
79
|
+
* which is Copyright (C) 2022 Ajitomi Daisuke and licensed
|
|
80
|
+
* under the MIT License, which follows:
|
|
81
|
+
*
|
|
82
|
+
* MIT License
|
|
83
|
+
*
|
|
84
|
+
* Copyright (c) 2022 Ajitomi Daisuke
|
|
85
|
+
*
|
|
86
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
87
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
88
|
+
* in the Software without restriction, including without limitation the rights
|
|
89
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
90
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
91
|
+
* furnished to do so, subject to the following conditions:
|
|
92
|
+
*
|
|
93
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
94
|
+
* copies or substantial portions of the Software.
|
|
95
|
+
*
|
|
96
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
97
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
98
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
99
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
100
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
101
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
102
|
+
* SOFTWARE.
|
|
103
|
+
*/
|
|
104
|
+
export async function loadSubtleCrypto() {
|
|
105
|
+
if (globalThis !== undefined && globalThis.crypto !== undefined) {
|
|
106
|
+
// Browsers, Node.js >= v19, Cloudflare Workers, Bun, etc.
|
|
107
|
+
return globalThis.crypto.subtle;
|
|
108
|
+
}
|
|
109
|
+
// Node.js <= v18
|
|
110
|
+
try {
|
|
111
|
+
const { webcrypto } = await import("crypto"); // node:crypto
|
|
112
|
+
return (webcrypto as unknown as Crypto).subtle;
|
|
113
|
+
} catch (e: unknown) {
|
|
114
|
+
throw new Error("subtle crypto not supported");
|
|
115
|
+
}
|
|
116
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -65,6 +65,18 @@ export function assertOk<D, T>(resp: ResponseType<D, T>, description?: string):
|
|
|
65
65
|
return resp.data;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Browser-friendly helper for decoding a 'base64'-encoded string into a byte array.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} b64 The 'base64'-encoded string to decode
|
|
72
|
+
* @return {Uint8Array} Decoded byte array
|
|
73
|
+
*/
|
|
74
|
+
export function decodeBase64(b64: string): Uint8Array {
|
|
75
|
+
return typeof Buffer === "function"
|
|
76
|
+
? Buffer.from(b64, "base64")
|
|
77
|
+
: Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
/**
|
|
69
81
|
* Browser-friendly helper for decoding a 'base64url'-encoded string into a byte array.
|
|
70
82
|
*
|
|
@@ -72,28 +84,35 @@ export function assertOk<D, T>(resp: ResponseType<D, T>, description?: string):
|
|
|
72
84
|
* @return {Uint8Array} Decoded byte array
|
|
73
85
|
*/
|
|
74
86
|
export function decodeBase64Url(b64url: string): Uint8Array {
|
|
75
|
-
const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/").replace(/=*$/g, "");
|
|
76
|
-
|
|
77
87
|
// NOTE: there is no "base64url" encoding in the "buffer" module for the browser (unlike in node.js)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
: Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
88
|
+
const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/").replace(/=*$/g, "");
|
|
89
|
+
return decodeBase64(b64);
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
/**
|
|
84
|
-
*
|
|
93
|
+
*
|
|
94
|
+
* Browser-friendly helper for encoding a byte array into a padded `base64`-encoded string.
|
|
85
95
|
*
|
|
86
96
|
* @param {Iterable<number>} buffer The byte array to encode
|
|
87
|
-
* @return {string} The '
|
|
97
|
+
* @return {string} The 'base64' encoding of the byte array.
|
|
88
98
|
*/
|
|
89
|
-
export function
|
|
99
|
+
export function encodeToBase64(buffer: Iterable<number>): string {
|
|
90
100
|
const bytes = new Uint8Array(buffer);
|
|
91
|
-
|
|
92
|
-
// NOTE: there is no "base64url" encoding in the "buffer" module for the browser (unlike in node.js)
|
|
93
101
|
const b64 =
|
|
94
102
|
typeof Buffer === "function"
|
|
95
103
|
? Buffer.from(bytes).toString("base64")
|
|
96
104
|
: btoa(bytes.reduce((s, b) => s + String.fromCharCode(b), ""));
|
|
105
|
+
return b64;
|
|
106
|
+
}
|
|
97
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Browser-friendly helper for encoding a byte array into a 'base64url`-encoded string.
|
|
110
|
+
*
|
|
111
|
+
* @param {Iterable<number>} buffer The byte array to encode
|
|
112
|
+
* @return {string} The 'base64url' encoding of the byte array.
|
|
113
|
+
*/
|
|
114
|
+
export function encodeToBase64Url(buffer: Iterable<number>): string {
|
|
115
|
+
const b64 = encodeToBase64(buffer);
|
|
116
|
+
// NOTE: there is no "base64url" encoding in the "buffer" module for the browser (unlike in node.js)
|
|
98
117
|
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, "");
|
|
99
118
|
}
|