diff --git a/keyserver/flow-typed/npm/@matrix-org/olm_vx.x.x.js b/keyserver/flow-typed/npm/@matrix-org/olm_vx.x.x.js new file mode 100644 index 000000000..04ee34568 --- /dev/null +++ b/keyserver/flow-typed/npm/@matrix-org/olm_vx.x.x.js @@ -0,0 +1,171 @@ +// flow-typed signature: 085f002da86534cfd8cee47ffa99dd67 +// flow-typed version: <>/@matrix-org/olm_v3.2.4/flow_v0.182.0 + +declare module '@matrix-org/olm' { + +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + declare export class Account { + constructor(): void; + free(): void; + create(): void; + identity_keys(): string; + sign(message: string | Uint8Array): string; + one_time_keys(): string; + mark_keys_as_published(): void; + max_number_of_one_time_keys(): number; + generate_one_time_keys(number_of_keys: number): void; + remove_one_time_keys(session: Session): void; + generate_fallback_key(): void; + fallback_key(): string; + unpublished_fallback_key(): string; + forget_old_fallback_key(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + } + + declare export type EncryptResult = { + +type: 0 | 1, // 0: PreKey, 1: Message + +body: string, + }; + declare export class Session { + constructor(): void; + free(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + create_outbound( + account: Account, + their_identity_key: string, + their_one_time_key: string, + ): void; + create_inbound(account: Account, one_time_key_message: string): void; + create_inbound_from( + account: Account, + identity_key: string, + one_time_key_message: string, + ): void; + session_id(): string; + has_received_message(): boolean; + matches_inbound(one_time_key_message: string): boolean; + matches_inbound_from( + identity_key: string, + one_time_key_message: string, + ): boolean; + encrypt(plaintext: string): EncryptResult; + decrypt(message_type: number, message: string): string; + describe(): string; + } + + declare export class Utility { + constructor(): void; + free(): void; + sha256(input: string | Uint8Array): string; + ed25519_verify( + key: string, + message: string | Uint8Array, + signature: string, + ): void; + } + + declare export type DecryptResult = { + +message_index: string, + +plaintext: string, + }; + + declare export class InboundGroupSession { + constructor(): void; + free(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + create(session_key: string): string; + import_session(session_key: string): string; + decrypt(message: string): DecryptResult; + session_id(): string; + first_known_index(): number; + export_session(message_index: number): string; + } + + declare export class OutboundGroupSession { + constructor(): void; + free(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + create(): void; + encrypt(plaintext: string): string; + session_id(): string; + session_key(): string; + message_index(): number; + } + + declare export type PkEncryptionEncryptResult = { + +ciphertext: string, + +mac: string, + +ephemeral: string, + }; + + declare export class PkEncryption { + constructor(): void; + free(): void; + set_recipient_key(key: string): void; + encrypt(plaintext: string): PkEncryptionEncryptResult; + } + + declare export class PkDecryption { + constructor(): void; + free(): void; + init_with_private_key(key: Uint8Array): string; + generate_key(): string; + get_private_key(): Uint8Array; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): string; + decrypt(ephemeral_key: string, mac: string, ciphertext: string): string; + } + + declare export class PkSigning { + constructor(): void; + free(): void; + init_with_seed(seed: Uint8Array): string; + generate_seed(): Uint8Array; + sign(message: string): string; + } + + declare export class SAS { + constructor(): void; + free(): void; + get_pubkey(): string; + set_their_key(their_key: string): void; + generate_bytes(info: string, length: number): Uint8Array; + calculate_mac(input: string, info: string): string; + calculate_mac_fixed_base64(input: string, info: string): string; + calculate_mac_long_kdf(input: string, info: string): string; + } + + declare export function init(opts?: Object): Promise; + + declare export function get_library_version(): [number, number, number]; + + declare export var PRIVATE_KEY_LENGTH: number; + + declare export default { + init: typeof init, + get_library_version: typeof get_library_version, + PRIVATE_KEY_LENGTH: typeof PRIVATE_KEY_LENGTH, + Account: typeof Account, + Utility: typeof Utility, + }; + +} diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js index f6cca5239..230a0f1a6 100644 --- a/keyserver/src/responders/user-responders.js +++ b/keyserver/src/responders/user-responders.js @@ -1,686 +1,686 @@ // @flow +import type { Utility as OlmUtility } from '@matrix-org/olm'; import invariant from 'invariant'; import { getRustAPI } from 'rust-node-addon'; import { ErrorTypes, SiweMessage } from 'siwe'; import t from 'tcomb'; import bcrypt from 'twin-bcrypt'; import { baseLegalPolicies, policies } from 'lib/facts/policies.js'; import { hasMinCodeVersion } from 'lib/shared/version-utils.js'; import type { ResetPasswordRequest, LogOutResponse, DeleteAccountRequest, RegisterResponse, RegisterRequest, LogInResponse, LogInRequest, UpdatePasswordRequest, UpdateUserSettingsRequest, PolicyAcknowledgmentRequest, } from 'lib/types/account-types.js'; import { userSettingsTypes, notificationTypeValues, logInActionSources, } from 'lib/types/account-types.js'; import type { IdentityKeysBlob, SignedIdentityKeysBlob, } from 'lib/types/crypto-types.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import { defaultNumberPerThread } from 'lib/types/message-types.js'; import type { SIWEAuthRequest, SIWEMessage, SIWESocialProof, } from 'lib/types/siwe-types.js'; import type { SubscriptionUpdateRequest, SubscriptionUpdateResponse, } from 'lib/types/subscription-types.js'; import type { PasswordUpdate } from 'lib/types/user-types.js'; import { identityKeysBlobValidator, signedIdentityKeysBlobValidator, } from 'lib/utils/crypto-utils.js'; import { ServerError } from 'lib/utils/errors.js'; import { values } from 'lib/utils/objects.js'; import { promiseAll } from 'lib/utils/promises.js'; import { getPublicKeyFromSIWEStatement, isValidSIWEMessage, isValidSIWEStatementWithPublicKey, primaryIdentityPublicKeyRegex, } from 'lib/utils/siwe-utils.js'; import { tShape, tPlatformDetails, tPassword, tEmail, tOldValidUsername, tRegex, } from 'lib/utils/validation-utils.js'; import { entryQueryInputValidator, newEntryQueryInputValidator, normalizeCalendarQuery, verifyCalendarQueryThreadIDs, } from './entry-responders.js'; import { handleAsyncPromise } from './handlers.js'; import { createAccount, processSIWEAccountCreation, } from '../creators/account-creator.js'; import { dbQuery, SQL } from '../database/database.js'; import { deleteAccount } from '../deleters/account-deleters.js'; import { deleteCookie } from '../deleters/cookie-deleters.js'; import { checkAndInvalidateSIWENonceEntry } from '../deleters/siwe-nonce-deleters.js'; import { fetchEntryInfos } from '../fetchers/entry-fetchers.js'; import { fetchMessageInfos } from '../fetchers/message-fetchers.js'; import { fetchNotAcknowledgedPolicies } from '../fetchers/policy-acknowledgment-fetchers.js'; import { fetchThreadInfos } from '../fetchers/thread-fetchers.js'; import { fetchKnownUserInfos, fetchLoggedInUserInfo, fetchUserIDForEthereumAddress, } from '../fetchers/user-fetchers.js'; import { createNewAnonymousCookie, createNewUserCookie, setNewSession, } from '../session/cookies.js'; import type { Viewer } from '../session/viewer.js'; import { accountUpdater, checkAndSendVerificationEmail, checkAndSendPasswordResetEmail, updatePassword, updateUserSettings, } from '../updaters/account-updaters.js'; import { userSubscriptionUpdater } from '../updaters/user-subscription-updaters.js'; import { viewerAcknowledgmentUpdater } from '../updaters/viewer-acknowledgment-updater.js'; -import { getOLMUtility } from '../utils/olm-utils.js'; -import type { OLMUtility } from '../utils/olm-utils.js'; +import { getOlmUtility } from '../utils/olm-utils.js'; import { validateInput } from '../utils/validation-utils.js'; const subscriptionUpdateRequestInputValidator = tShape({ threadID: t.String, updatedFields: tShape({ pushNotifs: t.maybe(t.Boolean), home: t.maybe(t.Boolean), }), }); async function userSubscriptionUpdateResponder( viewer: Viewer, input: any, ): Promise { const request: SubscriptionUpdateRequest = input; await validateInput(viewer, subscriptionUpdateRequestInputValidator, request); const threadSubscription = await userSubscriptionUpdater(viewer, request); return { threadSubscription }; } const accountUpdateInputValidator = tShape({ updatedFields: tShape({ email: t.maybe(tEmail), password: t.maybe(tPassword), }), currentPassword: tPassword, }); async function passwordUpdateResponder( viewer: Viewer, input: any, ): Promise { const request: PasswordUpdate = input; await validateInput(viewer, accountUpdateInputValidator, request); await accountUpdater(viewer, request); } async function sendVerificationEmailResponder(viewer: Viewer): Promise { await validateInput(viewer, null, null); await checkAndSendVerificationEmail(viewer); } const resetPasswordRequestInputValidator = tShape({ usernameOrEmail: t.union([tEmail, tOldValidUsername]), }); async function sendPasswordResetEmailResponder( viewer: Viewer, input: any, ): Promise { const request: ResetPasswordRequest = input; await validateInput(viewer, resetPasswordRequestInputValidator, request); await checkAndSendPasswordResetEmail(request); } async function logOutResponder(viewer: Viewer): Promise { await validateInput(viewer, null, null); if (viewer.loggedIn) { const [anonymousViewerData] = await Promise.all([ createNewAnonymousCookie({ platformDetails: viewer.platformDetails, deviceToken: viewer.deviceToken, }), deleteCookie(viewer.cookieID), ]); viewer.setNewCookie(anonymousViewerData); } return { currentUserInfo: { id: viewer.id, anonymous: true, }, }; } const deleteAccountRequestInputValidator = tShape({ password: t.maybe(tPassword), }); async function accountDeletionResponder( viewer: Viewer, input: any, ): Promise { const request: DeleteAccountRequest = input; await validateInput(viewer, deleteAccountRequestInputValidator, request); const result = await deleteAccount(viewer, request); invariant(result, 'deleteAccount should return result if handed request'); return result; } const deviceTokenUpdateRequestInputValidator = tShape({ deviceType: t.maybe(t.enums.of(['ios', 'android'])), deviceToken: t.String, }); const registerRequestInputValidator = tShape({ username: t.String, email: t.maybe(tEmail), password: tPassword, calendarQuery: t.maybe(newEntryQueryInputValidator), deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), platformDetails: tPlatformDetails, // We include `primaryIdentityPublicKey` to avoid breaking // old clients, but we no longer do anything with it. primaryIdentityPublicKey: t.maybe(tRegex(primaryIdentityPublicKeyRegex)), signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), }); async function accountCreationResponder( viewer: Viewer, input: any, ): Promise { const request: RegisterRequest = input; await validateInput(viewer, registerRequestInputValidator, request); const { signedIdentityKeysBlob } = request; if (signedIdentityKeysBlob) { const identityKeys: IdentityKeysBlob = JSON.parse( signedIdentityKeysBlob.payload, ); if (!identityKeysBlobValidator.is(identityKeys)) { throw new ServerError('invalid_identity_keys_blob'); } - const olmUtil: OLMUtility = getOLMUtility(); + const olmUtil: OlmUtility = getOlmUtility(); try { olmUtil.ed25519_verify( identityKeys.primaryIdentityPublicKeys.ed25519, signedIdentityKeysBlob.payload, signedIdentityKeysBlob.signature, ); } catch (e) { throw new ServerError('invalid_signature'); } } return await createAccount(viewer, request); } type ProcessSuccessfulLoginParams = { +viewer: Viewer, +input: any, +userID: string, +calendarQuery: ?CalendarQuery, +socialProof?: ?SIWESocialProof, +signedIdentityKeysBlob?: ?SignedIdentityKeysBlob, }; async function processSuccessfulLogin( params: ProcessSuccessfulLoginParams, ): Promise { const { viewer, input, userID, calendarQuery, socialProof, signedIdentityKeysBlob, } = params; const request: LogInRequest = input; const newServerTime = Date.now(); const deviceToken = request.deviceTokenUpdateRequest ? request.deviceTokenUpdateRequest.deviceToken : viewer.deviceToken; const [userViewerData, notAcknowledgedPolicies] = await Promise.all([ createNewUserCookie(userID, { platformDetails: request.platformDetails, deviceToken, socialProof, signedIdentityKeysBlob, }), fetchNotAcknowledgedPolicies(userID, baseLegalPolicies), deleteCookie(viewer.cookieID), ]); viewer.setNewCookie(userViewerData); if ( notAcknowledgedPolicies.length && hasMinCodeVersion(viewer.platformDetails, 181) ) { const currentUserInfo = await fetchLoggedInUserInfo(viewer); return { notAcknowledgedPolicies, currentUserInfo: currentUserInfo, rawMessageInfos: [], truncationStatuses: {}, userInfos: [], rawEntryInfos: [], serverTime: 0, cookieChange: { threadInfos: {}, userInfos: [], }, }; } if (calendarQuery) { await setNewSession(viewer, calendarQuery, newServerTime); } const threadCursors = {}; for (const watchedThreadID of request.watchedIDs) { threadCursors[watchedThreadID] = null; } const messageSelectionCriteria = { threadCursors, joinedThreads: true }; const [ threadsResult, messagesResult, entriesResult, userInfos, currentUserInfo, ] = await Promise.all([ fetchThreadInfos(viewer), fetchMessageInfos(viewer, messageSelectionCriteria, defaultNumberPerThread), calendarQuery ? fetchEntryInfos(viewer, [calendarQuery]) : undefined, fetchKnownUserInfos(viewer), fetchLoggedInUserInfo(viewer), ]); const rawEntryInfos = entriesResult ? entriesResult.rawEntryInfos : null; const response: LogInResponse = { currentUserInfo, rawMessageInfos: messagesResult.rawMessageInfos, truncationStatuses: messagesResult.truncationStatuses, serverTime: newServerTime, userInfos: values(userInfos), cookieChange: { threadInfos: threadsResult.threadInfos, userInfos: [], }, }; if (rawEntryInfos) { return { ...response, rawEntryInfos, }; } return response; } const logInRequestInputValidator = tShape({ username: t.maybe(t.String), usernameOrEmail: t.maybe(t.union([tEmail, tOldValidUsername])), password: tPassword, watchedIDs: t.list(t.String), calendarQuery: t.maybe(entryQueryInputValidator), deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), platformDetails: tPlatformDetails, source: t.maybe(t.enums.of(values(logInActionSources))), // We include `primaryIdentityPublicKey` to avoid breaking // old clients, but we no longer do anything with it. primaryIdentityPublicKey: t.maybe(tRegex(primaryIdentityPublicKeyRegex)), signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), }); async function logInResponder( viewer: Viewer, input: any, ): Promise { await validateInput(viewer, logInRequestInputValidator, input); const request: LogInRequest = input; let identityKeys: ?IdentityKeysBlob; const { signedIdentityKeysBlob } = request; if (signedIdentityKeysBlob) { identityKeys = JSON.parse(signedIdentityKeysBlob.payload); - const olmUtil: OLMUtility = getOLMUtility(); + const olmUtil: OlmUtility = getOlmUtility(); try { olmUtil.ed25519_verify( identityKeys.primaryIdentityPublicKeys.ed25519, signedIdentityKeysBlob.payload, signedIdentityKeysBlob.signature, ); } catch (e) { throw new ServerError('invalid_signature'); } } const calendarQuery = request.calendarQuery ? normalizeCalendarQuery(request.calendarQuery) : null; const promises = {}; if (calendarQuery) { promises.verifyCalendarQueryThreadIDs = verifyCalendarQueryThreadIDs(calendarQuery); } const username = request.username ?? request.usernameOrEmail; if (!username) { if (hasMinCodeVersion(viewer.platformDetails, 150)) { throw new ServerError('invalid_credentials'); } else { throw new ServerError('invalid_parameters'); } } const userQuery = SQL` SELECT id, hash, username FROM users WHERE LCASE(username) = LCASE(${username}) `; promises.userQuery = dbQuery(userQuery); const { userQuery: [userResult], } = await promiseAll(promises); if (userResult.length === 0) { if (hasMinCodeVersion(viewer.platformDetails, 150)) { throw new ServerError('invalid_credentials'); } else { throw new ServerError('invalid_parameters'); } } const userRow = userResult[0]; if (!userRow.hash || !bcrypt.compareSync(request.password, userRow.hash)) { throw new ServerError('invalid_credentials'); } const id = userRow.id.toString(); if (identityKeys && signedIdentityKeysBlob) { const constIdentityKeys = identityKeys; handleAsyncPromise( (async () => { const rustAPI = await getRustAPI(); try { await rustAPI.loginUserPake( id, constIdentityKeys.primaryIdentityPublicKeys.ed25519, request.password, signedIdentityKeysBlob, ); } catch (e) { if (e.code === 'InvalidArg' && e.message === 'user not found') { await rustAPI.registerUser( id, constIdentityKeys.primaryIdentityPublicKeys.ed25519, username, request.password, signedIdentityKeysBlob, ); } } })(), ); } return await processSuccessfulLogin({ viewer, input, userID: id, calendarQuery, signedIdentityKeysBlob, }); } const siweAuthRequestInputValidator = tShape({ signature: t.String, message: t.String, calendarQuery: entryQueryInputValidator, deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), platformDetails: tPlatformDetails, watchedIDs: t.list(t.String), signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator), }); async function siweAuthResponder( viewer: Viewer, input: any, ): Promise { await validateInput(viewer, siweAuthRequestInputValidator, input); const request: SIWEAuthRequest = input; const { message, signature, deviceTokenUpdateRequest, platformDetails, signedIdentityKeysBlob, } = request; const calendarQuery = normalizeCalendarQuery(request.calendarQuery); // 1. Ensure that `message` is a well formed Comm SIWE Auth message. const siweMessage: SIWEMessage = new SiweMessage(message); if (!isValidSIWEMessage(siweMessage)) { throw new ServerError('invalid_parameters'); } // 2. Ensure that the `nonce` exists in the `siwe_nonces` table // AND hasn't expired. If those conditions are met, delete the entry to // ensure that the same `nonce` can't be re-used in a future request. const wasNonceCheckedAndInvalidated = await checkAndInvalidateSIWENonceEntry( siweMessage.nonce, ); if (!wasNonceCheckedAndInvalidated) { throw new ServerError('invalid_parameters'); } // 3. Validate SIWEMessage signature and handle possible errors. try { await siweMessage.validate(signature); } catch (error) { if (error === ErrorTypes.EXPIRED_MESSAGE) { // Thrown when the `expirationTime` is present and in the past. throw new ServerError('expired_message'); } else if (error === ErrorTypes.INVALID_SIGNATURE) { // Thrown when the `validate()` function can't verify the message. throw new ServerError('invalid_signature'); } else if (error === ErrorTypes.MALFORMED_SESSION) { // Thrown when some required field is missing. throw new ServerError('malformed_session'); } else { throw new ServerError('unknown_error'); } } // 4. Pull `primaryIdentityPublicKey` out from SIWEMessage `statement`. // We expect it to be included for BOTH native and web clients. const { statement } = siweMessage; const primaryIdentityPublicKey = statement && isValidSIWEStatementWithPublicKey(statement) ? getPublicKeyFromSIWEStatement(statement) : null; if (!primaryIdentityPublicKey) { throw new ServerError('invalid_siwe_statement_public_key'); } // 5. Verify `signedIdentityKeysBlob.payload` with included `signature` // if `signedIdentityKeysBlob` was included in the `SIWEAuthRequest`. let identityKeys: ?IdentityKeysBlob; if (signedIdentityKeysBlob) { identityKeys = JSON.parse(signedIdentityKeysBlob.payload); if (!identityKeysBlobValidator.is(identityKeys)) { throw new ServerError('invalid_identity_keys_blob'); } - const olmUtil: OLMUtility = getOLMUtility(); + const olmUtil: OlmUtility = getOlmUtility(); try { olmUtil.ed25519_verify( identityKeys.primaryIdentityPublicKeys.ed25519, signedIdentityKeysBlob.payload, signedIdentityKeysBlob.signature, ); } catch (e) { throw new ServerError('invalid_signature'); } } // 6. Ensure that `primaryIdentityPublicKeys.ed25519` matches SIWE // statement `primaryIdentityPublicKey` if `identityKeys` exists. if ( identityKeys && identityKeys.primaryIdentityPublicKeys.ed25519 !== primaryIdentityPublicKey ) { throw new ServerError('primary_public_key_mismatch'); } // 7. Construct `SIWESocialProof` object with the stringified // SIWEMessage and the corresponding signature. const socialProof: SIWESocialProof = { siweMessage: siweMessage.toMessage(), siweMessageSignature: signature, }; // 8. Create account with call to `processSIWEAccountCreation(...)` // if address does not correspond to an existing user. let userID = await fetchUserIDForEthereumAddress(siweMessage.address); if (!userID) { const siweAccountCreationRequest = { address: siweMessage.address, calendarQuery, deviceTokenUpdateRequest, platformDetails, socialProof, }; userID = await processSIWEAccountCreation( viewer, siweAccountCreationRequest, ); } // 9. Try to double-write SIWE account info to the Identity service. const userIDCopy = userID; if (identityKeys && signedIdentityKeysBlob) { const identityKeysCopy = identityKeys; handleAsyncPromise( (async () => { const rustAPI = await getRustAPI(); await rustAPI.loginUserWallet( userIDCopy, identityKeysCopy.primaryIdentityPublicKeys.ed25519, siweMessage.toMessage(), signature, signedIdentityKeysBlob, JSON.stringify(socialProof), ); })(), ); } // 10. Complete login with call to `processSuccessfulLogin(...)`. return await processSuccessfulLogin({ viewer, input, userID, calendarQuery, socialProof, signedIdentityKeysBlob, }); } const updatePasswordRequestInputValidator = tShape({ code: t.String, password: tPassword, watchedIDs: t.list(t.String), calendarQuery: t.maybe(entryQueryInputValidator), deviceTokenUpdateRequest: t.maybe(deviceTokenUpdateRequestInputValidator), platformDetails: tPlatformDetails, }); async function oldPasswordUpdateResponder( viewer: Viewer, input: any, ): Promise { await validateInput(viewer, updatePasswordRequestInputValidator, input); const request: UpdatePasswordRequest = input; if (request.calendarQuery) { request.calendarQuery = normalizeCalendarQuery(request.calendarQuery); } return await updatePassword(viewer, request); } const updateUserSettingsInputValidator = tShape({ name: t.irreducible( userSettingsTypes.DEFAULT_NOTIFICATIONS, x => x === userSettingsTypes.DEFAULT_NOTIFICATIONS, ), data: t.enums.of(notificationTypeValues), }); async function updateUserSettingsResponder( viewer: Viewer, input: any, ): Promise { const request: UpdateUserSettingsRequest = input; await validateInput(viewer, updateUserSettingsInputValidator, request); return await updateUserSettings(viewer, request); } const policyAcknowledgmentRequestInputValidator = tShape({ policy: t.maybe(t.enums.of(policies)), }); async function policyAcknowledgmentResponder( viewer: Viewer, input: any, ): Promise { const request: PolicyAcknowledgmentRequest = input; await validateInput( viewer, policyAcknowledgmentRequestInputValidator, request, ); await viewerAcknowledgmentUpdater(viewer, request.policy); } export { userSubscriptionUpdateResponder, passwordUpdateResponder, sendVerificationEmailResponder, sendPasswordResetEmailResponder, logOutResponder, accountDeletionResponder, accountCreationResponder, logInResponder, siweAuthResponder, oldPasswordUpdateResponder, updateUserSettingsResponder, policyAcknowledgmentResponder, }; diff --git a/keyserver/src/utils/olm-utils.js b/keyserver/src/utils/olm-utils.js index 0803e43cf..796d5052b 100644 --- a/keyserver/src/utils/olm-utils.js +++ b/keyserver/src/utils/olm-utils.js @@ -1,38 +1,29 @@ // @flow import olm from '@matrix-org/olm'; +import type { Utility as OlmUtility } from '@matrix-org/olm'; import invariant from 'invariant'; import { importJSON } from './import-json.js'; type OlmConfig = { +picklingKey: string, +pickledAccount: string, }; async function getOlmConfig(): Promise { const olmConfig = await importJSON({ folder: 'secrets', name: 'olm_config' }); invariant(olmConfig, 'OLM config missing'); return olmConfig; } -export type OLMUtility = { - +free: () => void, - +sha256: (input: string | Uint8Array) => string, - +ed25519_verify: ( - key: string, - message: string | Uint8Array, - signature: string, - ) => void, -}; - -let cachedOLMUtility: OLMUtility; -function getOLMUtility(): OLMUtility { +let cachedOLMUtility: OlmUtility; +function getOlmUtility(): OlmUtility { if (cachedOLMUtility) { return cachedOLMUtility; } cachedOLMUtility = new olm.Utility(); return cachedOLMUtility; } -export { getOlmConfig, getOLMUtility }; +export { getOlmConfig, getOlmUtility }; diff --git a/web/flow-typed/npm/@matrix-org/olm_vx.x.x.js b/web/flow-typed/npm/@matrix-org/olm_vx.x.x.js new file mode 100644 index 000000000..04ee34568 --- /dev/null +++ b/web/flow-typed/npm/@matrix-org/olm_vx.x.x.js @@ -0,0 +1,171 @@ +// flow-typed signature: 085f002da86534cfd8cee47ffa99dd67 +// flow-typed version: <>/@matrix-org/olm_v3.2.4/flow_v0.182.0 + +declare module '@matrix-org/olm' { + +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + declare export class Account { + constructor(): void; + free(): void; + create(): void; + identity_keys(): string; + sign(message: string | Uint8Array): string; + one_time_keys(): string; + mark_keys_as_published(): void; + max_number_of_one_time_keys(): number; + generate_one_time_keys(number_of_keys: number): void; + remove_one_time_keys(session: Session): void; + generate_fallback_key(): void; + fallback_key(): string; + unpublished_fallback_key(): string; + forget_old_fallback_key(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + } + + declare export type EncryptResult = { + +type: 0 | 1, // 0: PreKey, 1: Message + +body: string, + }; + declare export class Session { + constructor(): void; + free(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + create_outbound( + account: Account, + their_identity_key: string, + their_one_time_key: string, + ): void; + create_inbound(account: Account, one_time_key_message: string): void; + create_inbound_from( + account: Account, + identity_key: string, + one_time_key_message: string, + ): void; + session_id(): string; + has_received_message(): boolean; + matches_inbound(one_time_key_message: string): boolean; + matches_inbound_from( + identity_key: string, + one_time_key_message: string, + ): boolean; + encrypt(plaintext: string): EncryptResult; + decrypt(message_type: number, message: string): string; + describe(): string; + } + + declare export class Utility { + constructor(): void; + free(): void; + sha256(input: string | Uint8Array): string; + ed25519_verify( + key: string, + message: string | Uint8Array, + signature: string, + ): void; + } + + declare export type DecryptResult = { + +message_index: string, + +plaintext: string, + }; + + declare export class InboundGroupSession { + constructor(): void; + free(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + create(session_key: string): string; + import_session(session_key: string): string; + decrypt(message: string): DecryptResult; + session_id(): string; + first_known_index(): number; + export_session(message_index: number): string; + } + + declare export class OutboundGroupSession { + constructor(): void; + free(): void; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): void; + create(): void; + encrypt(plaintext: string): string; + session_id(): string; + session_key(): string; + message_index(): number; + } + + declare export type PkEncryptionEncryptResult = { + +ciphertext: string, + +mac: string, + +ephemeral: string, + }; + + declare export class PkEncryption { + constructor(): void; + free(): void; + set_recipient_key(key: string): void; + encrypt(plaintext: string): PkEncryptionEncryptResult; + } + + declare export class PkDecryption { + constructor(): void; + free(): void; + init_with_private_key(key: Uint8Array): string; + generate_key(): string; + get_private_key(): Uint8Array; + pickle(key: string | Uint8Array): string; + unpickle(key: string | Uint8Array, pickle: string): string; + decrypt(ephemeral_key: string, mac: string, ciphertext: string): string; + } + + declare export class PkSigning { + constructor(): void; + free(): void; + init_with_seed(seed: Uint8Array): string; + generate_seed(): Uint8Array; + sign(message: string): string; + } + + declare export class SAS { + constructor(): void; + free(): void; + get_pubkey(): string; + set_their_key(their_key: string): void; + generate_bytes(info: string, length: number): Uint8Array; + calculate_mac(input: string, info: string): string; + calculate_mac_fixed_base64(input: string, info: string): string; + calculate_mac_long_kdf(input: string, info: string): string; + } + + declare export function init(opts?: Object): Promise; + + declare export function get_library_version(): [number, number, number]; + + declare export var PRIVATE_KEY_LENGTH: number; + + declare export default { + init: typeof init, + get_library_version: typeof get_library_version, + PRIVATE_KEY_LENGTH: typeof PRIVATE_KEY_LENGTH, + Account: typeof Account, + Utility: typeof Utility, + }; + +}