@cubist-labs/cubesigner-sdk 0.1.23
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/LICENSE-APACHE +177 -0
- package/LICENSE-MIT +25 -0
- package/NOTICE +13 -0
- package/README.md +470 -0
- package/dist/examples/ethers.d.ts +1 -0
- package/dist/examples/ethers.js +142 -0
- package/dist/spec/env/beta.json +9 -0
- package/dist/spec/env/gamma.json +9 -0
- package/dist/spec/env/prod.json +9 -0
- package/dist/src/client.d.ts +10 -0
- package/dist/src/client.js +21 -0
- package/dist/src/env.d.ts +15 -0
- package/dist/src/env.js +35 -0
- package/dist/src/ethers/index.d.ts +50 -0
- package/dist/src/ethers/index.js +122 -0
- package/dist/src/index.d.ts +114 -0
- package/dist/src/index.js +205 -0
- package/dist/src/key.d.ts +114 -0
- package/dist/src/key.js +201 -0
- package/dist/src/mfa.d.ts +23 -0
- package/dist/src/mfa.js +63 -0
- package/dist/src/org.d.ts +161 -0
- package/dist/src/org.js +264 -0
- package/dist/src/role.d.ts +224 -0
- package/dist/src/role.js +256 -0
- package/dist/src/schema.d.ts +3049 -0
- package/dist/src/schema.js +7 -0
- package/dist/src/session/generic.d.ts +47 -0
- package/dist/src/session/generic.js +3 -0
- package/dist/src/session/management_session_manager.d.ts +59 -0
- package/dist/src/session/management_session_manager.js +111 -0
- package/dist/src/session/oidc_session_manager.d.ts +78 -0
- package/dist/src/session/oidc_session_manager.js +142 -0
- package/dist/src/session/session_manager.d.ts +74 -0
- package/dist/src/session/session_manager.js +79 -0
- package/dist/src/session/session_storage.d.ts +47 -0
- package/dist/src/session/session_storage.js +76 -0
- package/dist/src/session/signer_session_manager.d.ts +88 -0
- package/dist/src/session/signer_session_manager.js +159 -0
- package/dist/src/sign.d.ts +114 -0
- package/dist/src/sign.js +248 -0
- package/dist/src/signer_session.d.ts +180 -0
- package/dist/src/signer_session.js +369 -0
- package/dist/src/util.d.ts +35 -0
- package/dist/src/util.js +75 -0
- package/dist/test/sessions.d.ts +35 -0
- package/dist/test/sessions.js +56 -0
- package/package.json +61 -0
- package/src/client.ts +12 -0
- package/src/env.ts +25 -0
- package/src/ethers/index.ts +131 -0
- package/src/index.ts +220 -0
- package/src/key.ts +249 -0
- package/src/org.ts +333 -0
- package/src/role.ts +385 -0
- package/src/schema.ts +3054 -0
- package/src/session/management_session_manager.ts +136 -0
- package/src/session/oidc_session_manager.ts +193 -0
- package/src/session/session_manager.ts +114 -0
- package/src/session/session_storage.ts +73 -0
- package/src/session/signer_session_manager.ts +211 -0
- package/src/signer_session.ts +464 -0
- package/src/util.ts +58 -0
- package/tsconfig.json +32 -0
package/LICENSE-APACHE
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
package/LICENSE-MIT
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Copyright (C) 2022-2023 Cubist, Inc.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any
|
|
4
|
+
person obtaining a copy of this software and associated
|
|
5
|
+
documentation files (the "Software"), to deal in the
|
|
6
|
+
Software without restriction, including without
|
|
7
|
+
limitation the rights to use, copy, modify, merge,
|
|
8
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software
|
|
10
|
+
is furnished to do so, subject to the following
|
|
11
|
+
conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice
|
|
14
|
+
shall be included in all copies or substantial portions
|
|
15
|
+
of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
18
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
19
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
20
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
21
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
22
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
24
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
25
|
+
DEALINGS IN THE SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (C) 2023 Cubist, Inc.
|
|
2
|
+
|
|
3
|
+
This software is licensed under either of
|
|
4
|
+
|
|
5
|
+
- Apache License, Version 2.0
|
|
6
|
+
(see https://www.apache.org/licenses/LICENSE-2.0 or LICENSE-APACHE)
|
|
7
|
+
|
|
8
|
+
- MIT license
|
|
9
|
+
(see https://opensource.org/licenses/MIT or LICENSE-MIT)
|
|
10
|
+
|
|
11
|
+
at your option.
|
|
12
|
+
|
|
13
|
+
The SPDX identifier for this project is "MIT OR Apache-2.0".
|
package/README.md
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# CubeSigner TypeScript SDK
|
|
2
|
+
|
|
3
|
+
CubeSigner is a hardware-backed, non-custodial key management platform
|
|
4
|
+
built by Cubist for programmatically managing cryptographic keys.
|
|
5
|
+
|
|
6
|
+
This repository is our SDK written in TypeScript for programmatically
|
|
7
|
+
interacting with CubeSigner services.
|
|
8
|
+
|
|
9
|
+
## Installing the SDK
|
|
10
|
+
|
|
11
|
+
You can install the SDK from npm, or from GitHub:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install --save "@cubist-labs/cubesigner-sdk" "github:cubist-labs/CubeSigner-TypeScript-SDK"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Logging into CubeSigner
|
|
18
|
+
|
|
19
|
+
Before running the "getting started" examples below (or tests later), you must
|
|
20
|
+
log into your CubeSigner organization using the `cs` command-line tool, e.g.,
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cs login owner@example.com --env '<gamma|prod|...>'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Getting started
|
|
27
|
+
|
|
28
|
+
In this section we are going to walk through a simple CubeSigner
|
|
29
|
+
setup. We'll create a signing key, then sign some EVM
|
|
30
|
+
transactions, and then add a security policy to restrict the kinds of
|
|
31
|
+
transactions that CubeSigner is allowed to sign.
|
|
32
|
+
|
|
33
|
+
To start, we'll instantiate the top-level `CubeSigner` class from an
|
|
34
|
+
existing CubeSigner management session already stored on disk
|
|
35
|
+
(remember, you must already be logged in).
|
|
36
|
+
|
|
37
|
+
Let's also assume that the following imports are available to all the
|
|
38
|
+
examples below.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import * as cs from "@cubist-labs/cubesigner-sdk";
|
|
42
|
+
import assert from "assert";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Instantiate `CubeSigner`
|
|
46
|
+
|
|
47
|
+
The first order of business is to create an instance of `CubeSigner`.
|
|
48
|
+
We can do that by simply loading the management session token from the
|
|
49
|
+
default location on disk (which is where the `cs login` command saves
|
|
50
|
+
it):
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const cubesigner = await cs.CubeSigner.loadManagementSession();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Alternatively, a `CubeSigner` instance can be created by explicitly providing a
|
|
57
|
+
session manager:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// Load session from a JSON file
|
|
61
|
+
const fileStorage = new cs.JsonFileSessionStorage<cs.ManagementSessionInfo>(
|
|
62
|
+
`${process.env.HOME}/.config/cubesigner/management-session.json`,
|
|
63
|
+
);
|
|
64
|
+
// Create a session manager for a management token
|
|
65
|
+
const sessionMgr = await cs.ManagementSessionManager.loadFromStorage(fileStorage);
|
|
66
|
+
new cs.CubeSigner({
|
|
67
|
+
sessionMgr,
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Get `User` and `Org` info
|
|
72
|
+
|
|
73
|
+
We can now obtain some information about the logged-in user and the
|
|
74
|
+
organization the user belongs to:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const me = await cubesigner.aboutMe();
|
|
78
|
+
console.log(me);
|
|
79
|
+
assert(me.user_id); // each user has a globally unique ID
|
|
80
|
+
assert(me.org_ids); // IDs of all organizations this user is a member of
|
|
81
|
+
assert(me.org_ids.length > 0); // assume that the user is a member of at least one org
|
|
82
|
+
|
|
83
|
+
const org = await cubesigner.getOrg(me.org_ids[0]);
|
|
84
|
+
assert(await org.enabled()); // assume that the org is enabled
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
There is a lot more to do with an organization, like creating/listing
|
|
88
|
+
keys, creating/listing roles, setting up org-wide security policies,
|
|
89
|
+
etc.
|
|
90
|
+
|
|
91
|
+
For the rest of this tutorial, we assume the logged-in user is a
|
|
92
|
+
member of at least one organization.
|
|
93
|
+
|
|
94
|
+
### Create a `Key`
|
|
95
|
+
|
|
96
|
+
Next, let's create a key that we'll later use to sign an Ethereum
|
|
97
|
+
transaction. For that, we need a key of type `Secp256k1.Evm`.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const secpKey = await org.createKey(cs.Secp256k1.Evm);
|
|
101
|
+
assert((await secpKey.owner()) == me.user_id);
|
|
102
|
+
assert(await secpKey.enabled());
|
|
103
|
+
console.log(`Created '${secpKey.type}' key ${secpKey.id}`);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Create a `Role` and a `SignerSession`
|
|
107
|
+
|
|
108
|
+
CubeSigner differentiates between _management_ and _signer_ sessions
|
|
109
|
+
since managing keys (and an org) is often separate from using keys.
|
|
110
|
+
|
|
111
|
+
Everything we've done so far has been using a management session. To
|
|
112
|
+
sign a message with a key, we now need to create a signer
|
|
113
|
+
session. Signer sessions are associated with roles (which we discuss
|
|
114
|
+
in this section) or users (which we discuss [later](#create-a-session-for-an-oidc-user)).
|
|
115
|
+
|
|
116
|
+
You can think of roles as groups
|
|
117
|
+
that give certain users access to certain keys. To get started, let's
|
|
118
|
+
create a `Role` and then simply call `createSession` on it:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const role = await org.createRole();
|
|
122
|
+
const sessionStorage = new cs.MemorySessionStorage<cs.SignerSessionData>();
|
|
123
|
+
const session = await role.createSession(sessionStorage, "readme");
|
|
124
|
+
console.log(`Created signer session for role '${role.id}'`);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Sessions have lifetimes, so they need to periodically refresh
|
|
128
|
+
themselves to stay valid. When that happens, the refreshed session
|
|
129
|
+
needs to be saved somewhere, which is why the `createSession` method
|
|
130
|
+
requires an instance of `SessionStorage`. In this example, we don't
|
|
131
|
+
plan to persist the session across multiple runs, so a simple
|
|
132
|
+
in-memory storage suffices; otherwise, opting for
|
|
133
|
+
`JsonFileSessionStorage` would be a better idea, i.e.,
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// this storage persists the signer session token to a file
|
|
137
|
+
// named 'session.json' in the current working directory
|
|
138
|
+
new cs.JsonFileSessionStorage("session.json");
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Sign an Ethereum transaction
|
|
142
|
+
|
|
143
|
+
Let's create a dummy `EvmSignRequest`.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const eth1Request = <cs.EvmSignRequest>{
|
|
147
|
+
chain_id: 1,
|
|
148
|
+
tx: <any>{
|
|
149
|
+
to: "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b",
|
|
150
|
+
type: "0x00",
|
|
151
|
+
gas: "0x61a80",
|
|
152
|
+
gasPrice: "0x77359400",
|
|
153
|
+
nonce: "0",
|
|
154
|
+
value: "0x100",
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
It seems we have everything in place to sign this request with the
|
|
160
|
+
previously created key. However, attempting to do so fails with `403
|
|
161
|
+
Forbidden` saying something like `Role 'Role#... is not authorized to
|
|
162
|
+
access key 'Key#...'`:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
try {
|
|
166
|
+
console.log("Trying to sign before adding key to role");
|
|
167
|
+
await session.signEvm(secpKey, eth1Request);
|
|
168
|
+
assert(false, "Must not be allowed to sign with key not in role");
|
|
169
|
+
} catch (e) {
|
|
170
|
+
assert(`${e}`.includes("not authorized to access key"));
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
By default, a newly created role does not get to access any
|
|
175
|
+
keys. Instead, keys have to be added to a role explicitly.
|
|
176
|
+
After we do that, the `signEvm` call should succeed:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
console.log("Adding key to role, then signing an Ethereum transaction");
|
|
180
|
+
await role.addKey(secpKey);
|
|
181
|
+
let sig = await session.signEvm(secpKey, eth1Request);
|
|
182
|
+
console.log(sig.data());
|
|
183
|
+
assert(sig.data().rlp_signed_tx);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Using ethers.js instead of the SDK directly
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const { ethers } = require("ethers");
|
|
190
|
+
// Create new Signer
|
|
191
|
+
const ethersSigner = new cs.ethers.Signer(secpKey.materialId, session /*, provider */);
|
|
192
|
+
assert((await ethersSigner.getAddress()) === secpKey.materialId);
|
|
193
|
+
// sign transaction as usual:
|
|
194
|
+
console.log(
|
|
195
|
+
"ethers.js signature:",
|
|
196
|
+
await ethersSigner.signTransaction({
|
|
197
|
+
to: "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b",
|
|
198
|
+
value: ethers.parseEther("0.0000001"),
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Set security policies
|
|
204
|
+
|
|
205
|
+
When we add a `Secp256k1.Evm` key to a role (as we did above), a signer session
|
|
206
|
+
associated with that role allows us to sign **any** Ethereum
|
|
207
|
+
transaction with that key. If that seems too permissive, we can attach a security
|
|
208
|
+
policy to restrict the allowed usages of this key in this role.
|
|
209
|
+
|
|
210
|
+
For example, to restrict signing to transactions with a pre-approved
|
|
211
|
+
recipient, we can attach a `TxReceiver` policy to our key:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
console.log("Setting transaction receiver policy");
|
|
215
|
+
await secpKey.setPolicy([{ TxReceiver: "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b" }]);
|
|
216
|
+
console.log("Signing transaction");
|
|
217
|
+
sig = await session.signEvm(secpKey, eth1Request);
|
|
218
|
+
assert(sig.data().rlp_signed_tx);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Try changing the transaction receiver and verify that the transaction
|
|
222
|
+
indeed gets rejected:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
console.log("Signing a transaction to a different receiver must be rejected");
|
|
226
|
+
try {
|
|
227
|
+
await session.signEvm(secpKey, {
|
|
228
|
+
chain_id: 1,
|
|
229
|
+
tx: <any>{
|
|
230
|
+
...eth1Request.tx,
|
|
231
|
+
to: "0x0000000000000000000000000000000000000000",
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
assert(false, "Must be rejected by policy");
|
|
235
|
+
} catch (e) {
|
|
236
|
+
assert(`${e}`.includes("Transaction receiver not allowed by policy"));
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> **Warning**
|
|
241
|
+
> Setting new policies overwrites the previous ones. Call
|
|
242
|
+
> `Key::appendPolicy` instead of `Key::setPolicy` to append to
|
|
243
|
+
> existing policies.
|
|
244
|
+
|
|
245
|
+
### Sign a raw blob
|
|
246
|
+
|
|
247
|
+
The `SignerSession` class exposes the `signBlob` method, which signs
|
|
248
|
+
an arbitrary (raw, uninterpreted) bag of bytes with a given key. This
|
|
249
|
+
operation, however, is not permitted by default; it is permanently
|
|
250
|
+
disabled for `BLS` keys, and for other key types it can be enabled by
|
|
251
|
+
attaching an `"AllowRawBlobSigning"` policy:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
console.log("Confirming that raw blob is rejected by default");
|
|
255
|
+
const blobReq = <cs.BlobSignRequest>{
|
|
256
|
+
message_base64: "L1kE9g59xD3fzYQQSR7340BwU9fGrP6EMfIFcyX/YBc=",
|
|
257
|
+
};
|
|
258
|
+
try {
|
|
259
|
+
await session.signBlob(secpKey, blobReq);
|
|
260
|
+
assert(false, "Must be rejected by policy");
|
|
261
|
+
} catch (e) {
|
|
262
|
+
assert(`${e}`.includes("Raw blob signing not allowed"));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.log("Signing raw blob after adding 'AllowRawBlobSigning' policy");
|
|
266
|
+
await secpKey.appendPolicy(["AllowRawBlobSigning"]);
|
|
267
|
+
const blobSig = await session.signBlob(secpKey, blobReq);
|
|
268
|
+
console.log(blobSig.data());
|
|
269
|
+
assert(blobSig.data().signature);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
> **Warning**
|
|
273
|
+
> When signing a raw blob with a `Secp256k1` key, the blob **MUST** be the output of a secure hash function like SHA-256, and must be exactly 32 bytes long. This is a strict requirement of the ECDSA signature algorithm used by Bitcoin, Ethereum, and other blockchains. Signing any byte string that is not the output of a secure hash function can lead to catastrophic security failure, including completely leaking your secret key.
|
|
274
|
+
|
|
275
|
+
Trying to sign an invalid message with a secp key will fail with
|
|
276
|
+
`400 Bad Request` saying `Signature scheme: InvalidMessage`.
|
|
277
|
+
|
|
278
|
+
### Load a signer session
|
|
279
|
+
|
|
280
|
+
If signing is all that your application needs to do, you can instantiate a
|
|
281
|
+
`SignerSession` directly from a signer session token, without ever needing a
|
|
282
|
+
management session.
|
|
283
|
+
|
|
284
|
+
First, get an existing token for a signer session:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
const token = await sessionStorage.retrieve();
|
|
288
|
+
// alternatively, save this object to a file
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
or generate a new one from the command line:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
cs token create --role-id $ROLE_ID --purpose MyPurpose --output json
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Next, create a `MemorySessionStorage` containing the previously
|
|
298
|
+
exported signer token, and just load the session from it.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
const signerSession = await cs.CubeSigner.loadSignerSession(
|
|
302
|
+
// alternatively, load 'token' from file or environment variable
|
|
303
|
+
new cs.MemorySessionStorage(token),
|
|
304
|
+
);
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Finally, use the new signer session to sign a transaction. To specify
|
|
308
|
+
the signing key, you can pass its "material id" as a plain string:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
console.log("Signing transaction using imported signer session");
|
|
312
|
+
const secpKeyStr: string = secpKey.materialId;
|
|
313
|
+
sig = await signerSession.signEvm(secpKeyStr, eth1Request);
|
|
314
|
+
console.log(sig.data());
|
|
315
|
+
assert(sig.data().rlp_signed_tx);
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Create a session for an OIDC user
|
|
319
|
+
|
|
320
|
+
CubeSigner supports the [OIDC](https://openid.net/developers/how-connect-works/)
|
|
321
|
+
standard for authenticating third-party users.
|
|
322
|
+
|
|
323
|
+
First, we need an OIDC token. We can get one from Google or any other
|
|
324
|
+
supported OIDC issuer, including CubeSigner! Remember that we had to
|
|
325
|
+
[log in](#logging-into-cubesigner) before we could instantiate
|
|
326
|
+
`CubeSigner`; now we can simply ask that instance of `CubeSigner` to
|
|
327
|
+
export the underlying token:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const oidcToken = await cubesigner.sessionMgr!.token();
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
and then exchange it for either a _signer_ session (i.e., an instance
|
|
334
|
+
of `SignerSession`, required by all signing endpoints, e.g.,
|
|
335
|
+
`signEvm`)
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
const oidcSession = await cubesigner.createOidcSession(oidcToken, org.id, ["sign:*"]);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
or a _management_ session (i.e., and instance of `CubeSigner`,
|
|
342
|
+
required by all management endpoints, e.g., retrieving user
|
|
343
|
+
information, configuring user MFA methods, etc.).
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
const oidcCubeSigner = new cs.CubeSigner({
|
|
347
|
+
sessionMgr: await cubesigner.createOidcManager(oidcToken, org.id, ["manage:*"]),
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
> **Info**
|
|
352
|
+
> For full details on how to use this SDK to create a CubeSigner
|
|
353
|
+
> account for a third-party user and then exchange a valid OIDC token
|
|
354
|
+
> CubeSigner session, check out the [OIDC
|
|
355
|
+
> Example](./examples/oidc/README.md).
|
|
356
|
+
|
|
357
|
+
### Set up TOTP for a user
|
|
358
|
+
|
|
359
|
+
To manage a user we need a management session bound to that user. It
|
|
360
|
+
doesn't matter if that user is native to CubeSigner or a third-party
|
|
361
|
+
OIDC user. For that purpose, in this section we are going to use the
|
|
362
|
+
previously created `oidcCubeSigner` instance.
|
|
363
|
+
|
|
364
|
+
To set up TOTP, we call the `resetTotp` method which creates a new
|
|
365
|
+
TOTP configuration for the users (overwriting the previous one, if
|
|
366
|
+
any) and returns a [TOTP
|
|
367
|
+
url](https://github.com/google/google-authenticator/wiki/Key-Uri-Format).
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
console.log(`Setting up TOTP for user ${me.email}`);
|
|
371
|
+
const totpResp = await oidcCubeSigner.resetTotp();
|
|
372
|
+
assert(totpResp.totp_url);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
From that url, we can generate a QR code to present to the user, or
|
|
376
|
+
create an authenticator for automated testing.
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { authenticator } from "otplib"; // npm install otplib@12.0.1
|
|
380
|
+
|
|
381
|
+
const totpSecret = new URL(totpResp.totp_url).searchParams.get("secret");
|
|
382
|
+
assert(totpSecret);
|
|
383
|
+
|
|
384
|
+
console.log(`Verifying current TOTP code`);
|
|
385
|
+
let code = authenticator.generate(totpSecret);
|
|
386
|
+
await oidcCubeSigner.verifyTotp(code);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
We can also check that the user's profile now indeed includes `Totp`
|
|
390
|
+
as one of the configured MFA factors.
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
const mfa = (await oidcCubeSigner.aboutMe()).mfa;
|
|
394
|
+
console.log("Configured MFA types", mfa);
|
|
395
|
+
assert(mfa.includes("Totp"));
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Configure MFA policy for signing
|
|
399
|
+
|
|
400
|
+
We've already discussed assigning a [security
|
|
401
|
+
policy](#set-security-policies) to a key; requiring multi-factor
|
|
402
|
+
authentication is another such policy.
|
|
403
|
+
|
|
404
|
+
Let's update our `secpKey` key to require an additional approval via
|
|
405
|
+
TOTP before anything may be signed with that key:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
console.log(`Require TOTP for key ${secpKey.materialId}`);
|
|
409
|
+
await secpKey.appendPolicy([{ RequireMfa: { count: 1, allowed_mfa_types: ["Totp"] } }]);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Now, when we call any signing operation on `secpKey`, we'll
|
|
413
|
+
receive `202 Accepted` instead of `200 Ok`. The response body contains
|
|
414
|
+
an MFA ID, which we can use to fetch and inspect the associated MFA
|
|
415
|
+
request, see how many approvals it requires, what kind of MFA factors
|
|
416
|
+
it allows, etc. Instead, since we know that our key requires TOTP, we
|
|
417
|
+
can just call `approveTotp` on the response and pass the current TOTP
|
|
418
|
+
code to it; if the code is correct, the call will succeed
|
|
419
|
+
and return the signature.
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
console.log(`Signing a transaction now requires TOTP`);
|
|
423
|
+
let resp = await oidcSession.signEvm(secpKeyStr, eth1Request);
|
|
424
|
+
assert(resp.requiresMfa());
|
|
425
|
+
|
|
426
|
+
console.log(`Approving with TOTP code`);
|
|
427
|
+
code = authenticator.generate(totpSecret);
|
|
428
|
+
resp = await resp.approveTotp(oidcSession, code);
|
|
429
|
+
assert(!resp.requiresMfa());
|
|
430
|
+
console.log(resp.data());
|
|
431
|
+
assert(resp.data().rlp_signed_tx);
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Clean up
|
|
435
|
+
|
|
436
|
+
Once we are done, we can revoke the signer session and delete the role
|
|
437
|
+
we created.
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
console.log("Cleaning up");
|
|
441
|
+
await session.sessionMgr.revoke();
|
|
442
|
+
await role.delete();
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
As of now, deleting keys is not supported.
|
|
446
|
+
|
|
447
|
+
## Building the SDK
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
npm install
|
|
451
|
+
npm run build
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Running the SDK tests
|
|
455
|
+
|
|
456
|
+
After [logging in](#logging-into-cubesigner), you can just run:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
npm test
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The tests will create some temporary keys and roles (in the
|
|
463
|
+
organization of the signed-in user), then sign some messages, and
|
|
464
|
+
finally clean (most of it) up.
|
|
465
|
+
|
|
466
|
+
## License
|
|
467
|
+
|
|
468
|
+
Copyright (C) 2022-2023 Cubist, Inc.
|
|
469
|
+
|
|
470
|
+
See the [NOTICE](NOTICE) file for licensing information.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|