@signalapp/mock-server
Advanced tools
Comparing version 8.3.1 to 9.0.0
{ | ||
"name": "@signalapp/mock-server", | ||
"version": "8.3.1", | ||
"version": "9.0.0", | ||
"description": "Mock Signal Server for writing tests", | ||
@@ -46,3 +46,3 @@ "main": "src/index.js", | ||
"@indutny/parallel-prettier": "^3.0.0", | ||
"@signalapp/libsignal-client": "^0.58.2", | ||
"@signalapp/libsignal-client": "^0.60.0", | ||
"@tus/file-store": "^1.4.0", | ||
@@ -49,0 +49,0 @@ "@tus/server": "^1.7.0", |
@@ -171,4 +171,7 @@ /// <reference types="node" /> | ||
readonly secondaryDevices: Device[]; | ||
readonly accountEntropyPool: string; | ||
readonly masterKey: Buffer; | ||
readonly mediaRootBackupKey: Buffer; | ||
ephemeralBackupKey: Buffer | undefined; | ||
storageRecordIkm: Buffer | undefined; | ||
readonly userAgent = "OWI"; | ||
@@ -175,0 +178,0 @@ constructor(device: Device, config: Config); |
@@ -37,2 +37,3 @@ "use strict"; | ||
const SignalClient = __importStar(require("@signalapp/libsignal-client")); | ||
const AccountKeys_1 = require("@signalapp/libsignal-client/dist/AccountKeys"); | ||
const debug_1 = __importDefault(require("debug")); | ||
@@ -232,5 +233,9 @@ const zkgroup_1 = require("@signalapp/libsignal-client/zkgroup"); | ||
secondaryDevices = new Array(); | ||
masterKey = crypto_1.default.randomBytes(32); | ||
accountEntropyPool = AccountKeys_1.AccountEntropyPool.generate(); | ||
masterKey = (0, crypto_2.deriveMasterKey)(this.accountEntropyPool); | ||
mediaRootBackupKey = crypto_1.default.randomBytes(32); | ||
// Forwarded in provisioning envelope | ||
ephemeralBackupKey; | ||
// Overridable to test legacy encryption modes | ||
storageRecordIkm = crypto_1.default.randomBytes(32); | ||
// TODO(indutny): make primary device type configurable | ||
@@ -553,3 +558,7 @@ userAgent = 'OWI'; | ||
async setStorageState(state, previousState) { | ||
const writeOperation = state.createWriteOperation(this.storageKey, previousState); | ||
const writeOperation = state.createWriteOperation({ | ||
storageKey: this.storageKey, | ||
recordIkm: this.storageRecordIkm, | ||
previous: previousState, | ||
}); | ||
(0, assert_1.default)(writeOperation.manifest, 'write operation without manifest'); | ||
@@ -989,3 +998,7 @@ const { updated, error } = await this.config.applyStorageWrite(writeOperation, false); | ||
response = { | ||
keys: { master: this.masterKey }, | ||
keys: { | ||
master: this.masterKey, | ||
mediaRootBackupKey: this.mediaRootBackupKey, | ||
accountEntropyPool: this.accountEntropyPool, | ||
}, | ||
}; | ||
@@ -1240,5 +1253,9 @@ stateChange = SyncState.Keys; | ||
key: keyBuffer, | ||
record: (0, crypto_2.decryptStorageItem)(this.storageKey, { | ||
key, | ||
value: item, | ||
record: (0, crypto_2.decryptStorageItem)({ | ||
storageKey: this.storageKey, | ||
recordIkm: this.storageRecordIkm, | ||
item: { | ||
key, | ||
value: item, | ||
}, | ||
}), | ||
@@ -1245,0 +1262,0 @@ }; |
@@ -332,2 +332,4 @@ "use strict"; | ||
ephemeralBackupKey: primaryDevice.ephemeralBackupKey, | ||
mediaRootBackupKey: primaryDevice.mediaRootBackupKey, | ||
accountEntropyPool: primaryDevice.accountEntropyPool, | ||
}).finish(); | ||
@@ -334,0 +336,0 @@ const { body, ephemeralKey } = (0, crypto_1.encryptProvisionMessage)(Buffer.from(envelopeData), publicKey); |
@@ -20,2 +20,11 @@ /// <reference types="node" /> | ||
}>; | ||
export type ToStorageItemOptions = Readonly<{ | ||
storageKey: Buffer; | ||
recordIkm: Buffer | undefined; | ||
}>; | ||
export type CreateWriteOperationOptions = Readonly<{ | ||
storageKey: Buffer; | ||
recordIkm: Buffer | undefined; | ||
previous?: StorageState; | ||
}>; | ||
export declare class StorageState { | ||
@@ -49,3 +58,3 @@ readonly version: number; | ||
hasKey(storageKey: Buffer): boolean; | ||
createWriteOperation(storageKey: Buffer, previous?: StorageState): Proto.IWriteOperation; | ||
createWriteOperation({ storageKey, recordIkm, previous, }: CreateWriteOperationOptions): Proto.IWriteOperation; | ||
inspect(): string; | ||
@@ -52,0 +61,0 @@ diff(oldState: StorageState): DiffResult; |
@@ -29,4 +29,9 @@ "use strict"; | ||
} | ||
toStorageItem(storageKey) { | ||
return (0, crypto_2.encryptStorageItem)(storageKey, this.key, this.record); | ||
toStorageItem({ storageKey, recordIkm, }) { | ||
return (0, crypto_2.encryptStorageItem)({ | ||
storageKey, | ||
recordIkm, | ||
key: this.key, | ||
record: this.record, | ||
}); | ||
} | ||
@@ -259,3 +264,3 @@ toIdentifier() { | ||
// | ||
createWriteOperation(storageKey, previous) { | ||
createWriteOperation({ storageKey, recordIkm, previous, }) { | ||
const newVersion = long_1.default.fromNumber(previous ? previous.version + 1 : this.version + 1); | ||
@@ -268,3 +273,3 @@ const keysToDelete = new Set((previous?.items ?? []).map((item) => { | ||
if (!keysToDelete.delete(item.getKeyString())) { | ||
insertItem.push(item.toStorageItem(storageKey)); | ||
insertItem.push(item.toStorageItem({ storageKey, recordIkm })); | ||
} | ||
@@ -275,2 +280,3 @@ } | ||
keys: this.items.map((item) => item.toIdentifier()), | ||
recordIkm, | ||
}); | ||
@@ -277,0 +283,0 @@ return { |
@@ -33,7 +33,25 @@ /// <reference types="node" /> | ||
export declare function deriveAccessKey(profileKey: Buffer): Buffer; | ||
export declare function deriveMasterKey(accountEntropyPool: string): Buffer; | ||
export declare function deriveStorageKey(masterKey: Buffer): Buffer; | ||
export type DeriveStorageItemKeyOptions = Readonly<{ | ||
storageKey: Buffer; | ||
recordIkm: Buffer | undefined; | ||
key: Buffer; | ||
}>; | ||
export declare function deriveStorageItemKey({ storageKey, recordIkm, key, }: DeriveStorageItemKeyOptions): Buffer; | ||
export declare function decryptStorageManifest(storageKey: Buffer, manifest: Proto.IStorageManifest): Proto.IManifestRecord; | ||
export declare function encryptStorageManifest(storageKey: Buffer, manifestRecord: Proto.IManifestRecord): Proto.IStorageManifest; | ||
export declare function decryptStorageItem(storageKey: Buffer, item: Proto.IStorageItem): Proto.IStorageRecord; | ||
export declare function encryptStorageItem(storageKey: Buffer, key: Buffer, record: Proto.IStorageRecord): Proto.IStorageItem; | ||
export type DecryptStorageItemOptions = Readonly<{ | ||
storageKey: Buffer; | ||
recordIkm: Buffer | undefined; | ||
item: Proto.IStorageItem; | ||
}>; | ||
export declare function decryptStorageItem({ storageKey, recordIkm, item, }: DecryptStorageItemOptions): Proto.IStorageRecord; | ||
export type EncryptStorageItemOptions = Readonly<{ | ||
storageKey: Buffer; | ||
key: Buffer; | ||
recordIkm: Buffer | undefined; | ||
record: Proto.IStorageRecord; | ||
}>; | ||
export declare function encryptStorageItem({ storageKey, key, recordIkm, record, }: EncryptStorageItemOptions): Proto.IStorageItem; | ||
export declare function encryptProfileName(profileKey: Buffer, name: string): Buffer; | ||
@@ -40,0 +58,0 @@ export declare function generateAccessKeyVerifier(accessKey: Buffer): Buffer; |
@@ -8,3 +8,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.decodeKyberPreKey = exports.decodeSignedPreKey = exports.decodePreKey = exports.generateAccessKeyVerifier = exports.encryptProfileName = exports.encryptStorageItem = exports.decryptStorageItem = exports.encryptStorageManifest = exports.decryptStorageManifest = exports.deriveStorageKey = exports.deriveAccessKey = exports.generateSenderCertificate = exports.generateServerCertificate = exports.encryptAttachment = exports.encryptProvisionMessage = void 0; | ||
exports.decodeKyberPreKey = exports.decodeSignedPreKey = exports.decodePreKey = exports.generateAccessKeyVerifier = exports.encryptProfileName = exports.encryptStorageItem = exports.decryptStorageItem = exports.encryptStorageManifest = exports.decryptStorageManifest = exports.deriveStorageItemKey = exports.deriveStorageKey = exports.deriveMasterKey = exports.deriveAccessKey = exports.generateSenderCertificate = exports.generateServerCertificate = exports.encryptAttachment = exports.encryptProvisionMessage = void 0; | ||
const crypto_1 = __importDefault(require("crypto")); | ||
@@ -20,2 +20,3 @@ const buffer_1 = require("buffer"); | ||
const AUTH_TAG_SIZE = 16; | ||
const MASTER_KEY_SIZE = 32; | ||
function encryptProvisionMessage(data, remotePubKey) { | ||
@@ -104,2 +105,7 @@ const privateKey = libsignal_client_1.PrivateKey.generate(); | ||
exports.deriveAccessKey = deriveAccessKey; | ||
function deriveMasterKey(accountEntropyPool) { | ||
const hkdf = libsignal_client_1.HKDF.new(3); | ||
return hkdf.deriveSecrets(MASTER_KEY_SIZE, buffer_1.Buffer.from(accountEntropyPool), buffer_1.Buffer.from('20240801_SIGNAL_SVR_MASTER_KEY'), null); | ||
} | ||
exports.deriveMasterKey = deriveMasterKey; | ||
function deriveStorageKey(masterKey) { | ||
@@ -116,7 +122,14 @@ const hash = crypto_1.default.createHmac('sha256', masterKey); | ||
} | ||
function deriveStorageItemKey(storageKey, itemKey) { | ||
const hash = crypto_1.default.createHmac('sha256', storageKey); | ||
hash.update(`Item_${itemKey.toString('base64')}`); | ||
return hash.digest(); | ||
const STORAGE_SERVICE_ITEM_KEY_INFO_PREFIX = '20240801_SIGNAL_STORAGE_SERVICE_ITEM_'; | ||
const STORAGE_SERVICE_ITEM_KEY_LEN = 32; | ||
function deriveStorageItemKey({ storageKey, recordIkm, key, }) { | ||
if (recordIkm === undefined) { | ||
const hash = crypto_1.default.createHmac('sha256', storageKey); | ||
hash.update(`Item_${key.toString('base64')}`); | ||
return hash.digest(); | ||
} | ||
const hkdf = libsignal_client_1.HKDF.new(3); | ||
return hkdf.deriveSecrets(STORAGE_SERVICE_ITEM_KEY_LEN, recordIkm, buffer_1.Buffer.concat([buffer_1.Buffer.from(STORAGE_SERVICE_ITEM_KEY_INFO_PREFIX), key]), buffer_1.Buffer.alloc(0)); | ||
} | ||
exports.deriveStorageItemKey = deriveStorageItemKey; | ||
function decryptAESGCM(ciphertext, key) { | ||
@@ -167,3 +180,3 @@ const iv = ciphertext.slice(0, AESGCM_IV_SIZE); | ||
exports.encryptStorageManifest = encryptStorageManifest; | ||
function decryptStorageItem(storageKey, item) { | ||
function decryptStorageItem({ storageKey, recordIkm, item, }) { | ||
if (!item.key) { | ||
@@ -175,8 +188,16 @@ throw new Error('Missing item.key'); | ||
} | ||
const itemKey = deriveStorageItemKey(storageKey, buffer_1.Buffer.from(item.key)); | ||
const itemKey = deriveStorageItemKey({ | ||
storageKey, | ||
recordIkm, | ||
key: buffer_1.Buffer.from(item.key), | ||
}); | ||
return compiled_1.signalservice.StorageRecord.decode(decryptAESGCM(buffer_1.Buffer.from(item.value), itemKey)); | ||
} | ||
exports.decryptStorageItem = decryptStorageItem; | ||
function encryptStorageItem(storageKey, key, record) { | ||
const itemKey = deriveStorageItemKey(storageKey, key); | ||
function encryptStorageItem({ storageKey, key, recordIkm, record, }) { | ||
const itemKey = deriveStorageItemKey({ | ||
storageKey, | ||
recordIkm, | ||
key, | ||
}); | ||
const encrypted = encryptAESGCM(buffer_1.Buffer.from(compiled_1.signalservice.StorageRecord.encode(record).finish()), itemKey); | ||
@@ -183,0 +204,0 @@ return { |
@@ -40,2 +40,3 @@ /// <reference types="node" /> | ||
versionedExpirationTimer: boolean; | ||
ssre2: boolean; | ||
}; | ||
@@ -42,0 +43,0 @@ backupLevel: BackupLevel; |
@@ -21,3 +21,3 @@ "use strict"; | ||
capabilities; | ||
backupLevel = zkgroup_1.BackupLevel.Media; | ||
backupLevel = zkgroup_1.BackupLevel.Paid; | ||
accessKey; | ||
@@ -44,2 +44,3 @@ profileKeyCommitment; | ||
versionedExpirationTimer: true, | ||
ssre2: true, | ||
}; | ||
@@ -46,0 +47,0 @@ } |
@@ -1,2 +0,1 @@ | ||
/// <reference types="node" /> | ||
import z from 'zod'; | ||
@@ -438,7 +437,10 @@ import { AciString, DeviceId, PniString, RegistrationId } from '../types'; | ||
export declare const SetBackupIdSchema: z.ZodObject<{ | ||
backupAuthCredentialRequest: z.ZodEffects<z.ZodString, Buffer, string>; | ||
messagesBackupAuthCredentialRequest: z.ZodEffects<z.ZodString, Buffer, string>; | ||
mediaBackupAuthCredentialRequest: z.ZodEffects<z.ZodString, Buffer, string>; | ||
}, "strip", z.ZodTypeAny, { | ||
backupAuthCredentialRequest: Buffer; | ||
messagesBackupAuthCredentialRequest: Buffer; | ||
mediaBackupAuthCredentialRequest: Buffer; | ||
}, { | ||
backupAuthCredentialRequest: string; | ||
messagesBackupAuthCredentialRequest: string; | ||
mediaBackupAuthCredentialRequest: string; | ||
}>; | ||
@@ -471,7 +473,7 @@ export type SetBackupId = z.infer<typeof SetBackupIdSchema>; | ||
}, "strip", z.ZodTypeAny, { | ||
cdn: number; | ||
key: string; | ||
}, { | ||
cdn: number; | ||
}, { | ||
key: string; | ||
cdn: number; | ||
}>; | ||
@@ -485,4 +487,4 @@ objectLength: z.ZodNumber; | ||
sourceAttachment: { | ||
cdn: number; | ||
key: string; | ||
cdn: number; | ||
}; | ||
@@ -496,4 +498,4 @@ objectLength: number; | ||
sourceAttachment: { | ||
cdn: number; | ||
key: string; | ||
cdn: number; | ||
}; | ||
@@ -509,4 +511,4 @@ objectLength: number; | ||
sourceAttachment: { | ||
cdn: number; | ||
key: string; | ||
cdn: number; | ||
}; | ||
@@ -522,4 +524,4 @@ objectLength: number; | ||
sourceAttachment: { | ||
cdn: number; | ||
key: string; | ||
cdn: number; | ||
}; | ||
@@ -526,0 +528,0 @@ objectLength: number; |
@@ -106,3 +106,4 @@ "use strict"; | ||
exports.SetBackupIdSchema = zod_1.default.object({ | ||
backupAuthCredentialRequest: zod_1.default.string().transform(util_1.fromBase64), | ||
messagesBackupAuthCredentialRequest: zod_1.default.string().transform(util_1.fromBase64), | ||
mediaBackupAuthCredentialRequest: zod_1.default.string().transform(util_1.fromBase64), | ||
}); | ||
@@ -109,0 +110,0 @@ exports.BackupHeadersSchema = zod_1.default.object({ |
@@ -40,2 +40,6 @@ /// <reference types="node" /> | ||
}>; | ||
export type BackupCredentials = Readonly<{ | ||
MESSAGES: Credentials; | ||
MEDIA: Credentials; | ||
}>; | ||
export type ChallengeResponse = Readonly<{ | ||
@@ -264,3 +268,3 @@ code: number; | ||
issueExpiringProfileKeyCredential({ aci, profileKeyCommitment }: Device, request: ProfileKeyCredentialRequest): Promise<Buffer | undefined>; | ||
setBackupId({ aci }: Device, { backupAuthCredentialRequest }: SetBackupId): Promise<void>; | ||
setBackupId({ aci }: Device, { messagesBackupAuthCredentialRequest, mediaBackupAuthCredentialRequest, }: SetBackupId): Promise<void>; | ||
setBackupKey(headers: BackupHeaders, { backupIdPublicKey }: SetBackupKey): Promise<void>; | ||
@@ -275,3 +279,3 @@ refreshBackup(headers: BackupHeaders): Promise<void>; | ||
authorizeBackupCDN(backupId: string, password: string): Promise<boolean>; | ||
getBackupCredentials({ aci, backupLevel }: Device, range: CredentialsRange): Promise<Credentials | undefined>; | ||
getBackupCredentials({ aci, backupLevel }: Device, range: CredentialsRange): Promise<BackupCredentials | undefined>; | ||
protected onNewBackupMediaObject(backupId: string, media: BackupMediaObject): Promise<void>; | ||
@@ -278,0 +282,0 @@ protected abstract backupTransitAttachments(backupId: string, batch: BackupMediaBatch): Promise<Array<BackupMediaBatchResponse>>; |
@@ -11,2 +11,4 @@ "use strict"; | ||
const zkgroup_1 = require("@signalapp/libsignal-client/zkgroup"); | ||
// TODO(indutny): wait for libsignal release | ||
const BackupCredentialType_1 = __importDefault(require("@signalapp/libsignal-client/dist/zkgroup/backups/BackupCredentialType")); | ||
const assert_1 = __importDefault(require("assert")); | ||
@@ -788,5 +790,7 @@ const crypto_1 = __importDefault(require("crypto")); | ||
} | ||
async setBackupId({ aci }, { backupAuthCredentialRequest }) { | ||
const req = new zkgroup_1.BackupAuthCredentialRequest(backupAuthCredentialRequest); | ||
this.backupAuthReqByAci.set(aci, req); | ||
async setBackupId({ aci }, { messagesBackupAuthCredentialRequest, mediaBackupAuthCredentialRequest, }) { | ||
this.backupAuthReqByAci.set(aci, { | ||
messages: new zkgroup_1.BackupAuthCredentialRequest(messagesBackupAuthCredentialRequest), | ||
media: new zkgroup_1.BackupAuthCredentialRequest(mediaBackupAuthCredentialRequest), | ||
}); | ||
} | ||
@@ -890,5 +894,12 @@ async setBackupKey(headers, { backupIdPublicKey }) { | ||
} | ||
return this.issueCredentials(range, (redemptionTime) => { | ||
return req.issueCredential(redemptionTime, backupLevel, this.backupServerSecret); | ||
const messages = this.issueCredentials(range, (redemptionTime) => { | ||
return req.messages.issueCredential(redemptionTime, zkgroup_1.BackupLevel.Free, BackupCredentialType_1.default.Messages, this.backupServerSecret); | ||
}); | ||
const media = this.issueCredentials(range, (redemptionTime) => { | ||
return req.media.issueCredential(redemptionTime, backupLevel, BackupCredentialType_1.default.Media, this.backupServerSecret); | ||
}); | ||
return { | ||
MESSAGES: messages, | ||
MEDIA: media, | ||
}; | ||
} | ||
@@ -895,0 +906,0 @@ async onNewBackupMediaObject(backupId, media) { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3946544
72791
+ Added@signalapp/libsignal-client@0.60.2(transitive)
- Removed@signalapp/libsignal-client@0.58.3(transitive)