Skip to content

Commit

Permalink
decrypt/verifyMessage: by default, verify messages slightly in the …
Browse files Browse the repository at this point in the history
…future compared to server time (#200)
  • Loading branch information
larabr authored May 28, 2024
1 parent 588c1a2 commit 68f5191
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 6 deletions.
7 changes: 6 additions & 1 deletion lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ export const MAX_ENC_HEADER_LENGTH = 1024;
* Offset needed for key generation to ensure key (incl. certification signatures) validity
* across different servers, which might have slightly mismatching server time
*/
export const DEFAULT_OFFSET = -60000;
export const DEFAULT_KEY_GENERATION_OFFSET = -60000;
/**
* Offset needed for message verification to ensure validity across different servers,
* which might have slightly mismatching server time
*/
export const DEFAULT_SIGNATURE_VERIFICATION_OFFSET = 60000;

export const ARGON2_PARAMS = { // from https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice
RECOMMENDED: { passes: 1, parallelism: 4, memoryExponent: 19, tagLength: 32 },
Expand Down
4 changes: 2 additions & 2 deletions lib/key/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
generateSessionKey as openpgp_generateSessionKey
} from '../openpgp';
import { serverTime } from '../serverTime';
import { DEFAULT_OFFSET } from '../constants';
import { DEFAULT_KEY_GENERATION_OFFSET } from '../constants';
import { getSymmetricKeySize, getRandomBytes } from '../crypto/utils';
import encryptMessage from '../message/encrypt';
import { SHA256 } from '../crypto/hash';
Expand All @@ -18,7 +18,7 @@ import { arrayToHexString } from '../utils';
* @returns {Promise<Object>} generated key data and armored revocation certificate in the form:
* { PrivateKey, PublicKey : String|Uint8Array|Object, revocationCertificate: String }
*/
export async function generateKey({ date = new Date(+serverTime() + DEFAULT_OFFSET), ...rest }) {
export async function generateKey({ date = new Date(+serverTime() + DEFAULT_KEY_GENERATION_OFFSET), ...rest }) {
return openpgp_generateKey({ date, ...rest });
}

Expand Down
7 changes: 6 additions & 1 deletion lib/message/decrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import { decrypt, readSignature } from '../openpgp';
import { serverTime } from '../serverTime';
import { getConfigForContextVerification } from './context';
import { handleVerificationResult } from './verify';
import { DEFAULT_SIGNATURE_VERIFICATION_OFFSET } from '../constants';

export default async function decryptMessage({
date = serverTime(), encryptedSignature, context, config = {}, ...options
date = new Date(+serverTime() + DEFAULT_SIGNATURE_VERIFICATION_OFFSET),
encryptedSignature,
context,
config = {},
...options
}) {
const sanitizedOptions = {
...options,
Expand Down
4 changes: 2 additions & 2 deletions lib/message/verify.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createMessage, verify, CleartextMessage } from '../openpgp';
import { VERIFICATION_STATUS } from '../constants';
import { DEFAULT_SIGNATURE_VERIFICATION_OFFSET, VERIFICATION_STATUS } from '../constants';
import { serverTime } from '../serverTime';
import { removeTrailingSpaces } from './utils';
import { ContextError, getConfigForContextVerification, isValidSignatureContext } from './context';
Expand Down Expand Up @@ -93,7 +93,7 @@ export async function verifyMessage({
stripTrailingSpaces,
context,
config = {},
date = serverTime(),
date = new Date(+serverTime() + DEFAULT_SIGNATURE_VERIFICATION_OFFSET),
...options
}) {
const dataType = binaryData ? 'binary' : 'text';
Expand Down
30 changes: 30 additions & 0 deletions test/message/encryptMessage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,34 @@ describe('message encryption and decryption', () => {
expect(await readToEnd(decrypted).then(arrayToBinaryString)).to.equal(inputData);
expect(await verified).to.equal(VERIFICATION_STATUS.SIGNED_AND_VALID);
});

it('it can decrypt and verify a message ten seconds in the future', async () => {
const privateKey = await readPrivateKey({ armoredKey: testPrivateKeyLegacy });
const decryptedPrivateKey = await decryptKey({ privateKey, passphrase: '123' });
const data = 'Hello world!';

const now = new Date();
const tenSecondsInTheFuture = new Date(+now + 1000);
const sessionKey = {
data: hexStringToArray('c5629d840fd64ef55aea474f87dcdeef76bbc798a340ef67045315eb7924a36f'),
algorithm: enums.read(enums.symmetric, enums.symmetric.aes256)
};

const { message: encrypted } = await encryptMessage({
textData: data,
encryptionKeys: [decryptedPrivateKey.toPublic()],
signingKeys: [decryptedPrivateKey],
sessionKey,
date: tenSecondsInTheFuture,
format: 'object'
});
const { data: decrypted, verified } = await decryptMessage({
message: encrypted,
sessionKeys: sessionKey,
verificationKeys: [decryptedPrivateKey.toPublic()]
});

expect(decrypted).to.equal(data);
expect(await verified).to.equal(VERIFICATION_STATUS.SIGNED_AND_VALID);
});
});

0 comments on commit 68f5191

Please sign in to comment.