Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
127 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { binaryStringToArray, decodeBase64 } from 'pmcrypto'; | ||
|
||
const MANDATORY_FIELDS = ['keydata', 'addr']; | ||
const OPTIONAL_FIELDS = ['prefer-encrypt']; | ||
const CRITICAL_FIELDS = OPTIONAL_FIELDS.concat(MANDATORY_FIELDS); | ||
|
||
// Parse according to https://autocrypt.org/level1.html#the-autocrypt-header | ||
export const getParsedAutocryptHeader = (header = '', sender = '') => { | ||
let invalid = false; | ||
|
||
const result = Object.fromEntries( | ||
header | ||
.split(';') | ||
.map((keyValue) => { | ||
const trimmedKeyValue = keyValue.trim(); | ||
|
||
// For ease of parsing, the keydata attribute MUST be the last attribute in this header. Avoid splitting by = since it's base64 | ||
if (trimmedKeyValue.startsWith('keydata=')) { | ||
try { | ||
const keydataStringValue = trimmedKeyValue.slice('keydata='.length); | ||
const keydataValue = binaryStringToArray(decodeBase64(keydataStringValue)); | ||
return ['keydata', keydataValue]; | ||
} catch (e) { | ||
return ['', '']; | ||
} | ||
} | ||
|
||
const [parsedKey = '', parsedValue = ''] = keyValue.split('='); | ||
|
||
const key = parsedKey.trim(); | ||
|
||
// It MUST treat the entire Autocrypt header as invalid if it encounters a “critical” attribute that it doesn’t support. | ||
if (!CRITICAL_FIELDS.includes(key) && !key.startsWith('_')) { | ||
invalid = true; | ||
return ['', '']; | ||
} | ||
|
||
return [key, parsedValue.trim()]; | ||
}) | ||
.filter(([key, value]) => { | ||
return key && value; | ||
}) | ||
); | ||
|
||
// The mandatory fields must be present. | ||
if (MANDATORY_FIELDS.some((field) => !result[field]) || invalid) { | ||
return; | ||
} | ||
|
||
// If addr differs from the addr in the From header, the entire Autocrypt header MUST be treated as invalid. | ||
if (result.addr.toLowerCase() !== sender.toLowerCase()) { | ||
return; | ||
} | ||
|
||
// The prefer-encrypt attribute is optional and can only occur with the value mutual. | ||
// Its presence in the Autocrypt header indicates an agreement to enable encryption by default. | ||
if (result['prefer-encrypt'] !== 'mutual') { | ||
return; | ||
} | ||
|
||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { getParsedAutocryptHeader } from '../../../../src/app/message/services/autocryptHelper'; | ||
|
||
const validKeyData = { | ||
base64: ` xjMEYAffqRYJKwYBBAHaRw8BAQdAFKaMT9wf5w6gMeW6X+6CSKCwTw5ohqESZQXzNfHG+CrN FDxwcm90b25xYUB5YWhvby5jb20+wncEEBYKAB8FAmAH36kGCwkHCAMCBBUICgIDFgIBAhkB AhsDAh4BAAoJEHhS0KVKBiDsdGgBAMzShBIHmMcpfR9wfXQdJZJBsO/3NTqJfpR6lQM7b0Yh AQDmVdb5v9XofabzXILvXFCsY6G8m2enwfCZw00YK/C0Dc44BGAH36kSCisGAQQBl1UBBQEB B0CnB3qq73mdvkEfyixD+hAk3+5vW/Gg5rZaPoV0gixGOwMBCAfCYQQYFggACQUCYAffqQIb DAAKCRB4UtClSgYg7On/AQDAh4kb5SvbWpxvAj2XJjSD3VnoTq4mXiYVX+5porb2XgEAijZr EgjyGGjkRTwRZ7+ufgn+Qfvk/6+uc7/3efwlngA=`, | ||
uint8array: new Uint8Array([198, 51, 4, 96, 7, 223, 169, 22, 9, 43, 6, 1, 4, 1, 218, 71, 15, 1, 1, 7, 64, 20, 166, 140, 79, 220, 31, 231, 14, 160, 49, 229, 186, 95, 238, 130, 72, 160, 176, 79, 14, 104, 134, 161, 18, 101, 5, 243, 53, 241, 198, 248, 42, 205, 20, 60, 112, 114, 111, 116, 111, 110, 113, 97, 64, 121, 97, 104, 111, 111, 46, 99, 111, 109, 62, 194, 119, 4, 16, 22, 10, 0, 31, 5, 2, 96, 7, 223, 169, 6, 11, 9, 7, 8, 3, 2, 4, 21, 8, 10, 2, 3, 22, 2, 1, 2, 25, 1, 2, 27, 3, 2, 30, 1, 0, 10, 9, 16, 120, 82, 208, 165, 74, 6, 32, 236, 116, 104, 1, 0, 204, 210, 132, 18, 7, 152, 199, 41, 125, 31, 112, 125, 116, 29, 37, 146, 65, 176, 239, 247, 53, 58, 137, 126, 148, 122, 149, 3, 59, 111, 70, 33, 1, 0, 230, 85, 214, 249, 191, 213, 232, 125, 166, 243, 92, 130, 239, 92, 80, 172, 99, 161, 188, 155, 103, 167, 193, 240, 153, 195, 77, 24, 43, 240, 180, 13, 206, 56, 4, 96, 7, 223, 169, 18, 10, 43, 6, 1, 4, 1, 151, 85, 1, 5, 1, 1, 7, 64, 167, 7, 122, 170, 239, 121, 157, 190, 65, 31, 202, 44, 67, 250, 16, 36, 223, 238, 111, 91, 241, 160, 230, 182, 90, 62, 133, 116, 130, 44, 70, 59, 3, 1, 8, 7, 194, 97, 4, 24, 22, 8, 0, 9, 5, 2, 96, 7, 223, 169, 2, 27, 12, 0, 10, 9, 16, 120, 82, 208, 165, 74, 6, 32, 236, 233, 255, 1, 0, 192, 135, 137, 27, 229, 43, 219, 90, 156, 111, 2, 61, 151, 38, 52, 131, 221, 89, 232, 78, 174, 38, 94, 38, 21, 95, 238, 105, 162, 182, 246, 94, 1, 0, 138, 54, 107, 18, 8, 242, 24, 104, 228, 69, 60, 17, 103, 191, 174, 126, 9, 254, 65, 251, 228, 255, 175, 174, 115, 191, 247, 121, 252, 37, 158, 0]) | ||
}; | ||
|
||
describe('autocrypt helper', () => { | ||
it('should parse a valid string', () => { | ||
const result = `addr=test@yahoo.com; prefer-encrypt=mutual; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual({ | ||
addr: 'test@yahoo.com', | ||
'prefer-encrypt': 'mutual', | ||
keydata: validKeyData.uint8array | ||
}); | ||
}); | ||
|
||
it('should not parse a valid string that does not contain prefer-encrypt', () => { | ||
const result = `addr=test@yahoo.com; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual(undefined); | ||
}); | ||
|
||
it('should not parse a valid string that contains an invalid prefer-encrypt', () => { | ||
const result = `addr=test@yahoo.com; _other=test; prefer-encrypt=none; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual(undefined); | ||
}); | ||
|
||
it('should not parse an invalid string that contains critical unknown attributes', () => { | ||
const result = `addr=test@yahoo.com; other=test; prefer-encrypt=mutual; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual(undefined); | ||
}); | ||
|
||
it('should not parse an invalid string that does not contain addr', () => { | ||
const result = `other=test; prefer-encrypt=mutual; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual(undefined); | ||
}); | ||
|
||
it('should not parse an invalid string', () => { | ||
const result = `addr=test@yahoo.com; prefer-encrypt=none; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual(undefined); | ||
}); | ||
|
||
it('should not parse an unknown sender', () => { | ||
const result = `addr=unknown@yahoo.com; prefer-encrypt=none; keydata=${validKeyData.base64}`; | ||
expect(getParsedAutocryptHeader(result, 'test@yahoo.com')).toEqual(undefined); | ||
}); | ||
}); |
6687fbb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/app/message/services/attachedPublicKey.js
6687fbb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
6687fbb