chore: added tests for SnodeSignatures calls

pull/2873/head
Audric Ackermann 2 years ago
parent 820103b340
commit e8e0e19b40

@ -1,8 +1,9 @@
import * as wrappers from 'libsodium-wrappers-sumo';
type LibSodiumWrappers = typeof wrappers;
export async function getSodiumNode() {
// don't ask me why, but when called from node we have to do this as the types are incorrect?!
const anyWrappers = wrappers as any;
await anyWrappers.ready;
return anyWrappers.default;
return anyWrappers.default as LibSodiumWrappers;
}

@ -210,7 +210,7 @@ export async function expireMessageOnSnode(props: ExpireMessageOnSnodeProps) {
const params: UpdateExpireNodeUserParams = {
pubkey: ourPubKey,
pubkey_ed25519: signResult.pubkey_ed25519.toUpperCase(),
pubkey_ed25519: signResult.pubkey.toUpperCase(),
// TODO better testing for failed case
messages: [messageHash],
expiry,

@ -4,7 +4,7 @@ import { Snode } from '../../../data/data';
import { updateIsOnline } from '../../../state/ducks/onion';
import { doSnodeBatchRequest } from './batchRequest';
import { GetNetworkTime } from './getNetworkTime';
import { SnodeNamespace, SnodeNamespaces } from './namespaces';
import { SnodeNamespace, SnodeNamespaces, SnodeNamespacesGroup } from './namespaces';
import { UserGroupsWrapperActions } from '../../../webworker/workers/browser/libsession_worker_interface';
import { DURATION } from '../../constants';
@ -92,7 +92,7 @@ async function retrieveRequestForGroup({
retrieveParam,
}: {
groupPk: GroupPubkeyType;
namespace: SnodeNamespaces;
namespace: SnodeNamespacesGroup;
retrieveParam: RetrieveParams;
}) {
if (!PubKey.isClosedGroupV2(groupPk)) {
@ -182,7 +182,7 @@ async function buildRetrieveRequest(
pubkey: UserUtils.getOurPubKeyStrFromCache(),
expiry,
signature: signResult.signature,
pubkey_ed25519: signResult.pubkey_ed25519,
pubkey_ed25519: signResult.pubkey,
},
};
retrieveRequestsParams.push(expireParams);

@ -1,13 +1,13 @@
import { FixedSizeUint8Array, GroupPubkeyType } from 'libsession_util_nodejs';
import { isEmpty } from 'lodash';
import { toFixedUint8ArrayOfLength } from '../../../types/sqlSharedTypes';
import { getSodiumRenderer } from '../../crypto';
import { PubKey } from '../../types';
import { StringUtils, UserUtils } from '../../utils';
import { fromHexToArray, fromUInt8ArrayToBase64 } from '../../utils/String';
import { GetNetworkTime } from './getNetworkTime';
import { SnodeNamespaces } from './namespaces';
import { PubKey } from '../../types';
import { toFixedUint8ArrayOfLength } from '../../../types/sqlSharedTypes';
import { PreConditionFailed } from '../../utils/errors';
import { GetNetworkTime } from './getNetworkTime';
import { SnodeNamespacesGroup } from './namespaces';
type WithTimestamp = { timestamp: number };
@ -86,7 +86,7 @@ function isSigParamsForGroupAdmin(
return PubKey.isClosedGroupV2(asGr.groupPk) && !!asGr.privKey;
}
async function getSnodeShared(params: SnodeSigParamsAdminGroup | SnodeSigParamsUs) {
async function getSnodeSignatureShared(params: SnodeSigParamsAdminGroup | SnodeSigParamsUs) {
const signatureTimestamp = GetNetworkTime.getNowWithNetworkOffset();
const verificationData = StringUtils.encode(
`${params.method}${params.namespace === 0 ? '' : params.namespace}${signatureTimestamp}`,
@ -130,7 +130,7 @@ async function getSnodeSignatureParamsUs({
const edKeyPrivBytes = ourEd25519Key.privKeyBytes;
const lengthCheckedPrivKey = toFixedUint8ArrayOfLength(edKeyPrivBytes, 64);
const sigData = await getSnodeShared({
const sigData = await getSnodeSignatureShared({
pubKey: UserUtils.getOurPubKeyStrFromCache(),
method,
namespace,
@ -149,14 +149,14 @@ async function getSnodeGroupSignatureParams({
groupIdentityPrivKey,
groupPk,
method,
namespace = 0,
namespace,
}: {
groupPk: GroupPubkeyType;
groupIdentityPrivKey: FixedSizeUint8Array<64>;
namespace: SnodeNamespaces;
namespace: SnodeNamespacesGroup;
method: 'retrieve' | 'store';
}): Promise<SnodeGroupSignatureResult> {
const sigData = await getSnodeShared({
const sigData = await getSnodeSignatureShared({
pubKey: groupPk,
method,
namespace,
@ -176,7 +176,7 @@ async function generateUpdateExpirySignature({
WithTimestamp & {
ed25519Privkey: Uint8Array | FixedSizeUint8Array<64>;
ed25519Pubkey: string;
}): Promise<{ signature: string; pubkey_ed25519: string }> {
}): Promise<{ signature: string; pubkey: string }> {
// "expire" || ShortenOrExtend || expiry || messages[0] || ... || messages[N]
const verificationString = `expire${shortenOrExtend}${timestamp}${messagesHashes.join('')}`;
const verificationData = StringUtils.encode(verificationString, 'utf8');
@ -193,7 +193,7 @@ async function generateUpdateExpirySignature({
return {
signature: signatureBase64,
pubkey_ed25519: ed25519Pubkey,
pubkey: ed25519Pubkey,
};
}
@ -235,7 +235,7 @@ async function generateUpdateExpiryGroupSignature({
}) {
if (isEmpty(groupPrivKey) || isEmpty(groupPk)) {
throw new PreConditionFailed(
'generateUpdateExpiryGroupSignature groupPrivKey or groupPks is empty'
'generateUpdateExpiryGroupSignature groupPrivKey or groupPk is empty'
);
}

@ -0,0 +1,333 @@
import { expect, use } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import Sinon from 'sinon';
import { HexString } from '../../../../node/hexStrings';
import { getSodiumNode } from '../../../../node/sodiumNode';
import { GetNetworkTime } from '../../../../session/apis/snode_api/getNetworkTime';
import { SnodeNamespaces } from '../../../../session/apis/snode_api/namespaces';
import { SnodeSignature } from '../../../../session/apis/snode_api/snodeSignatures';
import { concatUInt8Array } from '../../../../session/crypto';
import { UserUtils } from '../../../../session/utils';
import { fromBase64ToArray, fromHexToArray } from '../../../../session/utils/String';
import { toFixedUint8ArrayOfLength } from '../../../../types/sqlSharedTypes';
use(chaiAsPromised);
const validGroupPk = '03eef710fcaaa73fd50c4311333f5c496e0fdbbe9e8a70fdfa95e7ec62d5032f5c';
const privKeyUint = toFixedUint8ArrayOfLength(
concatUInt8Array(
fromHexToArray('cd8488c39bf9972739046d627e7796b2bc0e38e2fa99fc4edd59205c28f2cdb1'),
fromHexToArray(validGroupPk.slice(2))
),
64
);
const userEd25519Keypair = {
pubKey: '37e1631b002de498caf7c5c1712718bde7f257c6dadeed0c21abf5e939e6c309',
privKey:
'be1d11154ff9b6de77873f0b6b0bcc460000000000000000000000000000000037e1631b002de498caf7c5c1712718bde7f257c6dadeed0c21abf5e939e6c309',
};
const hardcodedTimestamp = 1234;
async function verifySig(ret: { pubkey: string; signature: string }, verificationData: string) {
const without03 = ret.pubkey.startsWith('03') ? ret.pubkey.slice(2) : ret.pubkey; //
const pk = HexString.fromHexString(without03);
const sodium = await getSodiumNode();
const verified = sodium.crypto_sign_verify_detached(
fromBase64ToArray(ret.signature),
verificationData,
pk
);
if (!verified) {
throw new Error('sig failed to be verified');
}
}
describe('SnodeSignature', () => {
afterEach(() => {
Sinon.restore();
});
describe('getSnodeGroupSignatureParams', () => {
beforeEach(() => {
Sinon.stub(GetNetworkTime, 'getNowWithNetworkOffset').returns(hardcodedTimestamp);
});
describe('retrieve', () => {
it('retrieve namespace ClosedGroupInfo', async () => {
const ret = await SnodeSignature.getSnodeGroupSignatureParams({
method: 'retrieve',
namespace: SnodeNamespaces.ClosedGroupInfo,
groupIdentityPrivKey: privKeyUint,
groupPk: validGroupPk,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
expect(ret.timestamp).to.be.eq(hardcodedTimestamp);
const verificationData = `retrieve${SnodeNamespaces.ClosedGroupInfo}${hardcodedTimestamp}`;
await verifySig(ret, verificationData);
});
it('retrieve namespace ClosedGroupKeys', async () => {
const ret = await SnodeSignature.getSnodeGroupSignatureParams({
method: 'retrieve',
namespace: SnodeNamespaces.ClosedGroupKeys,
groupIdentityPrivKey: privKeyUint,
groupPk: validGroupPk,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
expect(ret.timestamp).to.be.eq(hardcodedTimestamp);
const verificationData = `retrieve${SnodeNamespaces.ClosedGroupKeys}${hardcodedTimestamp}`;
await verifySig(ret, verificationData);
});
it('retrieve namespace ClosedGroupMessages', async () => {
const ret = await SnodeSignature.getSnodeGroupSignatureParams({
method: 'retrieve',
namespace: SnodeNamespaces.ClosedGroupMessages,
groupIdentityPrivKey: privKeyUint,
groupPk: validGroupPk,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
expect(ret.timestamp).to.be.eq(hardcodedTimestamp);
const verificationData = `retrieve${SnodeNamespaces.ClosedGroupMessages}${hardcodedTimestamp}`;
await verifySig(ret, verificationData);
});
});
describe('store', () => {
it('store namespace ClosedGroupInfo', async () => {
const ret = await SnodeSignature.getSnodeGroupSignatureParams({
method: 'store',
namespace: SnodeNamespaces.ClosedGroupInfo,
groupIdentityPrivKey: privKeyUint,
groupPk: validGroupPk,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
expect(ret.timestamp).to.be.eq(hardcodedTimestamp);
const verificationData = `store${SnodeNamespaces.ClosedGroupInfo}${hardcodedTimestamp}`;
await verifySig(ret, verificationData);
});
it('store namespace ClosedGroupKeys', async () => {
const ret = await SnodeSignature.getSnodeGroupSignatureParams({
method: 'store',
namespace: SnodeNamespaces.ClosedGroupKeys,
groupIdentityPrivKey: privKeyUint,
groupPk: validGroupPk,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
expect(ret.timestamp).to.be.eq(hardcodedTimestamp);
const verificationData = `store${SnodeNamespaces.ClosedGroupKeys}${hardcodedTimestamp}`;
await verifySig(ret, verificationData);
});
it('store namespace ClosedGroupMessages', async () => {
const ret = await SnodeSignature.getSnodeGroupSignatureParams({
method: 'store',
namespace: SnodeNamespaces.ClosedGroupMessages,
groupIdentityPrivKey: privKeyUint,
groupPk: validGroupPk,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
expect(ret.timestamp).to.be.eq(hardcodedTimestamp);
const verificationData = `store${SnodeNamespaces.ClosedGroupMessages}${hardcodedTimestamp}`;
await verifySig(ret, verificationData);
});
});
});
describe('generateUpdateExpiryGroupSignature', () => {
it('throws if groupPk not given', async () => {
const func = async () => {
return SnodeSignature.generateUpdateExpiryGroupSignature({
groupPk: null as any,
groupPrivKey: privKeyUint,
messagesHashes: ['[;p['],
shortenOrExtend: '',
timestamp: hardcodedTimestamp,
});
};
await expect(func()).to.be.rejectedWith(
'generateUpdateExpiryGroupSignature groupPrivKey or groupPk is empty'
);
});
it('throws if groupPrivKey is empty', async () => {
const func = async () => {
return SnodeSignature.generateUpdateExpiryGroupSignature({
groupPk: validGroupPk,
groupPrivKey: new Uint8Array() as any,
messagesHashes: ['[;p['],
shortenOrExtend: '',
timestamp: hardcodedTimestamp,
});
};
await expect(func()).to.be.rejectedWith(
'generateUpdateExpiryGroupSignature groupPrivKey or groupPk is empty'
);
});
it('works with valid pubkey and privkey', async () => {
const hashes = ['hash4321', 'hash4221'];
const timestamp = hardcodedTimestamp;
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryGroupSignature({
groupPk: validGroupPk,
groupPrivKey: privKeyUint,
messagesHashes: hashes,
shortenOrExtend: '',
timestamp,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
const verificationData = `expire${shortenOrExtend}${timestamp}${hashes.join('')}`;
await verifySig(ret, verificationData);
});
it('fails with invalid timestamp', async () => {
const hashes = ['hash4321', 'hash4221'];
const timestamp = hardcodedTimestamp;
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryGroupSignature({
groupPk: validGroupPk,
groupPrivKey: privKeyUint,
messagesHashes: hashes,
shortenOrExtend: '',
timestamp,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
const verificationData = `expire${shortenOrExtend}${timestamp}1${hashes.join('')}`;
const func = async () => verifySig(ret, verificationData);
await expect(func()).rejectedWith('sig failed to be verified');
});
it('fails with invalid hashes', async () => {
const hashes = ['hash4321', 'hash4221'];
const timestamp = hardcodedTimestamp;
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryGroupSignature({
groupPk: validGroupPk,
groupPrivKey: privKeyUint,
messagesHashes: hashes,
shortenOrExtend: '',
timestamp,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
const overridenHash = hashes.slice();
overridenHash[0] = '1111';
const verificationData = `expire${shortenOrExtend}${timestamp}${overridenHash.join('')}`;
const func = async () => verifySig(ret, verificationData);
await expect(func()).rejectedWith('sig failed to be verified');
});
it('fails with invalid number of hashes', async () => {
const hashes = ['hash4321', 'hash4221'];
const timestamp = hardcodedTimestamp;
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryGroupSignature({
groupPk: validGroupPk,
groupPrivKey: privKeyUint,
messagesHashes: hashes,
shortenOrExtend: '',
timestamp,
});
expect(ret.pubkey).to.be.eq(validGroupPk);
const overridenHash = [hashes[0]];
const verificationData = `expire${shortenOrExtend}${timestamp}${overridenHash.join('')}`;
const func = async () => verifySig(ret, verificationData);
await expect(func()).rejectedWith('sig failed to be verified');
});
});
describe('generateUpdateExpiryOurSignature', () => {
it('throws if our ed keypair is not set', async () => {
Sinon.stub(UserUtils, 'getUserED25519KeyPair').resolves(null as any);
const func = async () => {
const hashes = ['hash4321', 'hash4221'];
const shortenOrExtend = '';
return SnodeSignature.generateUpdateExpiryOurSignature({
messagesHashes: hashes,
shortenOrExtend,
timestamp: hardcodedTimestamp,
});
};
await expect(func()).to.be.rejectedWith(
'getSnodeSignatureParams "expiry": User has no getUserED25519KeyPair()'
);
});
it('throws if invalid hashes', async () => {
Sinon.stub(UserUtils, 'getUserED25519KeyPair').resolves(userEd25519Keypair);
const hashes = ['hash4321', 'hash4221'];
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryOurSignature({
messagesHashes: hashes,
shortenOrExtend,
timestamp: hardcodedTimestamp,
});
const overridenHash = [hashes[0]];
const verificationData = `expire${shortenOrExtend}${hardcodedTimestamp}${overridenHash.join(
''
)}`;
const func = async () => {
return verifySig(ret, verificationData);
};
await expect(func()).to.be.rejectedWith('sig failed to be verified');
});
it('throws if invalid timestamp', async () => {
Sinon.stub(UserUtils, 'getUserED25519KeyPair').resolves(userEd25519Keypair);
const hashes = ['hash4321', 'hash4221'];
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryOurSignature({
messagesHashes: hashes,
shortenOrExtend,
timestamp: hardcodedTimestamp,
});
const verificationData = `expire${shortenOrExtend}${hardcodedTimestamp}123${hashes.join('')}`;
const func = async () => {
return verifySig(ret, verificationData);
};
await expect(func()).to.be.rejectedWith('sig failed to be verified');
});
it('works with valid pubkey and privkey', async () => {
Sinon.stub(UserUtils, 'getUserED25519KeyPair').resolves(userEd25519Keypair);
const hashes = ['hash4321', 'hash4221'];
const timestamp = hardcodedTimestamp;
const shortenOrExtend = '';
const ret = await SnodeSignature.generateUpdateExpiryOurSignature({
messagesHashes: hashes,
shortenOrExtend: '',
timestamp,
});
expect(ret.pubkey).to.be.eq(userEd25519Keypair.pubKey);
const verificationData = `expire${shortenOrExtend}${timestamp}${hashes.join('')}`;
await verifySig(ret, verificationData);
});
});
});
Loading…
Cancel
Save