Skip to content

Commit

Permalink
Merge pull request #61 from VirgilSecurity/group-encryption
Browse files Browse the repository at this point in the history
Group Sessions
  • Loading branch information
vadimavdeev authored Sep 19, 2019
2 parents b2b76ed + ea1886c commit 5c84c48
Show file tree
Hide file tree
Showing 32 changed files with 846 additions and 183 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Last, you need to get familiar with [usage examples](guides/usage-examples.md) o

## Docs
- [API Reference](http://virgilsecurity.github.io/virgil-crypto-javascript/)
- [Crypto Core Library](https://github.com/VirgilSecurity/virgil-crypto)
- [Crypto Core Library](https://github.com/VirgilSecurity/virgil-crypto-c)
- [More usage examples](https://developer.virgilsecurity.com/docs/how-to#cryptography)

## License
Expand Down
6 changes: 3 additions & 3 deletions packages/base-crypto/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@virgilsecurity/base-crypto",
"version": "0.5.1",
"version": "0.5.2",
"description": "Virgil JavaScript Crypto Library is a high-level cryptographic library that allows you to perform all necessary operations for secure storing and transferring data and everything required to become HIPAA and GDPR compliant.",
"main": "./dist/base-crypto.cjs.js",
"module": "./dist/base-crypto.es.js",
Expand All @@ -18,8 +18,8 @@
"prepare": "npm run clean && npm run build"
},
"dependencies": {
"@virgilsecurity/crypto-types": "^0.1.3",
"@virgilsecurity/data-utils": "^0.1.4"
"@virgilsecurity/crypto-types": "^0.1.4",
"@virgilsecurity/data-utils": "^0.1.5"
},
"peerDependencies": {
"@virgilsecurity/core-foundation": "^0.2.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/base-crypto/src/HashAlgorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface HashAlgorithmType {
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const addHashAlgorithm = (obj: any, algIdName: string) => {
const addHashAlgorithm = (obj: any, algIdName: keyof typeof FoundationModules.AlgId) => {
Object.defineProperty(obj, algIdName, {
configurable: false,
enumerable: true,
Expand Down
8 changes: 6 additions & 2 deletions packages/base-crypto/src/KeyPairType.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { getFoundationModules } from './foundationModules';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const addKeyPairType = (obj: any, name: string, algIdName: string, bitlen?: number) => {
const addKeyPairType = (
obj: {},
name: string,
algIdName: keyof typeof FoundationModules.AlgId,
bitlen?: number,
) => {
Object.defineProperty(obj, name, {
configurable: false,
enumerable: true,
Expand Down
51 changes: 41 additions & 10 deletions packages/base-crypto/src/VirgilCrypto.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { FoundationModules } from '@virgilsecurity/core-foundation';
import { NodeBuffer, dataToUint8Array, toBuffer } from '@virgilsecurity/data-utils';

import { DATA_SIGNATURE_KEY, DATA_SIGNER_ID_KEY } from './constants';
import { getFoundationModules } from './foundationModules';
import { HashAlgorithm, HashAlgorithmType } from './HashAlgorithm';
import { KeyPairType, KeyPairTypeType } from './KeyPairType';
import { importPrivateKey, importPublicKey } from './keyProvider';
import { serializePrivateKey, serializePublicKey } from './keySerializer';
import { getLowLevelPrivateKey } from './privateKeyUtils';
import { ICrypto, NodeBuffer as BufferType, Data, LowLevelPrivateKey } from './types';
import { ICrypto, NodeBuffer as BufferType, Data, IGroupSession } from './types';
import { toArray, getLowLevelPublicKeys } from './utils';
import { validatePrivateKey, validatePublicKey, validatePublicKeysArray } from './validators';
import { VirgilPrivateKey } from './VirgilPrivateKey';
Expand All @@ -17,6 +15,10 @@ import { VirgilStreamCipher } from './VirgilStreamCipher';
import { VirgilStreamDecipher } from './VirgilStreamDecipher';
import { VirgilStreamSigner } from './VirgilStreamSigner';
import { VirgilStreamVerifier } from './VirgilStreamVerifier';
import { computeSessionId, createInitialEpoch } from './groups/helpers';
import { createVirgilGroupSession } from './groups/createVirgilGroupSession';

export const MIN_GROUP_ID_BYTE_LENGTH = 10;

export interface VirgilCryptoOptions {
useSha256Identifiers?: boolean;
Expand All @@ -30,9 +32,8 @@ export class VirgilCrypto implements ICrypto {
readonly hashAlgorithm = HashAlgorithm;
readonly keyPairType = KeyPairType;

private foundationModules: FoundationModules;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private random: any;
private foundationModules: typeof FoundationModules;
private random: FoundationModules.CtrDrbg;

constructor(options: VirgilCryptoOptions = {}) {
this.foundationModules = getFoundationModules();
Expand Down Expand Up @@ -60,10 +61,11 @@ export class VirgilCrypto implements ICrypto {
throw error;
}
if (keyPairType.algId === this.foundationModules.AlgId.RSA) {
keyProvider.setRsaParams(keyPairType.bitlen);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
keyProvider.setRsaParams(keyPairType.bitlen!);
}

let lowLevelPrivateKey: LowLevelPrivateKey;
let lowLevelPrivateKey: FoundationModules.PrivateKey;
try {
lowLevelPrivateKey = keyProvider.generatePrivateKey(keyPairType.algId);
} catch (error) {
Expand Down Expand Up @@ -107,10 +109,11 @@ export class VirgilCrypto implements ICrypto {
}
keyProvider.random = keyMaterialRng;
if (keyPairType.algId === this.foundationModules.AlgId.RSA) {
keyProvider.setRsaParams(keyPairType.bitlen);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
keyProvider.setRsaParams(keyPairType.bitlen!);
}

let lowLevelPrivateKey: LowLevelPrivateKey;
let lowLevelPrivateKey: FoundationModules.PrivateKey;
try {
lowLevelPrivateKey = keyProvider.generatePrivateKey(keyPairType.algId);
} catch (error) {
Expand Down Expand Up @@ -586,6 +589,34 @@ export class VirgilCrypto implements ICrypto {
return new VirgilStreamVerifier(signature);
}

generateGroupSession(groupId: Data): IGroupSession {
const groupIdBytes = dataToUint8Array(groupId, 'utf8');
if (groupIdBytes.byteLength < MIN_GROUP_ID_BYTE_LENGTH) {
throw new Error(
`The given group Id is too short. Must be at least ${MIN_GROUP_ID_BYTE_LENGTH} bytes.`,
);
}

const sessionId = computeSessionId(groupIdBytes);
const initialEpoch = createInitialEpoch(sessionId);

const initialEpochMessage = initialEpoch.serialize();
initialEpoch.delete();
return createVirgilGroupSession([initialEpochMessage]);
}

importGroupSession(epochMessages: Data[]): IGroupSession {
if (!Array.isArray(epochMessages)) {
throw new TypeError('Epoch messages must be an array.');
}

if (epochMessages.length === 0) {
throw new Error('Epoch messages must not be empty.');
}

return createVirgilGroupSession(epochMessages.map(it => dataToUint8Array(it, 'base64')));
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private createHash(data: Uint8Array, HashClass: any) {
const hashInstance = new HashClass();
Expand Down
4 changes: 2 additions & 2 deletions packages/base-crypto/src/VirgilStreamCipher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dataToUint8Array, toBuffer } from '@virgilsecurity/data-utils';

import { DATA_SIGNATURE_KEY } from './constants';
import { getFoundationModules } from './foundationModules';
import { Data, LowLevelPublicKey } from './types';
import { Data } from './types';
import { toArray, getLowLevelPublicKeys } from './utils';
import { validatePublicKeysArray } from './validators';
import { VirgilPublicKey } from './VirgilPublicKey';
Expand All @@ -21,7 +21,7 @@ export class VirgilStreamCipher {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private ctrDrbg: any;

private lowLevelPublicKeys: LowLevelPublicKey[] = [];
private lowLevelPublicKeys: FoundationModules.PublicKey[] = [];

constructor(publicKey: VirgilPublicKey | VirgilPublicKey[], signature?: Data) {
const foundationModules = getFoundationModules();
Expand Down
4 changes: 2 additions & 2 deletions packages/base-crypto/src/VirgilStreamDecipher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { dataToUint8Array, toBuffer } from '@virgilsecurity/data-utils';
import { DATA_SIGNATURE_KEY } from './constants';
import { getFoundationModules } from './foundationModules';
import { getLowLevelPrivateKey } from './privateKeyUtils';
import { Data, LowLevelPrivateKey } from './types';
import { Data } from './types';
import { validatePrivateKey } from './validators';
import { VirgilPrivateKey } from './VirgilPrivateKey';

Expand All @@ -14,7 +14,7 @@ export class VirgilStreamDecipher {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private recipientCipher: any;

private lowLevelPrivateKey: LowLevelPrivateKey;
private lowLevelPrivateKey: FoundationModules.PrivateKey;

constructor(privateKey: VirgilPrivateKey) {
const foundationModules = getFoundationModules();
Expand Down
54 changes: 53 additions & 1 deletion packages/base-crypto/src/__tests__/VirgilCrypto.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import initFoundation from '@virgilsecurity/core-foundation';

import {
setFoundationModules,
hasFoundationModules,
HashAlgorithm,
VirgilCrypto,
VirgilPrivateKey,
Expand All @@ -18,8 +19,13 @@ import {
describe('VirgilCrypto', () => {
let virgilCrypto: VirgilCrypto;

beforeEach(() => {
before(() => {
return new Promise(resolve => {
if (hasFoundationModules()) {
virgilCrypto = new VirgilCrypto();
return resolve();
}

initFoundation().then(foundationModules => {
setFoundationModules(foundationModules);
virgilCrypto = new VirgilCrypto();
Expand Down Expand Up @@ -269,4 +275,50 @@ describe('VirgilCrypto', () => {
const streamVerifier = virgilCrypto.createStreamVerifier(signature);
expect(streamVerifier).to.be.instanceOf(VirgilStreamVerifier);
});

describe('generateGroupSession', () => {
it('throws if groupId is less than 10 bytes long', () => {
expect(() => {
virgilCrypto.generateGroupSession('short_id');
}, 'should have thrown').throws(Error);
});

it('creates group with correct id', () => {
const expectedId = virgilCrypto
.calculateHash('i_am_long_enough_to_be_a_group_id', HashAlgorithm.SHA512)
.slice(0, 32);
const group = virgilCrypto.generateGroupSession('i_am_long_enough_to_be_a_group_id');
expect(group.getSessionId()).to.equal(expectedId.toString('hex'));
});

it('creates group with one epoch', () => {
const group = virgilCrypto.generateGroupSession('i_am_long_enough_to_be_a_group_id');
expect(group.export()).to.have.length(1);
});
});

describe('importGroupSession', () => {
it('throws if epoch messages is not an array', () => {
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
virgilCrypto.importGroupSession(undefined as any);
}).throws(TypeError);
});

it('throws if epoch messages array is empty', () => {
expect(() => {
virgilCrypto.importGroupSession([]);
}).throws(Error);
});

it('reconstructs the group session object from epoch messages', () => {
const myGroup = virgilCrypto.generateGroupSession(NodeBuffer.from('x'.repeat(10)));
myGroup.addNewEpoch();
const epochMessages = myGroup.export();
const theirGroup = virgilCrypto.importGroupSession(epochMessages);

expect(myGroup.getSessionId()).to.equal(theirGroup.getSessionId());
expect(myGroup.getCurrentEpochNumber()).to.equal(theirGroup.getCurrentEpochNumber());
});
});
});
Loading

0 comments on commit 5c84c48

Please sign in to comment.