@mysten/sui 2.19.0 → 2.20.1
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/CHANGELOG.md +25 -0
- package/dist/bcs/bcs.d.mts +6 -6
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
- package/dist/transactions/intents/CoinWithBalance.d.mts.map +1 -1
- package/dist/transactions/intents/CoinWithBalance.mjs +7 -3
- package/dist/transactions/intents/CoinWithBalance.mjs.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/dist/zklogin/bcs.d.mts +14 -14
- package/dist/zklogin/bcs.d.mts.map +1 -1
- package/dist/zklogin/index.d.mts +2 -1
- package/dist/zklogin/index.mjs +2 -1
- package/dist/zklogin/publickey.d.mts +4 -0
- package/dist/zklogin/publickey.d.mts.map +1 -1
- package/dist/zklogin/publickey.mjs +6 -0
- package/dist/zklogin/publickey.mjs.map +1 -1
- package/dist/zklogin/signer.d.mts +55 -0
- package/dist/zklogin/signer.d.mts.map +1 -0
- package/dist/zklogin/signer.mjs +68 -0
- package/dist/zklogin/signer.mjs.map +1 -0
- package/docs/cryptography/index.md +225 -0
- package/docs/cryptography/keypairs.md +36 -143
- package/docs/cryptography/signers/aws-kms.md +110 -0
- package/docs/cryptography/signers/gcp-kms.md +91 -0
- package/docs/cryptography/signers/index.md +78 -0
- package/docs/cryptography/signers/ledger.md +76 -0
- package/docs/cryptography/{multisig.md → signers/multisig.md} +2 -2
- package/docs/cryptography/{passkey.md → signers/passkey.md} +1 -1
- package/docs/cryptography/{webcrypto-signer.md → signers/webcrypto.md} +6 -6
- package/docs/index.md +4 -5
- package/docs/llms-index.md +9 -4
- package/docs/migrations/sui-2.0/index.md +14 -1
- package/docs/zklogin.md +2 -2
- package/package.json +1 -1
- package/src/transactions/intents/CoinWithBalance.ts +21 -6
- package/src/version.ts +1 -1
- package/src/zklogin/index.ts +2 -0
- package/src/zklogin/publickey.ts +7 -0
- package/src/zklogin/signer.ts +121 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Cryptography
|
|
2
|
+
|
|
3
|
+
> Sign and verify Sui transactions and messages with keypairs and external signers
|
|
4
|
+
|
|
5
|
+
A signature authorizes every transaction and personal message on Sui. The Sui TypeScript SDK exposes
|
|
6
|
+
a single abstraction for producing those signatures, the `Signer`, along with several
|
|
7
|
+
implementations of it, from in-process keypairs to hardware wallets and cloud key-management
|
|
8
|
+
services (KMS). This page walks through the concepts shared by every signer; the subpages cover each
|
|
9
|
+
implementation in detail.
|
|
10
|
+
|
|
11
|
+
## Signing schemes
|
|
12
|
+
|
|
13
|
+
A signer always produces a signature under a signature scheme, which determines the curve and
|
|
14
|
+
signature format. Sui supports three schemes, each with a corresponding `Keypair` class:
|
|
15
|
+
|
|
16
|
+
| Scheme | Class | Import |
|
|
17
|
+
| --------------- | ------------------ | -------------------------------- |
|
|
18
|
+
| Ed25519 | `Ed25519Keypair` | `@mysten/sui/keypairs/ed25519` |
|
|
19
|
+
| ECDSA Secp256k1 | `Secp256k1Keypair` | `@mysten/sui/keypairs/secp256k1` |
|
|
20
|
+
| ECDSA Secp256r1 | `Secp256r1Keypair` | `@mysten/sui/keypairs/secp256r1` |
|
|
21
|
+
|
|
22
|
+
For background on the schemes themselves, see the
|
|
23
|
+
[Signatures](https://docs.sui.io/concepts/cryptography/transaction-auth/signatures) concept topic. A
|
|
24
|
+
signer's scheme determines the format of its public key and the Sui address it derives.
|
|
25
|
+
|
|
26
|
+
## The Signer interface
|
|
27
|
+
|
|
28
|
+
`Signer` is an abstract class exported from `@mysten/sui/cryptography`. Every signer, whether it
|
|
29
|
+
holds the private key in memory, on a Ledger device, or in AWS KMS, implements the same interface,
|
|
30
|
+
so the rest of your code doesn't need to know where the key lives.
|
|
31
|
+
|
|
32
|
+
Implementations provide three primitives:
|
|
33
|
+
|
|
34
|
+
| Method | Description |
|
|
35
|
+
| ---------------- | ------------------------------------------------------------------ |
|
|
36
|
+
| `getPublicKey()` | Returns the `PublicKey` for deriving the address and verifying |
|
|
37
|
+
| `getKeyScheme()` | Returns the signature scheme (`ED25519`, `Secp256k1`, `Secp256r1`) |
|
|
38
|
+
| `sign(bytes)` | Signs raw bytes and returns the signature |
|
|
39
|
+
|
|
40
|
+
On top of those, `Signer` provides the methods you typically call:
|
|
41
|
+
|
|
42
|
+
| Method | Description |
|
|
43
|
+
| ----------------------------- | ------------------------------------------------ |
|
|
44
|
+
| `toSuiAddress()` | The Sui address derived from the public key |
|
|
45
|
+
| `signTransaction(bytes)` | Signs transaction bytes with the correct intent |
|
|
46
|
+
| `signPersonalMessage(bytes)` | Signs a personal message |
|
|
47
|
+
| `signAndExecuteTransaction()` | Signs and submits a transaction through a client |
|
|
48
|
+
|
|
49
|
+
There are two broad families of signer:
|
|
50
|
+
|
|
51
|
+
- **Key pairs** hold the private key in process. `Keypair` extends `Signer` and adds secret-key
|
|
52
|
+
import and export (`getSecretKey`, `fromSecretKey`). This is the easiest option for scripts,
|
|
53
|
+
backends, and tests. See [Key pairs](/sui/cryptography/keypairs).
|
|
54
|
+
- **External signers** keep the private key outside your application, in a KMS, on a hardware
|
|
55
|
+
wallet, or as a non-extractable browser key. They extend `Signer` directly. See
|
|
56
|
+
[Signers](/sui/cryptography/signers).
|
|
57
|
+
|
|
58
|
+
Because both families share the `Signer` interface, you can pass any signer wherever the SDK accepts
|
|
59
|
+
one, for example as the `signer` argument to a client's `signAndExecuteTransaction`.
|
|
60
|
+
|
|
61
|
+
## Public keys and addresses
|
|
62
|
+
|
|
63
|
+
Every signer exposes a `PublicKey` through `getPublicKey()`. You use the public key to derive the
|
|
64
|
+
signer's Sui address and to verify signatures:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
|
|
68
|
+
const keypair = new Ed25519Keypair();
|
|
69
|
+
|
|
70
|
+
// Derive the address directly from the signer...
|
|
71
|
+
const address = keypair.toSuiAddress();
|
|
72
|
+
|
|
73
|
+
// ...or from its public key
|
|
74
|
+
const publicKey = keypair.getPublicKey();
|
|
75
|
+
const sameAddress = publicKey.toSuiAddress();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Signing messages and transactions
|
|
79
|
+
|
|
80
|
+
The signing API is identical for every signer; only construction differs. Use `signPersonalMessage`
|
|
81
|
+
to sign arbitrary bytes and `signTransaction` to sign built transaction bytes:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const message = new TextEncoder().encode('hello world');
|
|
85
|
+
const { signature } = await keypair.signPersonalMessage(message);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Both `signPersonalMessage` and `signTransaction` resolve to `{ bytes, signature }`, where
|
|
89
|
+
`signature` is the serialized signature string used for execution and verification, and `bytes` is
|
|
90
|
+
the base64 payload that the signer signed.
|
|
91
|
+
|
|
92
|
+
To sign and submit a transaction in one step, pass the signer to a client:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
|
|
96
|
+
const client = new SuiGrpcClient({
|
|
97
|
+
network: 'testnet',
|
|
98
|
+
baseUrl: 'https://fullnode.testnet.sui.io:443',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const result = await client.signAndExecuteTransaction({
|
|
102
|
+
transaction,
|
|
103
|
+
signer: keypair,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// signAndExecuteTransaction resolves to a discriminated union, so check for failure first
|
|
107
|
+
if (result.FailedTransaction) {
|
|
108
|
+
throw new Error('Transaction failed to execute');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log('Digest:', result.Transaction.digest);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Verifying signatures
|
|
115
|
+
|
|
116
|
+
Verification confirms a signature is valid for a given message and that the expected key produced
|
|
117
|
+
it. The `@mysten/sui/verify` module verifies a signature against a message and recovers the
|
|
118
|
+
`PublicKey` that produced it, without needing the original signer.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
|
|
122
|
+
const keypair = new Ed25519Keypair();
|
|
123
|
+
const message = new TextEncoder().encode('hello world');
|
|
124
|
+
const { signature } = await keypair.signPersonalMessage(message);
|
|
125
|
+
|
|
126
|
+
const publicKey = await verifyPersonalMessageSignature(message, signature);
|
|
127
|
+
|
|
128
|
+
if (!publicKey.verifyAddress(keypair.toSuiAddress())) {
|
|
129
|
+
throw new Error('Signature was valid, but was signed by a different key pair');
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Verifying that a signature is valid for a specific address
|
|
134
|
+
|
|
135
|
+
`verifyPersonalMessageSignature` and `verifyTransactionSignature` accept an optional `address` and
|
|
136
|
+
throw if the signature is not valid for it:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
|
|
140
|
+
const keypair = new Ed25519Keypair();
|
|
141
|
+
const message = new TextEncoder().encode('hello world');
|
|
142
|
+
const { signature } = await keypair.signPersonalMessage(message);
|
|
143
|
+
|
|
144
|
+
await verifyPersonalMessageSignature(message, signature, {
|
|
145
|
+
address: keypair.toSuiAddress(),
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Verifying transaction signatures
|
|
150
|
+
|
|
151
|
+
Verifying transaction signatures works the same way, using `verifyTransactionSignature`:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
|
|
155
|
+
const client = new SuiGrpcClient({
|
|
156
|
+
network: 'testnet',
|
|
157
|
+
baseUrl: 'https://fullnode.testnet.sui.io:443',
|
|
158
|
+
});
|
|
159
|
+
const tx = new Transaction();
|
|
160
|
+
// ... add some transactions...
|
|
161
|
+
const bytes = await tx.build({ client });
|
|
162
|
+
|
|
163
|
+
const keypair = new Ed25519Keypair();
|
|
164
|
+
const { signature } = await keypair.signTransaction(bytes);
|
|
165
|
+
|
|
166
|
+
await verifyTransactionSignature(bytes, signature, {
|
|
167
|
+
// optionally verify that the signature is valid for a specific address
|
|
168
|
+
address: keypair.toSuiAddress(),
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Verifying zkLogin signatures
|
|
173
|
+
|
|
174
|
+
You can't verify zkLogin signatures purely on the client. When verifying a zkLogin signature, the
|
|
175
|
+
SDK uses the GraphQL API. This works for Mainnet signatures without any additional configuration.
|
|
176
|
+
For Testnet signatures, provide a Testnet GraphQL client:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
|
|
180
|
+
const publicKey = await verifyPersonalMessageSignature(message, zkSignature, {
|
|
181
|
+
client: new SuiGraphQLClient({
|
|
182
|
+
url: 'https://graphql.testnet.sui.io/graphql',
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
For some zkLogin accounts, there are two valid addresses for a given set of inputs, so comparing the
|
|
188
|
+
address from `publicKey.toSuiAddress()` directly against an expected address can fail. Instead, pass
|
|
189
|
+
the expected address during verification, or use `publicKey.verifyAddress(address)`:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
|
|
193
|
+
const publicKey = await verifyPersonalMessageSignature(message, zkSignature, {
|
|
194
|
+
client: new SuiGraphQLClient({
|
|
195
|
+
url: 'https://graphql.testnet.sui.io/graphql',
|
|
196
|
+
}),
|
|
197
|
+
// Throws if the signature is not valid for the provided address
|
|
198
|
+
address: '0x...expectedAddress',
|
|
199
|
+
});
|
|
200
|
+
// or
|
|
201
|
+
|
|
202
|
+
if (!publicKey.verifyAddress('0x...expectedAddress')) {
|
|
203
|
+
throw new Error('Signature was valid, but was signed by a different key pair');
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Both methods check the signature against the standard and
|
|
208
|
+
[legacy versions of the zkLogin address](https://sdk.mystenlabs.com/sui/zklogin#legacy-addresses).
|
|
209
|
+
|
|
210
|
+
## Choosing an implementation
|
|
211
|
+
|
|
212
|
+
- **[Key pairs](/sui/cryptography/keypairs)**: `Ed25519Keypair`, `Secp256k1Keypair`, and
|
|
213
|
+
`Secp256r1Keypair` hold the private key in process. The easiest option when you manage the secret
|
|
214
|
+
key yourself. Covers deriving keypairs from mnemonics and secret keys.
|
|
215
|
+
- **[Signers](/sui/cryptography/signers)**: other implementations of the `Signer` interface,
|
|
216
|
+
including signer packages that keep the private key outside your application — cloud KMS
|
|
217
|
+
([AWS](/sui/cryptography/signers/aws-kms), [GCP](/sui/cryptography/signers/gcp-kms)), a
|
|
218
|
+
[Ledger](/sui/cryptography/signers/ledger) hardware wallet, and non-extractable browser keys with
|
|
219
|
+
the [Web Crypto](/sui/cryptography/signers/webcrypto) signer — as well as
|
|
220
|
+
[passkeys](/sui/cryptography/signers/passkey) and
|
|
221
|
+
[multi-signature](/sui/cryptography/signers/multisig).
|
|
222
|
+
|
|
223
|
+
If you just need to sign in a script or server and are comfortable managing the secret key yourself,
|
|
224
|
+
start with [Key pairs](/sui/cryptography/keypairs). If the key must never live in your application's
|
|
225
|
+
memory, use one of the external [signers](/sui/cryptography/signers).
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# Key pairs
|
|
2
2
|
|
|
3
|
-
> Create
|
|
3
|
+
> Create Ed25519, Secp256k1, and Secp256r1 keypairs and derive them from mnemonics and secret keys
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
A `Keypair` holds a Sui private key in process and signs with it. It is the easiest
|
|
6
|
+
[`Signer`](/sui/cryptography) to use. It is ideal for scripts, backends, and tests where you manage
|
|
7
|
+
the secret key yourself. For the signing and verification API shared by all signers, see
|
|
8
|
+
[Cryptography](/sui/cryptography); this page focuses on creating keypairs and deriving them from
|
|
9
|
+
mnemonics and secret keys.
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
Each signing scheme has a corresponding `Keypair` class:
|
|
9
12
|
|
|
10
13
|
| Sign scheme | Class name | Import folder |
|
|
11
14
|
| --------------- | ------------------ | -------------------------------- |
|
|
@@ -13,9 +16,6 @@ The Sui TypeScript SDK supports three signing schemes:
|
|
|
13
16
|
| ECDSA Secp256k1 | `Secp256k1Keypair` | `@mysten/sui/keypairs/secp256k1` |
|
|
14
17
|
| ECDSA Secp256r1 | `Secp256r1Keypair` | `@mysten/sui/keypairs/secp256r1` |
|
|
15
18
|
|
|
16
|
-
For information on these schemes, see the
|
|
17
|
-
[Signatures](https://docs.sui.io/concepts/cryptography/transaction-auth/signatures) topic.
|
|
18
|
-
|
|
19
19
|
To use, import the key pair class your project uses from the `@mysten/sui/keypairs` folder. For
|
|
20
20
|
example, to use the Ed25519 scheme, import the `Ed25519Keypair` class from
|
|
21
21
|
`@mysten/sui/keypairs/ed25519`.
|
|
@@ -24,155 +24,55 @@ example, to use the Ed25519 scheme, import the `Ed25519Keypair` class from
|
|
|
24
24
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
## Creating a key pair
|
|
28
|
+
|
|
27
29
|
To create a random key pair (which identifies a Sui address), instantiate a new `Keypair` class. To
|
|
28
30
|
reference a key pair from an existing secret key, pass the secret to the `fromSecretKey` function.
|
|
29
31
|
|
|
30
32
|
```typescript
|
|
31
33
|
// random Keypair
|
|
32
34
|
const keypair = new Ed25519Keypair();
|
|
33
|
-
// Keypair from an existing secret key (Uint8Array)
|
|
34
|
-
const keypair = Ed25519Keypair.fromSecretKey(secretKey);
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
With your key pair created, you can reference it when performing actions on the network. For
|
|
38
|
-
example, you can use it to sign transactions, like the following code that creates and signs a
|
|
39
|
-
personal message using the public key from the key pair created in the previous code:
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
const publicKey = keypair.getPublicKey();
|
|
43
|
-
const message = new TextEncoder().encode('hello world');
|
|
44
35
|
|
|
45
|
-
|
|
46
|
-
const
|
|
36
|
+
// or, a Keypair from an existing secret key (Uint8Array)
|
|
37
|
+
const fromSecret = Ed25519Keypair.fromSecretKey(secretKey);
|
|
47
38
|
```
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Each `Keypair` has an associated `PublicKey` class. You use the public key to verify signatures or
|
|
52
|
-
to retrieve its associated Sui address. You can access a `Keypair` from its `PublicKey` or construct
|
|
53
|
-
it from the bytes (as a `Uint8Array`) of the `PublicKey`, as in the following code:
|
|
40
|
+
With your key pair created, you can derive its address and sign with it. The following signs a
|
|
41
|
+
personal message and verifies it with the public key:
|
|
54
42
|
|
|
55
43
|
```typescript
|
|
56
|
-
|
|
57
|
-
const keypair = new Ed25519Keypair();
|
|
58
|
-
|
|
59
|
-
// method 1
|
|
60
|
-
const bytes = keypair.getPublicKey().toRawBytes();
|
|
61
|
-
const publicKey = new Ed25519PublicKey(bytes);
|
|
62
|
-
const address = publicKey.toSuiAddress();
|
|
63
|
-
|
|
64
|
-
// method 2
|
|
65
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
66
|
-
// or use `toSuiAddress()` directly
|
|
44
|
+
const publicKey = keypair.getPublicKey();
|
|
67
45
|
const address = keypair.toSuiAddress();
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Verifying signatures without a key pair
|
|
71
|
-
|
|
72
|
-
When you have an existing public key, you can use it to verify a signature. Verification ensures the
|
|
73
|
-
signature is valid for the provided message and is signed with the appropriate secret key.
|
|
74
46
|
|
|
75
|
-
The following code creates a key pair in the Ed25519 scheme, creates and signs a message with it,
|
|
76
|
-
then verifies the message to retrieve the public key. The code then uses `toSuiAddress()` to check
|
|
77
|
-
if the address associated with the public key matches the address that the key pair defines.
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
|
|
81
|
-
const keypair = new Ed25519Keypair();
|
|
82
47
|
const message = new TextEncoder().encode('hello world');
|
|
83
48
|
const { signature } = await keypair.signPersonalMessage(message);
|
|
84
|
-
|
|
85
|
-
const publicKey = await verifyPersonalMessageSignature(message, signature);
|
|
86
|
-
|
|
87
|
-
if (!publicKey.verifyAddress(keypair.toSuiAddress())) {
|
|
88
|
-
throw new Error('Signature was valid, but was signed by a different key pair');
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Verifying that a signature is valid for a specific address
|
|
93
|
-
|
|
94
|
-
`verifyPersonalMessageSignature` and `verifyTransactionSignature` accept an optional `address`, and
|
|
95
|
-
will throw an error if the signature is not valid for the provided address.
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
|
|
99
|
-
const keypair = new Ed25519Keypair();
|
|
100
|
-
const message = new TextEncoder().encode('hello world');
|
|
101
|
-
const { signature } = await keypair.signPersonalMessage(message);
|
|
102
|
-
|
|
103
|
-
await verifyPersonalMessageSignature(message, signature, {
|
|
104
|
-
address: keypair.toSuiAddress(),
|
|
105
|
-
});
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Verifying transaction signatures
|
|
109
|
-
|
|
110
|
-
Verifying transaction signatures is similar to personal message signature verification, except you
|
|
111
|
-
use `verifyTransactionSignature`:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// import SuiGrpcClient to create a network client
|
|
115
|
-
|
|
116
|
-
// create a client connected to testnet
|
|
117
|
-
const client = new SuiGrpcClient({
|
|
118
|
-
network: 'testnet',
|
|
119
|
-
baseUrl: 'https://fullnode.testnet.sui.io:443',
|
|
120
|
-
});
|
|
121
|
-
const tx = new Transaction();
|
|
122
|
-
// ... add some transactions...
|
|
123
|
-
const bytes = await tx.build({ client });
|
|
124
|
-
|
|
125
|
-
const keypair = new Ed25519Keypair();
|
|
126
|
-
const { signature } = await keypair.signTransaction(bytes);
|
|
127
|
-
|
|
128
|
-
await verifyTransactionSignature(bytes, signature, {
|
|
129
|
-
// optionally verify that the signature is valid for a specific address
|
|
130
|
-
address: keypair.toSuiAddress(),
|
|
131
|
-
});
|
|
49
|
+
const isValid = await publicKey.verifyPersonalMessage(message, signature);
|
|
132
50
|
```
|
|
133
51
|
|
|
134
|
-
|
|
52
|
+
See [Cryptography](/sui/cryptography) for the full signing and verification API, including
|
|
53
|
+
[verifying signatures](/sui/cryptography#verifying-signatures) without the original key pair.
|
|
135
54
|
|
|
136
|
-
|
|
137
|
-
SDK uses the GraphQL API to verify the signature. This works for Mainnet signatures without any
|
|
138
|
-
additional configuration.
|
|
55
|
+
## Public keys
|
|
139
56
|
|
|
140
|
-
|
|
57
|
+
Each `Keypair` has an associated `PublicKey`, which you use to verify signatures or to retrieve its
|
|
58
|
+
Sui address. Access it from a `Keypair` with `getPublicKey()`. To recreate a keypair, and therefore
|
|
59
|
+
its public key, from saved key material, reconstruct it from the secret key:
|
|
141
60
|
|
|
142
61
|
```typescript
|
|
143
62
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
url: 'https://graphql.testnet.sui.io/graphql',
|
|
147
|
-
}),
|
|
148
|
-
});
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
For some zklogin accounts, there are two valid addresses for a given set of inputs. This means you
|
|
152
|
-
might run into issues if you try to compare the address returned by `publicKey.toSuiAddress()`
|
|
153
|
-
directly with an expected address.
|
|
154
|
-
|
|
155
|
-
Instead, you can either pass in the expected address during verification, or use the
|
|
156
|
-
`publicKey.verifyAddress(address)` method:
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
63
|
+
// Recreate the keypair from its secret key (Uint8Array)
|
|
64
|
+
const keypair = Ed25519Keypair.fromSecretKey(secretKey);
|
|
159
65
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
address: '0x...expectedAddress',
|
|
166
|
-
});
|
|
167
|
-
// or
|
|
168
|
-
|
|
169
|
-
if (!publicKey.verifyAddress('0x...expectedAddress')) {
|
|
170
|
-
throw new Error('Signature was valid, but was signed by a different key pair');
|
|
171
|
-
}
|
|
66
|
+
// Access the public key and derive the address
|
|
67
|
+
const publicKey = keypair.getPublicKey();
|
|
68
|
+
const address = publicKey.toSuiAddress();
|
|
69
|
+
// or derive the address directly from the keypair
|
|
70
|
+
const sameAddress = keypair.toSuiAddress();
|
|
172
71
|
```
|
|
173
72
|
|
|
174
|
-
|
|
175
|
-
[
|
|
73
|
+
See
|
|
74
|
+
[Deriving a `Keypair` from a Bech32 encoded secret key](#deriving-a-keypair-from-a-bech32-encoded-secret-key)
|
|
75
|
+
for working with encoded secret-key formats.
|
|
176
76
|
|
|
177
77
|
## Deriving a key pair from a mnemonic
|
|
178
78
|
|
|
@@ -199,14 +99,16 @@ const keypair = Ed25519Keypair.fromSecretKey(secretKey);
|
|
|
199
99
|
```
|
|
200
100
|
|
|
201
101
|
You can also use the `decodeSuiPrivateKey` function to decode the secret key and determine the
|
|
202
|
-
keyScheme, and get the
|
|
203
|
-
`Keypair` class.
|
|
102
|
+
keyScheme, and get the key pair's private key bytes, which you can use to instantiate the
|
|
103
|
+
appropriate `Keypair` class.
|
|
204
104
|
|
|
205
105
|
```typescript
|
|
206
106
|
|
|
107
|
+
const encoded = 'suiprivkey1qzse89atw7d3zum8ujep76d2cxmgduyuast0y9fu23xcl0mpafgkktllhyc';
|
|
207
108
|
const { scheme, secretKey } = decodeSuiPrivateKey(encoded);
|
|
208
109
|
|
|
209
110
|
// use scheme to choose the correct key pair
|
|
111
|
+
let keypair;
|
|
210
112
|
switch (scheme) {
|
|
211
113
|
case 'ED25519':
|
|
212
114
|
keypair = Ed25519Keypair.fromSecretKey(secretKey);
|
|
@@ -225,16 +127,7 @@ switch (scheme) {
|
|
|
225
127
|
See [SIP-15](https://github.com/sui-foundation/sips/blob/main/sips/sip-15.md) for additional context
|
|
226
128
|
and motivation.
|
|
227
129
|
|
|
228
|
-
|
|
229
|
-
to directly derive the keypair from the secret key.
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
const secretKey = 'suiprivkey1qzse89atw7d3zum8ujep76d2cxmgduyuast0y9fu23xcl0mpafgkktllhyc';
|
|
233
|
-
|
|
234
|
-
const keypair = Ed25519Keypair.fromSecretKey(secretKey);
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
You can also export a keypair to a Bech32 encoded secret key using the `getSecretKey` method.
|
|
130
|
+
You can export a keypair to a Bech32 encoded secret key using the `getSecretKey` method.
|
|
238
131
|
|
|
239
132
|
```typescript
|
|
240
133
|
const secretKey = keypair.getSecretKey();
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# AWS KMS Signer
|
|
2
|
+
|
|
3
|
+
> Sign Sui transactions with a key stored in AWS Key Management Service
|
|
4
|
+
|
|
5
|
+
The `AwsKmsSigner` signs Sui transactions using a key held in
|
|
6
|
+
[AWS Key Management Service](https://aws.amazon.com/kms/). The private key never leaves AWS; the
|
|
7
|
+
signer sends the message to KMS and receives the signature back.
|
|
8
|
+
|
|
9
|
+
AWS KMS supports the `Ed25519`, `Secp256k1`, and `Secp256r1` schemes. The curve of the KMS key
|
|
10
|
+
determines the signature scheme (`ECC_NIST_EDWARDS25519` → `Ed25519`, `ECC_SECG_P256K1` →
|
|
11
|
+
`Secp256k1`, `ECC_NIST_P256` → `Secp256r1`). `Ed25519` is Sui's native scheme.
|
|
12
|
+
|
|
13
|
+
> **Warning:** The AWS KMS Signer requires Node.js >= 22 (the package's `engines.node` constraint) and relies on
|
|
14
|
+
> the global Web Crypto API.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```sh npm2yarn
|
|
19
|
+
npm i @mysten/aws-kms-signer
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Creating a signer
|
|
23
|
+
|
|
24
|
+
Construct the signer with `AwsKmsSigner.fromKeyId`, passing the KMS key ID and the AWS credentials
|
|
25
|
+
and region. This is async: it fetches the public key from KMS so the signer can derive the Sui
|
|
26
|
+
address.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
|
|
30
|
+
const { AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_KMS_KEY_ID } = process.env;
|
|
31
|
+
|
|
32
|
+
const signer = await AwsKmsSigner.fromKeyId(AWS_KMS_KEY_ID, {
|
|
33
|
+
region: AWS_REGION,
|
|
34
|
+
accessKeyId: AWS_ACCESS_KEY_ID,
|
|
35
|
+
secretAccessKey: AWS_SECRET_ACCESS_KEY,
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Parameters
|
|
40
|
+
|
|
41
|
+
`fromKeyId(keyId, options)`
|
|
42
|
+
|
|
43
|
+
| Parameter | Type | Description |
|
|
44
|
+
| ------------------------- | ----------------------- | ---------------------------------------------------------------- |
|
|
45
|
+
| `keyId` | `string` | The AWS KMS key ID |
|
|
46
|
+
| `options.region` | `string` | The AWS region the key lives in |
|
|
47
|
+
| `options.accessKeyId` | `string` | The AWS access key ID (omit when using `credentials`) |
|
|
48
|
+
| `options.secretAccessKey` | `string` | The AWS secret access key (omit when using `credentials`) |
|
|
49
|
+
| `options.credentials` | `AwsCredentialProvider` | Optional async provider resolved before each request (see below) |
|
|
50
|
+
|
|
51
|
+
You must supply either static `accessKeyId`/`secretAccessKey` **or** a `credentials` provider.
|
|
52
|
+
|
|
53
|
+
### Using a credential provider
|
|
54
|
+
|
|
55
|
+
Instead of static keys, pass an async `credentials` provider (any function returning
|
|
56
|
+
`{ accessKeyId, secretAccessKey, sessionToken? }`). This is structurally compatible with the
|
|
57
|
+
providers from
|
|
58
|
+
[`@aws-sdk/credential-providers`](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html),
|
|
59
|
+
so the standard AWS credential chain (SSO, IAM roles, and container/instance metadata) works out of
|
|
60
|
+
the box. Credentials are resolved before each request, so temporary credentials refresh
|
|
61
|
+
automatically once their session nears expiry.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
|
|
65
|
+
const signer = await AwsKmsSigner.fromKeyId(process.env.AWS_KMS_KEY_ID, {
|
|
66
|
+
region: process.env.AWS_REGION,
|
|
67
|
+
credentials: fromNodeProviderChain(),
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`@aws-sdk/credential-providers` is **not** a dependency of this package, so install it yourself if
|
|
72
|
+
you want the AWS-provided resolvers. This keeps the signer lightweight for edge, browser, and Worker
|
|
73
|
+
runtimes, where you can instead pass static credentials or a custom resolver.
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
Once created, the signer behaves like any other [`Signer`](/sui/cryptography): derive the address,
|
|
78
|
+
sign messages, and sign or execute transactions.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Derive the Sui address
|
|
82
|
+
const address = signer.getPublicKey().toSuiAddress();
|
|
83
|
+
|
|
84
|
+
// Sign a personal message
|
|
85
|
+
const message = new TextEncoder().encode('Hello, AWS KMS Signer!');
|
|
86
|
+
const { signature } = await signer.signPersonalMessage(message);
|
|
87
|
+
|
|
88
|
+
// Verify the signature
|
|
89
|
+
const isValid = await signer.getPublicKey().verifyPersonalMessage(message, signature);
|
|
90
|
+
console.log(isValid); // true
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
To sign and submit a transaction, pass the signer to a client:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
|
|
97
|
+
const client = new SuiGrpcClient({
|
|
98
|
+
network: 'testnet',
|
|
99
|
+
baseUrl: 'https://fullnode.testnet.sui.io:443',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const result = await client.signAndExecuteTransaction({ transaction, signer });
|
|
103
|
+
if (result.FailedTransaction) {
|
|
104
|
+
throw new Error('Transaction failed to execute');
|
|
105
|
+
}
|
|
106
|
+
console.log(result.Transaction.digest);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
See [Cryptography](/sui/cryptography) for the full signing and verification API shared by all
|
|
110
|
+
signers.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# GCP KMS Signer
|
|
2
|
+
|
|
3
|
+
> Sign Sui transactions with a key stored in Google Cloud Key Management Service
|
|
4
|
+
|
|
5
|
+
The `GcpKmsSigner` signs Sui transactions using a key held in
|
|
6
|
+
[Google Cloud KMS](https://cloud.google.com/kms). The private key never leaves GCP; the signer sends
|
|
7
|
+
the message digest to KMS and receives the signature back.
|
|
8
|
+
|
|
9
|
+
GCP KMS supports the `Secp256k1` and `Secp256r1` schemes. The curve of the KMS key determines the
|
|
10
|
+
signature scheme (`EC_SIGN_SECP256K1_SHA256` → `Secp256k1`, `EC_SIGN_P256_SHA256` → `Secp256r1`).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```sh npm2yarn
|
|
15
|
+
npm i @mysten/gcp-kms-signer
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Creating a signer
|
|
19
|
+
|
|
20
|
+
Construct the signer with `GcpKmsSigner.fromOptions`, identifying the key by its project, location,
|
|
21
|
+
key ring, key, and version. This is async: it fetches the public key from KMS so the signer can
|
|
22
|
+
derive the Sui address.
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
|
|
26
|
+
const signer = await GcpKmsSigner.fromOptions({
|
|
27
|
+
projectId: 'your-google-project-id',
|
|
28
|
+
location: 'your-google-location',
|
|
29
|
+
keyRing: 'your-google-keyring',
|
|
30
|
+
cryptoKey: 'your-google-key-name',
|
|
31
|
+
cryptoKeyVersion: 'your-google-key-version',
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Parameters
|
|
36
|
+
|
|
37
|
+
`fromOptions(options)`
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Description |
|
|
40
|
+
| ------------------ | -------- | --------------------- |
|
|
41
|
+
| `projectId` | `string` | The GCP project ID |
|
|
42
|
+
| `location` | `string` | The GCP location |
|
|
43
|
+
| `keyRing` | `string` | The GCP key ring name |
|
|
44
|
+
| `cryptoKey` | `string` | The GCP key name |
|
|
45
|
+
| `cryptoKeyVersion` | `string` | The GCP key version |
|
|
46
|
+
|
|
47
|
+
If you already have a fully-qualified KMS version name, construct the signer directly with
|
|
48
|
+
`GcpKmsSigner.fromVersionName`:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const signer = await GcpKmsSigner.fromVersionName(
|
|
52
|
+
'projects/p/locations/l/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1',
|
|
53
|
+
);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
Once created, the signer behaves like any other [`Signer`](/sui/cryptography): derive the address,
|
|
59
|
+
sign messages, and sign or execute transactions.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Derive the Sui address
|
|
63
|
+
const address = signer.getPublicKey().toSuiAddress();
|
|
64
|
+
|
|
65
|
+
// Sign a personal message
|
|
66
|
+
const message = new TextEncoder().encode('Hello, GCP KMS Signer!');
|
|
67
|
+
const { signature } = await signer.signPersonalMessage(message);
|
|
68
|
+
|
|
69
|
+
// Verify the signature
|
|
70
|
+
const isValid = await signer.getPublicKey().verifyPersonalMessage(message, signature);
|
|
71
|
+
console.log(isValid); // true
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
To sign and submit a transaction, pass the signer to a client:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
|
|
78
|
+
const client = new SuiGrpcClient({
|
|
79
|
+
network: 'testnet',
|
|
80
|
+
baseUrl: 'https://fullnode.testnet.sui.io:443',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = await client.signAndExecuteTransaction({ transaction, signer });
|
|
84
|
+
if (result.FailedTransaction) {
|
|
85
|
+
throw new Error('Transaction failed to execute');
|
|
86
|
+
}
|
|
87
|
+
console.log(result.Transaction.digest);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See [Cryptography](/sui/cryptography) for the full signing and verification API shared by all
|
|
91
|
+
signers.
|