1 import { binaryStringToArray, decodeBase64 } from '@proton/crypto/lib/utils';
3 const MANDATORY_FIELDS = ['keydata', 'addr'];
4 const OPTIONAL_FIELDS = ['prefer-encrypt'];
5 const CRITICAL_FIELDS = OPTIONAL_FIELDS.concat(MANDATORY_FIELDS);
7 export interface AutocryptHeader {
10 'prefer-encrypt'?: 'mutual';
11 // Non-critical optional fields
12 [_key: string]: undefined | string | Uint8Array;
15 // Parse according to https://autocrypt.org/level1.html#the-autocrypt-header
16 export const getParsedAutocryptHeader = (header = '', sender = ''): AutocryptHeader | undefined => {
19 const result: AutocryptHeader = Object.fromEntries(
23 const trimmedKeyValue = keyValue.trim();
25 // For ease of parsing, the keydata attribute MUST be the last attribute in this header. Avoid splitting by = since it's base64
26 if (trimmedKeyValue.startsWith('keydata=')) {
28 const keydataStringValue = trimmedKeyValue.slice('keydata='.length);
29 const keydataValue = binaryStringToArray(decodeBase64(keydataStringValue));
30 return ['keydata', keydataValue];
36 const [parsedKey = '', parsedValue = ''] = keyValue.split('=');
38 const key = parsedKey.trim();
40 // It MUST treat the entire Autocrypt header as invalid if it encounters a “critical” attribute that it doesn’t support.
41 if (!CRITICAL_FIELDS.includes(key) && !key.startsWith('_')) {
46 return [key, parsedValue.trim()];
48 .filter(([key, value]) => {
53 // The mandatory fields must be present.
54 if (MANDATORY_FIELDS.some((field) => !result[field]) || invalid) {
58 // If addr differs from the addr in the From header, the entire Autocrypt header MUST be treated as invalid.
59 if (result.addr.toLowerCase() !== sender.toLowerCase()) {
63 // The prefer-encrypt attribute is optional and can only occur with the value mutual.
64 // Its presence in the Autocrypt header indicates an agreement to enable encryption by default.
65 if (result['prefer-encrypt'] !== 'mutual') {