Skip to content

Commit 68f5191

Browse files
authored
decrypt/verifyMessage: by default, verify messages slightly in the future compared to server time (#200)
1 parent 588c1a2 commit 68f5191

File tree

5 files changed

+46
-6
lines changed

5 files changed

+46
-6
lines changed

lib/constants.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ export const MAX_ENC_HEADER_LENGTH = 1024;
1515
* Offset needed for key generation to ensure key (incl. certification signatures) validity
1616
* across different servers, which might have slightly mismatching server time
1717
*/
18-
export const DEFAULT_OFFSET = -60000;
18+
export const DEFAULT_KEY_GENERATION_OFFSET = -60000;
19+
/**
20+
* Offset needed for message verification to ensure validity across different servers,
21+
* which might have slightly mismatching server time
22+
*/
23+
export const DEFAULT_SIGNATURE_VERIFICATION_OFFSET = 60000;
1924

2025
export const ARGON2_PARAMS = { // from https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice
2126
RECOMMENDED: { passes: 1, parallelism: 4, memoryExponent: 19, tagLength: 32 },

lib/key/utils.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
generateSessionKey as openpgp_generateSessionKey
55
} from '../openpgp';
66
import { serverTime } from '../serverTime';
7-
import { DEFAULT_OFFSET } from '../constants';
7+
import { DEFAULT_KEY_GENERATION_OFFSET } from '../constants';
88
import { getSymmetricKeySize, getRandomBytes } from '../crypto/utils';
99
import encryptMessage from '../message/encrypt';
1010
import { SHA256 } from '../crypto/hash';
@@ -18,7 +18,7 @@ import { arrayToHexString } from '../utils';
1818
* @returns {Promise<Object>} generated key data and armored revocation certificate in the form:
1919
* { PrivateKey, PublicKey : String|Uint8Array|Object, revocationCertificate: String }
2020
*/
21-
export async function generateKey({ date = new Date(+serverTime() + DEFAULT_OFFSET), ...rest }) {
21+
export async function generateKey({ date = new Date(+serverTime() + DEFAULT_KEY_GENERATION_OFFSET), ...rest }) {
2222
return openpgp_generateKey({ date, ...rest });
2323
}
2424

lib/message/decrypt.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import { decrypt, readSignature } from '../openpgp';
33
import { serverTime } from '../serverTime';
44
import { getConfigForContextVerification } from './context';
55
import { handleVerificationResult } from './verify';
6+
import { DEFAULT_SIGNATURE_VERIFICATION_OFFSET } from '../constants';
67

78
export default async function decryptMessage({
8-
date = serverTime(), encryptedSignature, context, config = {}, ...options
9+
date = new Date(+serverTime() + DEFAULT_SIGNATURE_VERIFICATION_OFFSET),
10+
encryptedSignature,
11+
context,
12+
config = {},
13+
...options
914
}) {
1015
const sanitizedOptions = {
1116
...options,

lib/message/verify.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createMessage, verify, CleartextMessage } from '../openpgp';
2-
import { VERIFICATION_STATUS } from '../constants';
2+
import { DEFAULT_SIGNATURE_VERIFICATION_OFFSET, VERIFICATION_STATUS } from '../constants';
33
import { serverTime } from '../serverTime';
44
import { removeTrailingSpaces } from './utils';
55
import { ContextError, getConfigForContextVerification, isValidSignatureContext } from './context';
@@ -93,7 +93,7 @@ export async function verifyMessage({
9393
stripTrailingSpaces,
9494
context,
9595
config = {},
96-
date = serverTime(),
96+
date = new Date(+serverTime() + DEFAULT_SIGNATURE_VERIFICATION_OFFSET),
9797
...options
9898
}) {
9999
const dataType = binaryData ? 'binary' : 'text';

test/message/encryptMessage.spec.ts

+30
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,34 @@ describe('message encryption and decryption', () => {
349349
expect(await readToEnd(decrypted).then(arrayToBinaryString)).to.equal(inputData);
350350
expect(await verified).to.equal(VERIFICATION_STATUS.SIGNED_AND_VALID);
351351
});
352+
353+
it('it can decrypt and verify a message ten seconds in the future', async () => {
354+
const privateKey = await readPrivateKey({ armoredKey: testPrivateKeyLegacy });
355+
const decryptedPrivateKey = await decryptKey({ privateKey, passphrase: '123' });
356+
const data = 'Hello world!';
357+
358+
const now = new Date();
359+
const tenSecondsInTheFuture = new Date(+now + 1000);
360+
const sessionKey = {
361+
data: hexStringToArray('c5629d840fd64ef55aea474f87dcdeef76bbc798a340ef67045315eb7924a36f'),
362+
algorithm: enums.read(enums.symmetric, enums.symmetric.aes256)
363+
};
364+
365+
const { message: encrypted } = await encryptMessage({
366+
textData: data,
367+
encryptionKeys: [decryptedPrivateKey.toPublic()],
368+
signingKeys: [decryptedPrivateKey],
369+
sessionKey,
370+
date: tenSecondsInTheFuture,
371+
format: 'object'
372+
});
373+
const { data: decrypted, verified } = await decryptMessage({
374+
message: encrypted,
375+
sessionKeys: sessionKey,
376+
verificationKeys: [decryptedPrivateKey.toPublic()]
377+
});
378+
379+
expect(decrypted).to.equal(data);
380+
expect(await verified).to.equal(VERIFICATION_STATUS.SIGNED_AND_VALID);
381+
});
352382
});

0 commit comments

Comments
 (0)