@msssystems/mss-link-sdk 0.1.5 → 0.1.6
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/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/media-crypto.contracts.d.ts +28 -0
- package/dist/client/media-crypto.contracts.js +1 -0
- package/dist/client/media-message-metadata.js +37 -0
- package/dist/client/media-message.contracts.d.ts +16 -0
- package/dist/client/mss-link-browser-client.d.ts +3 -0
- package/dist/client/mss-link-browser-client.js +70 -0
- package/dist/client/wasm-client.types.d.ts +3 -0
- package/package.json +1 -1
package/dist/client/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './client-options';
|
|
|
3
3
|
export * from './decryption-errors';
|
|
4
4
|
export * from './local-crypto-health';
|
|
5
5
|
export * from './local-crypto-health.contracts';
|
|
6
|
+
export * from './media-crypto.contracts';
|
|
6
7
|
export * from './media-message.contracts';
|
|
7
8
|
export * from './media-message-metadata';
|
|
8
9
|
export * from './message.contracts';
|
package/dist/client/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from './client-options';
|
|
|
3
3
|
export * from './decryption-errors';
|
|
4
4
|
export * from './local-crypto-health';
|
|
5
5
|
export * from './local-crypto-health.contracts';
|
|
6
|
+
export * from './media-crypto.contracts';
|
|
6
7
|
export * from './media-message.contracts';
|
|
7
8
|
export * from './media-message-metadata';
|
|
8
9
|
export * from './message.contracts';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MediaMessageAttachmentEncryptionInput, MediaMessageAttachmentInput, MediaMessageAttachmentMetadata } from './media-message.contracts';
|
|
2
|
+
export interface EncryptMediaBlobInput {
|
|
3
|
+
assetId: string;
|
|
4
|
+
blob: Blob;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
size: string | number;
|
|
7
|
+
originalName?: string;
|
|
8
|
+
uploadedAt?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface EncryptedMediaBlobResult {
|
|
11
|
+
encryptedBlob: Blob;
|
|
12
|
+
attachment: MediaMessageAttachmentInput;
|
|
13
|
+
encryption: MediaMessageAttachmentEncryptionInput;
|
|
14
|
+
}
|
|
15
|
+
export interface DecryptMediaBlobInput {
|
|
16
|
+
encryptedBlob: Blob;
|
|
17
|
+
metadata: MediaMessageAttachmentMetadata;
|
|
18
|
+
}
|
|
19
|
+
export interface DecryptedMediaBlobResult {
|
|
20
|
+
blob: Blob;
|
|
21
|
+
fileName?: string;
|
|
22
|
+
mimeType: string;
|
|
23
|
+
}
|
|
24
|
+
export interface WasmEncryptedMediaBytes {
|
|
25
|
+
ciphertext: Uint8Array;
|
|
26
|
+
key: string;
|
|
27
|
+
nonce: string;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -22,6 +22,9 @@ export function serializeMediaMessageMetadata(payload) {
|
|
|
22
22
|
? { originalName: attachment.originalName }
|
|
23
23
|
: {}),
|
|
24
24
|
...(attachment.uploadedAt ? { uploadedAt: attachment.uploadedAt } : {}),
|
|
25
|
+
...(attachment.encryption
|
|
26
|
+
? { encryption: serializeAttachmentEncryption(attachment.encryption) }
|
|
27
|
+
: {}),
|
|
25
28
|
})),
|
|
26
29
|
...(payload.caption ? { caption: payload.caption } : {}),
|
|
27
30
|
});
|
|
@@ -52,5 +55,39 @@ function normalizeAttachment(attachment) {
|
|
|
52
55
|
...(attachment.uploadedAt?.trim()
|
|
53
56
|
? { uploadedAt: attachment.uploadedAt.trim() }
|
|
54
57
|
: {}),
|
|
58
|
+
...(attachment.encryption
|
|
59
|
+
? { encryption: normalizeAttachmentEncryption(attachment.encryption) }
|
|
60
|
+
: {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function normalizeAttachmentEncryption(encryption) {
|
|
64
|
+
const keyId = encryption.keyId?.trim();
|
|
65
|
+
const nonce = encryption.nonce?.trim();
|
|
66
|
+
const wrappedKey = encryption.wrappedKey?.trim();
|
|
67
|
+
const algorithm = encryption.algorithm ?? 'AES-256-GCM';
|
|
68
|
+
if (algorithm !== 'AES-256-GCM') {
|
|
69
|
+
throw new Error('Unsupported media encryption algorithm.');
|
|
70
|
+
}
|
|
71
|
+
if (!nonce) {
|
|
72
|
+
throw new Error('Media encryption nonce is required.');
|
|
73
|
+
}
|
|
74
|
+
if (!wrappedKey) {
|
|
75
|
+
throw new Error('Media encryption wrappedKey is required.');
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
schema: 'mss.link.media-encryption.v1',
|
|
79
|
+
algorithm,
|
|
80
|
+
...(keyId ? { keyId } : {}),
|
|
81
|
+
nonce,
|
|
82
|
+
wrappedKey,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function serializeAttachmentEncryption(encryption) {
|
|
86
|
+
return {
|
|
87
|
+
schema: 'mss.link.media-encryption.v1',
|
|
88
|
+
algorithm: encryption.algorithm,
|
|
89
|
+
...(encryption.keyId ? { keyId: encryption.keyId } : {}),
|
|
90
|
+
nonce: encryption.nonce,
|
|
91
|
+
wrappedKey: encryption.wrappedKey,
|
|
55
92
|
};
|
|
56
93
|
}
|
|
@@ -4,6 +4,14 @@ export interface MediaMessageAttachmentInput {
|
|
|
4
4
|
size: string | number;
|
|
5
5
|
originalName?: string;
|
|
6
6
|
uploadedAt?: string;
|
|
7
|
+
encryption?: MediaMessageAttachmentEncryptionInput;
|
|
8
|
+
}
|
|
9
|
+
export interface MediaMessageAttachmentEncryptionInput {
|
|
10
|
+
schema?: 'mss.link.media-encryption.v1';
|
|
11
|
+
keyId?: string;
|
|
12
|
+
nonce?: string;
|
|
13
|
+
wrappedKey?: string;
|
|
14
|
+
algorithm?: 'AES-256-GCM';
|
|
7
15
|
}
|
|
8
16
|
export interface SendMediaMessageParams {
|
|
9
17
|
chatId: string;
|
|
@@ -17,6 +25,14 @@ export interface MediaMessageAttachmentMetadata {
|
|
|
17
25
|
size: string;
|
|
18
26
|
originalName?: string;
|
|
19
27
|
uploadedAt?: string;
|
|
28
|
+
encryption?: MediaMessageAttachmentEncryptionMetadata;
|
|
29
|
+
}
|
|
30
|
+
export interface MediaMessageAttachmentEncryptionMetadata {
|
|
31
|
+
schema: 'mss.link.media-encryption.v1';
|
|
32
|
+
algorithm: 'AES-256-GCM';
|
|
33
|
+
keyId?: string;
|
|
34
|
+
nonce: string;
|
|
35
|
+
wrappedKey: string;
|
|
20
36
|
}
|
|
21
37
|
export interface MediaMessageMetadataPayload {
|
|
22
38
|
schema: 'mss.link.media-message.v1';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MssLinkBrowserClientOptions } from './client-options';
|
|
2
2
|
import type { LocalCryptoHealth } from './local-crypto-health.contracts';
|
|
3
|
+
import type { DecryptMediaBlobInput, DecryptedMediaBlobResult, EncryptedMediaBlobResult, EncryptMediaBlobInput } from './media-crypto.contracts';
|
|
3
4
|
import type { SendMediaMessageParams } from './media-message.contracts';
|
|
4
5
|
import type { DecryptedMessage } from './message.contracts';
|
|
5
6
|
export declare class MssLinkBrowserClient {
|
|
@@ -26,6 +27,8 @@ export declare class MssLinkBrowserClient {
|
|
|
26
27
|
kind?: string;
|
|
27
28
|
}): Promise<string>;
|
|
28
29
|
sendMediaMessage(params: SendMediaMessageParams): Promise<string>;
|
|
30
|
+
encryptMediaBlob(input: EncryptMediaBlobInput): Promise<EncryptedMediaBlobResult>;
|
|
31
|
+
decryptMediaBlob(input: DecryptMediaBlobInput): Promise<DecryptedMediaBlobResult>;
|
|
29
32
|
syncMessages(): Promise<void>;
|
|
30
33
|
getLocalMessages(chatId: string): Promise<DecryptedMessage[]>;
|
|
31
34
|
reset(): void;
|
|
@@ -80,6 +80,76 @@ export class MssLinkBrowserClient {
|
|
|
80
80
|
throw normalizeLocalCryptoError(error);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
async encryptMediaBlob(input) {
|
|
84
|
+
try {
|
|
85
|
+
const plaintext = new Uint8Array(await input.blob.arrayBuffer());
|
|
86
|
+
const encrypted = await this.queue.run(async () => {
|
|
87
|
+
const module = await this.loadWasmModule();
|
|
88
|
+
return module.encryptMediaBytes(plaintext);
|
|
89
|
+
});
|
|
90
|
+
const encryptedBytes = new Uint8Array(encrypted.ciphertext);
|
|
91
|
+
return {
|
|
92
|
+
encryptedBlob: new Blob([encryptedBytes], {
|
|
93
|
+
type: 'application/octet-stream',
|
|
94
|
+
}),
|
|
95
|
+
encryption: {
|
|
96
|
+
schema: 'mss.link.media-encryption.v1',
|
|
97
|
+
algorithm: 'AES-256-GCM',
|
|
98
|
+
keyId: input.assetId,
|
|
99
|
+
nonce: encrypted.nonce,
|
|
100
|
+
wrappedKey: encrypted.key,
|
|
101
|
+
},
|
|
102
|
+
attachment: {
|
|
103
|
+
assetId: input.assetId,
|
|
104
|
+
mimeType: input.mimeType,
|
|
105
|
+
size: input.size,
|
|
106
|
+
...(input.originalName ? { originalName: input.originalName } : {}),
|
|
107
|
+
...(input.uploadedAt ? { uploadedAt: input.uploadedAt } : {}),
|
|
108
|
+
encryption: {
|
|
109
|
+
schema: 'mss.link.media-encryption.v1',
|
|
110
|
+
algorithm: 'AES-256-GCM',
|
|
111
|
+
keyId: input.assetId,
|
|
112
|
+
nonce: encrypted.nonce,
|
|
113
|
+
wrappedKey: encrypted.key,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw normalizeLocalCryptoError(error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async decryptMediaBlob(input) {
|
|
123
|
+
const encryption = input.metadata.encryption;
|
|
124
|
+
if (!encryption) {
|
|
125
|
+
return {
|
|
126
|
+
blob: input.encryptedBlob,
|
|
127
|
+
mimeType: input.metadata.mimeType,
|
|
128
|
+
...(input.metadata.originalName
|
|
129
|
+
? { fileName: input.metadata.originalName }
|
|
130
|
+
: {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const ciphertext = new Uint8Array(await input.encryptedBlob.arrayBuffer());
|
|
135
|
+
const plaintext = await this.queue.run(async () => {
|
|
136
|
+
const module = await this.loadWasmModule();
|
|
137
|
+
return module.decryptMediaBytes(ciphertext, encryption.wrappedKey, encryption.nonce);
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
blob: new Blob([new Uint8Array(plaintext)], {
|
|
141
|
+
type: input.metadata.mimeType,
|
|
142
|
+
}),
|
|
143
|
+
mimeType: input.metadata.mimeType,
|
|
144
|
+
...(input.metadata.originalName
|
|
145
|
+
? { fileName: input.metadata.originalName }
|
|
146
|
+
: {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw normalizeLocalCryptoError(error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
83
153
|
async syncMessages() {
|
|
84
154
|
try {
|
|
85
155
|
await this.runWithClient((client) => client.syncMessages());
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { WasmMssClient } from '@msssystems/mss-crypto-wasm';
|
|
2
|
+
import type { WasmEncryptedMediaBytes } from './media-crypto.contracts';
|
|
2
3
|
export interface MssLinkWasmClient extends WasmMssClient {
|
|
3
4
|
sendMediaMessage(chatId: string, targetUserId: string, metadataJson: string, mediaAssetIds: string[]): Promise<string>;
|
|
4
5
|
}
|
|
5
6
|
export type WasmMssClientConstructor = new (userId: string, deviceId: number, apiBaseUrl: string) => MssLinkWasmClient;
|
|
6
7
|
export interface MssCryptoWasmModule {
|
|
7
8
|
WasmMssClient: WasmMssClientConstructor;
|
|
9
|
+
encryptMediaBytes(plaintext: Uint8Array): WasmEncryptedMediaBytes;
|
|
10
|
+
decryptMediaBytes(ciphertext: Uint8Array, key: string, nonce: string): Uint8Array;
|
|
8
11
|
}
|
|
9
12
|
export type WasmMssClientInstance = MssLinkWasmClient;
|