mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-20 03:52:17 +02:00
SVR3: Node bridge
This commit is contained in:
parent
a4da946705
commit
536ec242c9
2
node/Native.d.ts
vendored
2
node/Native.d.ts
vendored
@ -430,6 +430,8 @@ export function SignedPreKeyRecord_GetSignature(obj: Wrapper<SignedPreKeyRecord>
|
||||
export function SignedPreKeyRecord_GetTimestamp(obj: Wrapper<SignedPreKeyRecord>): Timestamp;
|
||||
export function SignedPreKeyRecord_New(id: number, timestamp: Timestamp, pubKey: Wrapper<PublicKey>, privKey: Wrapper<PrivateKey>, signature: Buffer): SignedPreKeyRecord;
|
||||
export function SignedPreKeyRecord_Serialize(obj: Wrapper<SignedPreKeyRecord>): Buffer;
|
||||
export function Svr3Backup(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, secret: Buffer, password: string, maxTries: number, username: string, enclavePassword: string, opTimeoutMs: number): Promise<Buffer>;
|
||||
export function Svr3Restore(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, password: string, shareSet: Buffer, username: string, enclavePassword: string, opTimeoutMs: number): Promise<Buffer>;
|
||||
export function TESTING_CdsiLookupErrorConvert(): void;
|
||||
export function TESTING_CdsiLookupResponseConvert(asyncRuntime: Wrapper<TokioAsyncContext>): Promise<LookupResponse>;
|
||||
export function TESTING_ErrorOnBorrowAsync(_input: null): Promise<void>;
|
||||
|
@ -7,7 +7,7 @@ import * as Native from '../Native';
|
||||
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
enum ServiceIdKind {
|
||||
export enum ServiceIdKind {
|
||||
Aci = 0,
|
||||
Pni,
|
||||
}
|
||||
|
@ -40,6 +40,10 @@ export enum ErrorCode {
|
||||
InvalidUsernameLinkEncryptedData,
|
||||
|
||||
RateLimitedError,
|
||||
|
||||
SvrDataMissing,
|
||||
SvrRequestFailed,
|
||||
SvrRestoreFailed,
|
||||
}
|
||||
|
||||
export class LibSignalErrorBase extends Error {
|
||||
@ -195,6 +199,18 @@ export type RateLimitedError = LibSignalErrorBase & {
|
||||
readonly retryAfterSecs: number;
|
||||
};
|
||||
|
||||
export type SvrDataMissingError = LibSignalErrorBase & {
|
||||
code: ErrorCode.SvrDataMissing;
|
||||
};
|
||||
|
||||
export type SvrRequestFailedError = LibSignalErrorCommon & {
|
||||
code: ErrorCode.SvrRequestFailed;
|
||||
};
|
||||
|
||||
export type SvrRestoreFailedError = LibSignalErrorCommon & {
|
||||
code: ErrorCode.SvrRestoreFailed;
|
||||
};
|
||||
|
||||
export type LibSignalError =
|
||||
| GenericError
|
||||
| DuplicatedMessageError
|
||||
@ -221,4 +237,7 @@ export type LibSignalError =
|
||||
| InvalidUsernameLinkEncryptedData
|
||||
| IoError
|
||||
| InvalidMediaInputError
|
||||
| SvrDataMissingError
|
||||
| SvrRestoreFailedError
|
||||
| SvrRequestFailedError
|
||||
| UnsupportedMediaInputError;
|
||||
|
@ -32,6 +32,11 @@
|
||||
*/
|
||||
|
||||
import * as Native from '../Native';
|
||||
import {
|
||||
IoError,
|
||||
InvalidMediaInputError,
|
||||
UnsupportedMediaInputError,
|
||||
} from './Errors';
|
||||
import { InputStream } from './io';
|
||||
|
||||
export class SanitizedMetadata {
|
||||
@ -49,7 +54,7 @@ export class SanitizedMetadata {
|
||||
|
||||
/**
|
||||
* Get the sanitized metadata, if any.
|
||||
* @returns The sanitized metadata, or {@code null} if it didn't need to be sanitized.
|
||||
* @returns The sanitized metadata, or `null` if it didn't need to be sanitized.
|
||||
*/
|
||||
getMetadata(): Buffer | null {
|
||||
const metadata = Native.SanitizedMetadata_GetMetadata(this);
|
||||
@ -80,11 +85,11 @@ export class SanitizedMetadata {
|
||||
* Sanitize an MP4 input.
|
||||
*
|
||||
* @param input An MP4 format input stream.
|
||||
* @param length The exact length of the input stream.
|
||||
* @param len The exact length of the input stream.
|
||||
* @returns The sanitized metadata.
|
||||
* @throws IoError If an IO error on the input occurs.
|
||||
* @throws InvalidMediaInputError If the input could not be parsed because it was invalid.
|
||||
* @throws UnsupportedMediaInputError If the input could not be parsed because it's unsupported in some way.
|
||||
* @throws {IoError} If an IO error on the input occurs.
|
||||
* @throws {InvalidMediaInputError} If the input could not be parsed because it was invalid.
|
||||
* @throws {UnsupportedMediaInputError} If the input could not be parsed because it's unsupported in some way.
|
||||
*/
|
||||
export async function sanitize(
|
||||
input: InputStream,
|
||||
|
@ -13,15 +13,19 @@
|
||||
*/
|
||||
|
||||
import * as Native from '../Native';
|
||||
import {
|
||||
IoError,
|
||||
InvalidMediaInputError,
|
||||
UnsupportedMediaInputError,
|
||||
} from './Errors';
|
||||
|
||||
/**
|
||||
* Sanitize a WebP input.
|
||||
*
|
||||
* @param input A WebP format input stream.
|
||||
* @param length The exact length of the input stream.
|
||||
* @throws IoError If an IO error on the input occurs.
|
||||
* @throws InvalidMediaInputError If the input could not be parsed because it was invalid.
|
||||
* @throws UnsupportedMediaInputError If the input could not be parsed because it's unsupported in some way.
|
||||
* @throws {IoError} If an IO error on the input occurs.
|
||||
* @throws {InvalidMediaInputError} If the input could not be parsed because it was invalid.
|
||||
* @throws {UnsupportedMediaInputError} If the input could not be parsed because it's unsupported in some way.
|
||||
*/
|
||||
export function sanitize(input: Buffer): void {
|
||||
Native.WebpSanitizer_Sanitize(input);
|
||||
|
@ -1379,7 +1379,7 @@ export class SealedSenderDecryptionResult {
|
||||
}
|
||||
}
|
||||
|
||||
interface CiphertextMessageConvertible {
|
||||
export interface CiphertextMessageConvertible {
|
||||
asCiphertextMessage(): CiphertextMessage;
|
||||
}
|
||||
|
||||
@ -1602,7 +1602,7 @@ export function sealedSenderEncrypt(
|
||||
return Native.SealedSender_Encrypt(address, content, identityStore);
|
||||
}
|
||||
|
||||
type SealedSenderMultiRecipientEncryptOptions = {
|
||||
export type SealedSenderMultiRecipientEncryptOptions = {
|
||||
content: UnidentifiedSenderMessageContent;
|
||||
recipients: ProtocolAddress[];
|
||||
excludedRecipients?: ServiceId[];
|
||||
|
167
node/ts/net.ts
167
node/ts/net.ts
@ -6,6 +6,12 @@
|
||||
import type { ReadonlyDeep } from 'type-fest';
|
||||
import * as Native from '../Native';
|
||||
import { Aci } from './Address';
|
||||
import {
|
||||
IoError,
|
||||
SvrDataMissingError,
|
||||
SvrRestoreFailedError,
|
||||
SvrRequestFailedError,
|
||||
} from './Errors';
|
||||
|
||||
// This must match the libsignal-bridge Rust enum of the same name.
|
||||
export enum Environment {
|
||||
@ -13,7 +19,7 @@ export enum Environment {
|
||||
Production = 1,
|
||||
}
|
||||
|
||||
export type CDSAuthType = {
|
||||
export type ServiceAuth = {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
@ -44,13 +50,19 @@ export class Net {
|
||||
private readonly _asyncContext: Native.TokioAsyncContext;
|
||||
private readonly _connectionManager: Native.ConnectionManager;
|
||||
|
||||
/**
|
||||
* Instance of the {@link Svr3Client} to access SVR3.
|
||||
*/
|
||||
svr3: Svr3Client;
|
||||
|
||||
constructor(env: Environment) {
|
||||
this._asyncContext = Native.TokioAsyncContext_new();
|
||||
this._connectionManager = Native.ConnectionManager_new(env);
|
||||
this.svr3 = new Svr3ClientImpl(this._asyncContext, this._connectionManager);
|
||||
}
|
||||
|
||||
async cdsiLookup(
|
||||
{ username, password }: Readonly<CDSAuthType>,
|
||||
{ username, password }: Readonly<ServiceAuth>,
|
||||
{
|
||||
e164s,
|
||||
acisAndAccessKeys,
|
||||
@ -91,3 +103,154 @@ export class Net {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface provides functionality for communicating with SVR3
|
||||
*
|
||||
* Its instance can be obtained from an {@link Net#svr3} property
|
||||
* of the {@link Net} class.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Environment, Net } from '../net';
|
||||
* // Obtain an instance
|
||||
* const SVR3 = new Net(Environment.Staging).svr3;
|
||||
* // Instantiate ServiceAuth with the username and password obtained from the Chat Server.
|
||||
* const auth = { username: USERNAME, password: ENCLAVE_PASSWORD };
|
||||
* // Store a value in SVR3. Here 10 is the number of permitted restore attempts.
|
||||
* const shareSet = await SVR3.backup(SECRET_TO_BE_STORED, PASSWORD, 10, auth, TIMEOUT);
|
||||
* const restoredSecret = await SVR3.restore( PASSWORD, shareSet, auth, TIMEOUT);
|
||||
* ```
|
||||
*/
|
||||
export interface Svr3Client {
|
||||
/**
|
||||
* Backup a secret to SVR3.
|
||||
*
|
||||
* Error messages are expected to be log-safe and not contain any sensitive
|
||||
* data.
|
||||
*
|
||||
* @param what - The secret to be stored. Must be 32 bytes long.
|
||||
* @param password - User-provide password that will be used to derive the
|
||||
* encryption key for the secret.
|
||||
* @param maxTries - Number of times the secret will be allowed to be guessed.
|
||||
* Each call to {@link Svr3Client#restore} that has reached the server will
|
||||
* decrement the counter. Must be positive.
|
||||
* @param auth - An instance of {@link ServiceAuth} containing the username
|
||||
* and password obtained from the Chat Server. The password is an OTP which is
|
||||
* generally good for about 15 minutes, therefore it can be reused for the
|
||||
* subsequent calls to either backup or restore that are not too far apart in
|
||||
* time.
|
||||
* @param opTimeoutMs - The maximum wall time libsignal is allowed to spend
|
||||
* communicating with SVR3 service.
|
||||
* @returns A `Promise` which--when awaited--will return a byte array with a
|
||||
* serialized masked share set. It is supposed to be an opaque blob for the
|
||||
* clients and therefore no assumptions should be made about its contents.
|
||||
* This byte array should be stored by the clients and used to restore the
|
||||
* secret along with the password. Please note that masked share set does not
|
||||
* have to be treated as secret.
|
||||
*
|
||||
* The returned `Promise` can also fail due to the network issues (including the
|
||||
* timeout), problems establishing the Noise connection to the enclaves, or
|
||||
* invalid arguments' values. {@link IoError} errors can, in general, be
|
||||
* retried, although there is already a retry-with-backoff mechanism inside
|
||||
* libsignal used to connect to the SVR3 servers. Other exceptions are caused
|
||||
* by the bad input or data missing on the server. They are therefore
|
||||
* non-actionable and are guaranteed to be thrown again when retried.
|
||||
*/
|
||||
backup(
|
||||
what: Buffer,
|
||||
password: string,
|
||||
maxTries: number,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
): Promise<Buffer>;
|
||||
|
||||
/**
|
||||
* Restore a secret from SVR3.
|
||||
*
|
||||
* Error messages are expected to be log-safe and not contain any sensitive
|
||||
* data.
|
||||
*
|
||||
* @param password - User-provide password that will be used to derive the
|
||||
* decryption key for the secret.
|
||||
* @param shareSet - a serialized masked share set returned by a call to
|
||||
* {@link Svr3Client#backup}.
|
||||
* @param auth - An instance of {@link ServiceAuth} containing the username
|
||||
* and password obtained from the Chat Server. The password is an OTP which is
|
||||
* generally good for about 15 minutes, therefore it can be reused for the
|
||||
* subsequent calls to either backup or restore that are not too far apart in
|
||||
* time.
|
||||
* @param opTimeoutMs - The maximum wall time libsignal is allowed to spend
|
||||
* communicating with SVR3 service.
|
||||
* @returns A `Promise` which--when awaited--will return a byte array with the
|
||||
* restored secret.
|
||||
*
|
||||
* The returned `Promise` can also fail due to the network issues (including the
|
||||
* timeout), problems establishing the Noise connection to the enclaves, or
|
||||
* invalid arguments' values. {@link IoError} errors can, in general, be
|
||||
* retried, although there is already a retry-with-backoff mechanism inside
|
||||
* libsignal used to connect to the SVR3 servers. Other exceptions are caused
|
||||
* by the bad input or data missing on the server. They are therefore
|
||||
* non-actionable and are guaranteed to be thrown again when retried.
|
||||
*
|
||||
* - {@link SvrDataMissingError} is returned when the maximum restore attempts
|
||||
* number has been exceeded or if the value has never been backed up.
|
||||
* - {@link SvrRestoreFailedError} is returned when the combination of the
|
||||
* password and masked share set does not result in successful restoration
|
||||
* of the secret.
|
||||
* - {@link SvrRequestFailedError} is returned when the de-serialization of a
|
||||
* masked share set fails, or when the server requests fail for reasons
|
||||
* other than "maximum attempts exceeded".
|
||||
*/
|
||||
restore(
|
||||
password: string,
|
||||
shareSet: Buffer,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
): Promise<Buffer>;
|
||||
}
|
||||
|
||||
class Svr3ClientImpl implements Svr3Client {
|
||||
constructor(
|
||||
private readonly _asyncContext: Native.TokioAsyncContext,
|
||||
private readonly _connectionManager: Native.ConnectionManager
|
||||
) {}
|
||||
|
||||
async backup(
|
||||
what: Buffer,
|
||||
password: string,
|
||||
maxTries: number,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
): Promise<Buffer> {
|
||||
return Native.Svr3Backup(
|
||||
{ _nativeHandle: this._asyncContext },
|
||||
{ _nativeHandle: this._connectionManager },
|
||||
what,
|
||||
password,
|
||||
maxTries,
|
||||
auth.username,
|
||||
auth.password,
|
||||
opTimeoutMs
|
||||
);
|
||||
}
|
||||
|
||||
async restore(
|
||||
password: string,
|
||||
shareSet: Buffer,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
): Promise<Buffer> {
|
||||
return Native.Svr3Restore(
|
||||
{ _nativeHandle: this._asyncContext },
|
||||
{ _nativeHandle: this._connectionManager },
|
||||
password,
|
||||
shareSet,
|
||||
auth.username,
|
||||
auth.password,
|
||||
opTimeoutMs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,16 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import { config, expect } from 'chai';
|
||||
import { config, expect, use } from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import * as util from './util';
|
||||
import { Aci, Pni } from '../Address';
|
||||
import * as Native from '../../Native';
|
||||
import { ErrorCode, LibSignalErrorBase } from '../Errors';
|
||||
import { Environment, Net, ServiceAuth } from '../net';
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
use(chaiAsPromised);
|
||||
|
||||
util.initLogger();
|
||||
config.truncateThreshold = 0;
|
||||
@ -50,3 +55,118 @@ describe('cdsi lookup', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SVR3', () => {
|
||||
const TIMEOUT = 5000;
|
||||
const USERNAME = randomBytes(16).toString('hex');
|
||||
const SVR3 = new Net(Environment.Staging).svr3;
|
||||
|
||||
function make_auth(): Readonly<ServiceAuth> {
|
||||
const otp = Native.CreateOTPFromBase64(
|
||||
USERNAME,
|
||||
// Empty string is a valid base64 encoding
|
||||
process.env.ENCLAVE_SECRET || ''
|
||||
);
|
||||
return { username: USERNAME, password: otp };
|
||||
}
|
||||
|
||||
describe('Backup', () => {
|
||||
// It is OK to reuse the auth in "input validation" tests.
|
||||
const AUTH = make_auth();
|
||||
|
||||
it('maxTries must be positive', () => {
|
||||
const secret = randomBytes(32);
|
||||
return expect(SVR3.backup(secret, 'password', 0, AUTH, TIMEOUT)).to
|
||||
.eventually.be.rejected;
|
||||
});
|
||||
|
||||
it('Secret must be 32 bytes', () => {
|
||||
const secret = randomBytes(42);
|
||||
return expect(SVR3.backup(secret, 'password', 1, AUTH, TIMEOUT)).to
|
||||
.eventually.be.rejected;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Restore', () => {
|
||||
it('Empty share set', () => {
|
||||
const auth = make_auth();
|
||||
const shareSet = Buffer.alloc(0);
|
||||
return expect(
|
||||
SVR3.restore('password', shareSet, auth, TIMEOUT)
|
||||
).to.eventually.be.rejectedWith(LibSignalErrorBase);
|
||||
});
|
||||
|
||||
it('Share set bad format', () => {
|
||||
const auth = make_auth();
|
||||
const shareSet = Buffer.from([42]);
|
||||
return expect(
|
||||
SVR3.restore('password', shareSet, auth, TIMEOUT)
|
||||
).to.eventually.be.rejectedWith(LibSignalErrorBase);
|
||||
});
|
||||
});
|
||||
|
||||
// Integration tests require access to the staging environment and make real
|
||||
// network calls and as such require the secret (and lacking the secret will
|
||||
// not be run).
|
||||
describe('Integration tests', function (this: Mocha.Suite) {
|
||||
before(() => {
|
||||
if (!process.env.ENCLAVE_SECRET) {
|
||||
this.ctx.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it('Backup and restore work in staging', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth, TIMEOUT);
|
||||
const restoredSecret = await SVR3.restore(
|
||||
'password',
|
||||
shareSet,
|
||||
auth,
|
||||
TIMEOUT
|
||||
);
|
||||
expect(restoredSecret).to.eql(secret);
|
||||
}).timeout(10000);
|
||||
|
||||
it('Restore with wrong password', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth, TIMEOUT);
|
||||
return expect(SVR3.restore('wrong password', shareSet, auth, TIMEOUT))
|
||||
.to.eventually.be.rejectedWith(LibSignalErrorBase)
|
||||
.and.have.property('code', ErrorCode.SvrRestoreFailed);
|
||||
}).timeout(10000);
|
||||
|
||||
it('Restore with corrupted share set', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth, TIMEOUT);
|
||||
// The first byte is the serialization format version, changing that
|
||||
// _will_ fail (checked in the other test). Changing the actual share set
|
||||
// value makes a more interesting test case.
|
||||
shareSet[1] ^= 0xff;
|
||||
return expect(
|
||||
SVR3.restore('password', shareSet, auth, TIMEOUT)
|
||||
).to.eventually.be.rejectedWith(LibSignalErrorBase);
|
||||
}).timeout(10000);
|
||||
|
||||
it('Exceed maxTries', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 1, auth, TIMEOUT);
|
||||
await SVR3.restore('password', shareSet, auth, TIMEOUT);
|
||||
return expect(SVR3.restore('password', shareSet, auth, TIMEOUT))
|
||||
.to.eventually.be.rejectedWith(LibSignalErrorBase)
|
||||
.and.have.property('code', ErrorCode.SvrDataMissing);
|
||||
});
|
||||
|
||||
it('Timeout', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const SHORT_TIMEOUT = 100;
|
||||
return expect(SVR3.backup(secret, 'password', 10, auth, SHORT_TIMEOUT))
|
||||
.to.eventually.be.rejectedWith(LibSignalErrorBase)
|
||||
.and.have.property('code', ErrorCode.IoError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -333,6 +333,11 @@ ansi-regex@^6.0.1:
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
|
||||
integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
|
||||
|
||||
ansi-sequence-parser@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf"
|
||||
integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==
|
||||
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||
@ -1666,6 +1671,11 @@ json5@^1.0.2:
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
jsonc-parser@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
|
||||
integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==
|
||||
|
||||
keyv@^4.5.3:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
@ -1720,6 +1730,11 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lunr@^2.3.9:
|
||||
version "2.3.9"
|
||||
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
|
||||
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
|
||||
|
||||
make-fetch-happen@^13.0.0:
|
||||
version "13.0.0"
|
||||
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz#705d6f6cbd7faecb8eac2432f551e49475bfedf0"
|
||||
@ -1737,6 +1752,11 @@ make-fetch-happen@^13.0.0:
|
||||
promise-retry "^2.0.1"
|
||||
ssri "^10.0.0"
|
||||
|
||||
marked@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3"
|
||||
integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==
|
||||
|
||||
merge2@^1.3.0, merge2@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
@ -1764,7 +1784,7 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^9.0.1:
|
||||
minimatch@^9.0.1, minimatch@^9.0.3:
|
||||
version "9.0.3"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
|
||||
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
|
||||
@ -2308,6 +2328,16 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
shiki@^0.14.7:
|
||||
version "0.14.7"
|
||||
resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e"
|
||||
integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==
|
||||
dependencies:
|
||||
ansi-sequence-parser "^1.1.0"
|
||||
jsonc-parser "^3.2.0"
|
||||
vscode-oniguruma "^1.7.0"
|
||||
vscode-textmate "^8.0.0"
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
@ -2610,6 +2640,16 @@ typed-array-length@^1.0.4:
|
||||
for-each "^0.3.3"
|
||||
is-typed-array "^1.1.9"
|
||||
|
||||
typedoc@^0.25.8:
|
||||
version "0.25.8"
|
||||
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.8.tgz#7d0e1bf12d23bf1c459fd4893c82cb855911ff12"
|
||||
integrity sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==
|
||||
dependencies:
|
||||
lunr "^2.3.9"
|
||||
marked "^4.3.0"
|
||||
minimatch "^9.0.3"
|
||||
shiki "^0.14.7"
|
||||
|
||||
typescript@4.9.3:
|
||||
version "4.9.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
|
||||
@ -2666,6 +2706,16 @@ uuid@^8.3.0:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
vscode-oniguruma@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b"
|
||||
integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==
|
||||
|
||||
vscode-textmate@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d"
|
||||
integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
|
@ -33,6 +33,7 @@ def translate_to_ts(typ):
|
||||
"String": "string",
|
||||
"&str": "string",
|
||||
"Vec<u8>": "Buffer",
|
||||
"Box<[u8]>": "Buffer",
|
||||
"ServiceId": "Buffer",
|
||||
"Aci": "Buffer",
|
||||
"Pni": "Buffer",
|
||||
|
@ -27,7 +27,7 @@ use crate::support::*;
|
||||
use crate::*;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "jni")] {
|
||||
if #[cfg(any(feature = "jni", feature = "node"))] {
|
||||
use futures_util::future::TryFutureExt as _;
|
||||
use rand::rngs::OsRng;
|
||||
use std::num::NonZeroU32;
|
||||
@ -88,7 +88,7 @@ impl Environment {
|
||||
|
||||
pub struct ConnectionManager {
|
||||
cdsi: EndpointConnection<Cdsi, MultiRouteConnectionManager, TcpSslTransportConnector>,
|
||||
#[cfg(feature = "jni")]
|
||||
#[cfg(any(feature = "jni", feature = "node"))]
|
||||
svr3: (
|
||||
EndpointConnection<Sgx, MultiRouteConnectionManager, TcpSslTransportConnector>,
|
||||
EndpointConnection<Nitro, MultiRouteConnectionManager, TcpSslTransportConnector>,
|
||||
@ -100,7 +100,7 @@ impl ConnectionManager {
|
||||
fn new(environment: Environment) -> Self {
|
||||
Self {
|
||||
cdsi: Self::endpoint_connection(environment.env().cdsi),
|
||||
#[cfg(feature = "jni")]
|
||||
#[cfg(any(feature = "jni", feature = "node"))]
|
||||
svr3: (
|
||||
Self::endpoint_connection(environment.env().svr3.sgx()),
|
||||
Self::endpoint_connection(environment.env().svr3.nitro()),
|
||||
@ -250,7 +250,7 @@ fn CreateOTPFromBase64(username: String, secret: String) -> String {
|
||||
Auth::otp(&username, &secret, std::time::SystemTime::now())
|
||||
}
|
||||
|
||||
#[bridge_io(TokioAsyncContext, ffi = false, node = false)]
|
||||
#[bridge_io(TokioAsyncContext, ffi = false)]
|
||||
async fn Svr3Backup(
|
||||
connection_manager: &ConnectionManager,
|
||||
secret: Box<[u8]>,
|
||||
@ -279,7 +279,7 @@ async fn Svr3Backup(
|
||||
Ok(share_set.serialize().expect("can serialize the share set"))
|
||||
}
|
||||
|
||||
#[bridge_io(TokioAsyncContext, ffi = false, node = false)]
|
||||
#[bridge_io(TokioAsyncContext, ffi = false)]
|
||||
async fn Svr3Restore(
|
||||
connection_manager: &ConnectionManager,
|
||||
password: String,
|
||||
@ -301,7 +301,7 @@ async fn Svr3Restore(
|
||||
Ok(restored_secret.to_vec())
|
||||
}
|
||||
|
||||
#[cfg(feature = "jni")]
|
||||
#[cfg(any(feature = "jni", feature = "node"))]
|
||||
async fn svr3_connect<'a>(
|
||||
connection_manager: &ConnectionManager,
|
||||
username: String,
|
||||
|
@ -351,6 +351,14 @@ impl SimpleArgTypeInfo for bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleArgTypeInfo for Box<[u8]> {
|
||||
type ArgType = JsBuffer;
|
||||
|
||||
fn convert_from(cx: &mut FunctionContext, foreign: Handle<Self::ArgType>) -> NeonResult<Self> {
|
||||
Ok(foreign.as_slice(cx).to_vec().into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts `null` to `None`, passing through all other values.
|
||||
impl<'storage, 'context: 'storage, T> ArgTypeInfo<'storage, 'context> for Option<T>
|
||||
where
|
||||
|
@ -4,6 +4,7 @@
|
||||
//
|
||||
use std::fmt;
|
||||
|
||||
use libsignal_net::svr3::Error as Svr3Error;
|
||||
use paste::paste;
|
||||
use signal_media::sanitize::mp4::{Error as Mp4Error, ParseError as Mp4ParseError};
|
||||
use signal_media::sanitize::webp::{Error as WebpError, ParseError as WebpParseError};
|
||||
@ -131,9 +132,12 @@ pub trait SignalNodeError: Sized + fmt::Display {
|
||||
}
|
||||
}
|
||||
|
||||
const RATE_LIMITED_ERROR: &str = "RateLimitedError";
|
||||
const IO_ERROR: &str = "IoError";
|
||||
const INVALID_MEDIA_INPUT: &str = "InvalidMediaInput";
|
||||
const IO_ERROR: &str = "IoError";
|
||||
const RATE_LIMITED_ERROR: &str = "RateLimitedError";
|
||||
const SVR3_DATA_MISSING: &str = "SvrDataMissing";
|
||||
const SVR3_REQUEST_FAILED: &str = "SvrRequestFailed";
|
||||
const SVR3_RESTORE_FAILED: &str = "SvrRestoreFailed";
|
||||
const UNSUPPORTED_MEDIA_INPUT: &str = "UnsupportedMediaInput";
|
||||
|
||||
impl SignalNodeError for neon::result::Throw {
|
||||
@ -431,6 +435,35 @@ impl SignalNodeError for libsignal_net::cdsi::LookupError {
|
||||
}
|
||||
}
|
||||
|
||||
impl SignalNodeError for libsignal_net::svr3::Error {
|
||||
fn throw<'a>(
|
||||
self,
|
||||
cx: &mut impl Context<'a>,
|
||||
module: Handle<'a, JsObject>,
|
||||
operation_name: &str,
|
||||
) -> JsResult<'a, JsValue> {
|
||||
let name = match self {
|
||||
Svr3Error::Net(_) => Some(IO_ERROR),
|
||||
Svr3Error::AttestationError(inner) => {
|
||||
return inner.throw(cx, module, operation_name);
|
||||
}
|
||||
Svr3Error::RequestFailed(_) => Some(SVR3_REQUEST_FAILED),
|
||||
Svr3Error::RestoreFailed => Some(SVR3_RESTORE_FAILED),
|
||||
Svr3Error::DataMissing => Some(SVR3_DATA_MISSING),
|
||||
Svr3Error::Protocol(_) => None,
|
||||
};
|
||||
|
||||
let message = self.to_string();
|
||||
match new_js_error(cx, module, name, &message, operation_name, None) {
|
||||
Some(error) => cx.throw(error),
|
||||
None => {
|
||||
// Make sure we still throw something.
|
||||
cx.throw_error(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an error returned by a callback.
|
||||
#[derive(Debug)]
|
||||
struct CallbackError {
|
||||
|
Loading…
Reference in New Issue
Block a user