Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fetch local certificate root [WPB-3338] #5863

Merged
merged 7 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
OidcChallengeResponseSchema,
GetCertificateResponseData,
GetCertificateResponseSchema,
LocalCertificateRootResponseSchema,
} from './schema';

import {AcmeChallenge, AcmeDirectory} from '../../E2EIService.types';
Expand All @@ -57,10 +58,16 @@ export class AcmeService {
private readonly axiosInstance: AxiosInstance = axios.create();
private readonly url = {
DIRECTORY: '/directory',
ROOTS: '/roots.pem',
};

constructor(private discoveryUrl: string) {}

private get acmeBaseUrl() {
const {origin} = new URL(this.discoveryUrl);
return origin;
}

// ############ Internal Functions ############

private extractNonce(headers: any): ResponseHeaderNonce['replay-nonce'] {
Expand Down Expand Up @@ -114,6 +121,12 @@ export class AcmeService {
}
}

public async getLocalCertificateRoot(): Promise<string> {
const {data} = await this.axiosInstance.get(`${this.acmeBaseUrl}${this.url.ROOTS}`);
const localCertificateRoot = LocalCertificateRootResponseSchema.parse(data);
return localCertificateRoot;
}

public async getInitialNonce(url: AcmeDirectory['newNonce']): GetInitialNonceReturnValue {
try {
const {headers} = await this.axiosInstance.head(url);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const DirectoryResponseSchema = z.object({
});
export type DirectoryResponseData = z.infer<typeof DirectoryResponseSchema>;

export const LocalCertificateRootResponseSchema = nonOptionalString;
export type LocalCertificateRootResonseData = z.infer<typeof LocalCertificateRootResponseSchema>;

export const NewAccountResponseSchema = z.object({
status: nonOptionalString,
orders: nonOptionalUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import {Decoder} from 'bazinga64';

import {Ciphersuite, CoreCrypto, E2eiConversationState, WireIdentity, DeviceStatus} from '@wireapp/core-crypto';

import {AcmeService} from './Connection';
import {getE2EIClientId} from './Helper';
import {E2EIStorage} from './Storage/E2EIStorage';

import {ClientService} from '../../../client';
import {parseFullQualifiedClientId} from '../../../util/fullyQualifiedClientIdUtils';
import {LocalStorageStore} from '../../../util/LocalStorageStore';

export type DeviceIdentity = Omit<WireIdentity, 'free' | 'status'> & {status?: DeviceStatus; deviceId: string};

Expand Down Expand Up @@ -125,4 +127,44 @@ export class E2EIServiceExternal {
}
return typeof client.mls_public_keys.ed25519 !== 'string' || client.mls_public_keys.ed25519.length === 0;
}

private async registerLocalCertificateRoot(connection: AcmeService): Promise<string> {
const localCertificateRoot = await connection.getLocalCertificateRoot();
await this.coreCryptoClient.e2eiRegisterAcmeCA(localCertificateRoot);

return localCertificateRoot;
}

/**
* This function is used to register different server certificates in CoreCrypto.
*
* 1. Root Certificate: This is the root certificate of the server.
* - It must only be registered once.
* - It must be the first certificate to be registered. Nothing else will work
*
* 2. Intermediate Certificate: This is the intermediate certificate of the server. It must be updated every 24 hours.
* - It must be registered after the root certificate.
* - It must be updated every 24 hours.
*
* Both must be registered before the first enrollment.
*
* @param discoveryUrl
*/
public async registerServerCertificates(discoveryUrl: string): Promise<void> {
const ROOT_CA_KEY = 'e2ei_root-registered';
const store = LocalStorageStore(ROOT_CA_KEY);
const acmeService = new AcmeService(discoveryUrl);

// Register root certificate if not already registered
if (!store.has(ROOT_CA_KEY)) {
try {
await this.registerLocalCertificateRoot(acmeService);
store.add(ROOT_CA_KEY, 'true');
} catch (error) {
console.error('Failed to register root certificate', error);
}
}

// Register intermediate certificate and update it every 24 hours
}
}
Loading