@solana-mobile/mobile-wallet-adapter-protocol 2.0.1 → 2.0.2
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/android/build.gradle +3 -3
- package/android/src/main/java/com/solanamobile/mobilewalletadapter/reactnative/SolanaMobileWalletAdapterModule.kt +17 -15
- package/lib/cjs/index.browser.js +290 -81
- package/lib/cjs/index.js +290 -81
- package/lib/cjs/index.native.js +180 -28
- package/lib/esm/index.browser.js +288 -82
- package/lib/esm/index.js +288 -82
- package/lib/types/index.browser.d.ts +68 -5
- package/lib/types/index.browser.d.ts.map +1 -1
- package/lib/types/index.d.ts +68 -5
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.native.d.ts +68 -5
- package/lib/types/index.native.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/__forks__/react-native/base64Utils.ts +1 -0
- package/src/__forks__/react-native/transact.ts +92 -106
- package/src/base64Utils.ts +3 -0
- package/src/createMobileWalletProxy.ts +175 -0
- package/src/createSIWSMessage.ts +14 -0
- package/src/encryptedMessage.ts +60 -0
- package/src/errors.ts +95 -93
- package/src/getAssociateAndroidIntentURL.ts +57 -52
- package/src/jsonRpcMessage.ts +38 -81
- package/src/parseHelloRsp.ts +46 -44
- package/src/parseSessionProps.ts +33 -0
- package/src/transact.ts +266 -268
- package/src/types.ts +74 -4
package/src/jsonRpcMessage.ts
CHANGED
|
@@ -1,81 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { SolanaMobileWalletAdapterProtocolError } from './errors.js';
|
|
3
|
-
import { SharedSecret } from './parseHelloRsp.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
new
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return response;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function decryptJsonRpcMessage<TMessage>(message: ArrayBuffer, sharedSecret: SharedSecret) {
|
|
43
|
-
const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
|
|
44
|
-
const initializationVector = message.slice(
|
|
45
|
-
SEQUENCE_NUMBER_BYTES,
|
|
46
|
-
SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES,
|
|
47
|
-
);
|
|
48
|
-
const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
|
|
49
|
-
const plaintextBuffer = await crypto.subtle.decrypt(
|
|
50
|
-
getAlgorithmParams(sequenceNumberVector, initializationVector),
|
|
51
|
-
sharedSecret,
|
|
52
|
-
ciphertext,
|
|
53
|
-
);
|
|
54
|
-
const plaintext = getUtf8Decoder().decode(plaintextBuffer);
|
|
55
|
-
const jsonRpcMessage = JSON.parse(plaintext);
|
|
56
|
-
if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
|
|
57
|
-
throw new SolanaMobileWalletAdapterProtocolError<typeof jsonRpcMessage.error.code>(
|
|
58
|
-
jsonRpcMessage.id,
|
|
59
|
-
jsonRpcMessage.error.code,
|
|
60
|
-
jsonRpcMessage.error.message,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
return jsonRpcMessage as JSONRPCResponse<TMessage>;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function getAlgorithmParams(sequenceNumber: ArrayBuffer, initializationVector: ArrayBuffer) {
|
|
67
|
-
return {
|
|
68
|
-
additionalData: sequenceNumber,
|
|
69
|
-
iv: initializationVector,
|
|
70
|
-
name: 'AES-GCM',
|
|
71
|
-
tagLength: 128, // 16 byte tag => 128 bits
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let _utf8Decoder: TextDecoder | undefined;
|
|
76
|
-
function getUtf8Decoder() {
|
|
77
|
-
if (_utf8Decoder === undefined) {
|
|
78
|
-
_utf8Decoder = new TextDecoder('utf-8');
|
|
79
|
-
}
|
|
80
|
-
return _utf8Decoder;
|
|
81
|
-
}
|
|
1
|
+
import { decryptMessage, encryptMessage } from './encryptedMessage.js';
|
|
2
|
+
import { SolanaMobileWalletAdapterProtocolError } from './errors.js';
|
|
3
|
+
import { SharedSecret } from './parseHelloRsp.js';
|
|
4
|
+
|
|
5
|
+
interface JSONRPCRequest<TParams> {
|
|
6
|
+
id: number;
|
|
7
|
+
jsonrpc: '2.0';
|
|
8
|
+
method: string;
|
|
9
|
+
params: TParams;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type JSONRPCResponse<TMessage> = {
|
|
13
|
+
id: number;
|
|
14
|
+
jsonrpc: '2.0';
|
|
15
|
+
result: TMessage;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export async function encryptJsonRpcMessage<TParams>(
|
|
19
|
+
jsonRpcMessage: JSONRPCRequest<TParams>,
|
|
20
|
+
sharedSecret: SharedSecret,
|
|
21
|
+
) {
|
|
22
|
+
const plaintext = JSON.stringify(jsonRpcMessage);
|
|
23
|
+
const sequenceNumber = jsonRpcMessage.id;
|
|
24
|
+
return encryptMessage(plaintext, sequenceNumber, sharedSecret);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function decryptJsonRpcMessage<TMessage>(message: ArrayBuffer, sharedSecret: SharedSecret) {
|
|
28
|
+
const plaintext = await decryptMessage(message, sharedSecret);
|
|
29
|
+
const jsonRpcMessage = JSON.parse(plaintext);
|
|
30
|
+
if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
|
|
31
|
+
throw new SolanaMobileWalletAdapterProtocolError<typeof jsonRpcMessage.error.code>(
|
|
32
|
+
jsonRpcMessage.id,
|
|
33
|
+
jsonRpcMessage.error.code,
|
|
34
|
+
jsonRpcMessage.error.message,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return jsonRpcMessage as JSONRPCResponse<TMessage>;
|
|
38
|
+
}
|
package/src/parseHelloRsp.ts
CHANGED
|
@@ -1,44 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
import { ENCODED_PUBLIC_KEY_LENGTH_BYTES } from "./encryptedMessage";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A secret agreed upon by the app and the wallet. Used as
|
|
5
|
+
* a symmetric key to encrypt and decrypt messages over an
|
|
6
|
+
* unsecured channel.
|
|
7
|
+
*/
|
|
8
|
+
export type SharedSecret = CryptoKey;
|
|
9
|
+
|
|
10
|
+
export default async function parseHelloRsp(
|
|
11
|
+
payloadBuffer: ArrayBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
|
|
12
|
+
associationPublicKey: CryptoKey,
|
|
13
|
+
ecdhPrivateKey: CryptoKey,
|
|
14
|
+
): Promise<SharedSecret> {
|
|
15
|
+
const [associationPublicKeyBuffer, walletPublicKey] = await Promise.all([
|
|
16
|
+
crypto.subtle.exportKey('raw', associationPublicKey),
|
|
17
|
+
crypto.subtle.importKey(
|
|
18
|
+
'raw',
|
|
19
|
+
payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES),
|
|
20
|
+
{ name: 'ECDH', namedCurve: 'P-256' },
|
|
21
|
+
false /* extractable */,
|
|
22
|
+
[] /* keyUsages */,
|
|
23
|
+
),
|
|
24
|
+
]);
|
|
25
|
+
const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
|
|
26
|
+
const ecdhSecretKey = await crypto.subtle.importKey(
|
|
27
|
+
'raw',
|
|
28
|
+
sharedSecret,
|
|
29
|
+
'HKDF',
|
|
30
|
+
false /* extractable */,
|
|
31
|
+
['deriveKey'] /* keyUsages */,
|
|
32
|
+
);
|
|
33
|
+
const aesKeyMaterialVal = await crypto.subtle.deriveKey(
|
|
34
|
+
{
|
|
35
|
+
name: 'HKDF',
|
|
36
|
+
hash: 'SHA-256',
|
|
37
|
+
salt: new Uint8Array(associationPublicKeyBuffer),
|
|
38
|
+
info: new Uint8Array(),
|
|
39
|
+
},
|
|
40
|
+
ecdhSecretKey,
|
|
41
|
+
{ name: 'AES-GCM', length: 128 },
|
|
42
|
+
false /* extractable */,
|
|
43
|
+
['encrypt', 'decrypt'],
|
|
44
|
+
);
|
|
45
|
+
return aesKeyMaterialVal as SharedSecret;
|
|
46
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { decryptMessage } from "./encryptedMessage";
|
|
2
|
+
import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode } from "./errors";
|
|
3
|
+
import { SharedSecret } from "./parseHelloRsp";
|
|
4
|
+
import { ProtocolVersion, SessionProperties } from "./types";
|
|
5
|
+
|
|
6
|
+
export default async function parseSessionProps(
|
|
7
|
+
message: ArrayBuffer,
|
|
8
|
+
sharedSecret: SharedSecret
|
|
9
|
+
): Promise<SessionProperties> {
|
|
10
|
+
const plaintext = await decryptMessage(message, sharedSecret);
|
|
11
|
+
const jsonProperties = JSON.parse(plaintext);
|
|
12
|
+
let protocolVersion: ProtocolVersion = 'legacy';
|
|
13
|
+
if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
|
|
14
|
+
switch (jsonProperties.v) {
|
|
15
|
+
case 1:
|
|
16
|
+
case '1':
|
|
17
|
+
case 'v1':
|
|
18
|
+
protocolVersion = 'v1'
|
|
19
|
+
break;
|
|
20
|
+
case 'legacy':
|
|
21
|
+
protocolVersion = 'legacy'
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
throw new SolanaMobileWalletAdapterError(
|
|
25
|
+
SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION,
|
|
26
|
+
`Unknown/unsupported protocol version: ${jsonProperties.v}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return <SessionProperties>({
|
|
31
|
+
protocol_version: protocolVersion
|
|
32
|
+
})
|
|
33
|
+
}
|