Skip to content

Commit 1a7c8c3

Browse files
author
Alexey Smirnov
authored
Merge pull request #35 from VirgilSecurity/v3.2.3
v3.2.3
2 parents 4a91df0 + 12b61ea commit 1a7c8c3

File tree

10 files changed

+156
-18
lines changed

10 files changed

+156
-18
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "virgil-crypto",
3-
"version": "3.2.2",
3+
"version": "3.2.3",
44
"main": "dist/virgil-crypto.cjs.js",
55
"browser": {
66
"./dist/virgil-crypto.cjs.js": "./dist/virgil-crypto.browser.cjs.js",

src/VirgilCrypto.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { VirgilPublicKey } from './VirgilPublicKey';
77
import { VirgilPrivateKey } from './VirgilPrivateKey';
88
import { getPrivateKeyBytes } from './privateKeyUtils';
99
import { validatePrivateKey, validatePublicKey, validatePublicKeysArray } from './validators';
10-
import { VirgilStreamCipher } from './streams/VirgilStreamCipher';
10+
import { VirgilStreamCipher, VirgilStreamCipherOptions } from './streams/VirgilStreamCipher';
1111
import { VirgilStreamDecipher } from './streams/VirgilStreamDecipher';
1212
import { VirgilStreamSigner } from './streams/VirgilStreamSigner';
1313
import { VirgilStreamVerifier } from './streams/VirgilStreamVerifier';
@@ -477,11 +477,12 @@ export class VirgilCrypto {
477477
/**
478478
* Creates an instance of {@link VirgilStreamCipher} to be used
479479
* to encrypt data in chunks using the given `publicKey`.
480-
* @param {VirgilPublicKey|VirgilPublicKey[]} publicKey - A signle
480+
* @param {VirgilPublicKey|VirgilPublicKey[]} publicKey - A single
481481
* public key or an array of public keys to encrypt the data with.
482+
* @param {Data} [signature] - Optionally add a signature of plain data to the encrypted stream.
482483
*/
483-
createStreamCipher (publicKey: VirgilPublicKey|VirgilPublicKey[]) {
484-
return new VirgilStreamCipher(publicKey);
484+
createStreamCipher (publicKey: VirgilPublicKey|VirgilPublicKey[], options?: VirgilStreamCipherOptions) {
485+
return new VirgilStreamCipher(publicKey, options);
485486
}
486487

487488
/**
@@ -504,7 +505,7 @@ export class VirgilCrypto {
504505

505506
/**
506507
* Creates an instance of {@link VirgilStreamVerifier} to be used
507-
* to verify the `signature` for the data in comming in chunks.
508+
* to verify the `signature` for the data in coming in chunks.
508509
*
509510
* @param {Data} signature - The signature to be verified.
510511
* @param {StringEncoding} encoding - If `signature` is a string,

src/common/IVirgilCryptoWrapper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ export interface IDeletable {
323323
* `VirgilByteArray` to\from `Buffer`s.
324324
*/
325325
export interface WrappedVirgilCipherBase extends IDeletable {
326+
customParams(): any;
326327
addKeyRecipientSafe(recipientId: Buffer, publicKey: Buffer): void;
327328
addPasswordRecipientSafe(password: Buffer): void;
328329
}

src/streams/VirgilStreamCipher.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,29 @@ import { VirgilPublicKey } from '../VirgilPublicKey';
22
import { toArray } from '../utils/toArray';
33
import { validatePublicKeysArray } from '../validators';
44
import { VirgilStreamCipherBase } from './VirgilStreamCipherBase';
5+
import { Data } from './../interfaces';
6+
import { DATA_SIGNATURE_KEY } from '../common/constants';
7+
import { anyToBuffer, StringEncoding } from '../utils/anyToBuffer';
8+
9+
/**
10+
* `VirgilStreamCipher` constructor `options` parameter.
11+
*/
12+
export interface VirgilStreamCipherOptions {
13+
/**
14+
* Optionally add a signature of plain data to the encrypted stream.
15+
*/
16+
signature?: Data;
17+
/**
18+
* If `signature` is a string,
19+
* specifies its encoding, otherwise is ignored. Default is 'base64'.
20+
*/
21+
encoding?: StringEncoding;
22+
}
523

624
/**
725
* Class responsible for encryption of streams of data.
826
*/
927
export class VirgilStreamCipher extends VirgilStreamCipherBase {
10-
1128
/**
1229
* Initializes a new instance of `VirgilStreamCipher`.
1330
* `VirgilStreamCipher` objects are not meant to be created with the `new`
@@ -19,16 +36,27 @@ export class VirgilStreamCipher extends VirgilStreamCipherBase {
1936
* @param {VirgilPublicKey|VirgilPublicKey[]} publicKeys - A single
2037
* {@link VirgilPublicKey} or an array of {@link VirgilPublicKey}'s to
2138
* to encrypt the data with.
39+
* @param {VirgilStreamCipherOptions} [options] - `VirgilStreamCipherOptions` object.
2240
*/
23-
constructor (publicKeys: VirgilPublicKey|VirgilPublicKey[]) {
41+
constructor(
42+
publicKeys: VirgilPublicKey | VirgilPublicKey[],
43+
options?: VirgilStreamCipherOptions
44+
) {
2445
const publicKeyArr = toArray(publicKeys);
2546
validatePublicKeysArray(publicKeyArr);
2647

2748
super();
2849

29-
for (const { identifier, key} of publicKeyArr) {
50+
for (const { identifier, key } of publicKeyArr) {
3051
this.seqCipher.addKeyRecipientSafe(identifier, key);
3152
}
53+
54+
if (options && options.signature) {
55+
const signatureKey = Buffer.from(DATA_SIGNATURE_KEY);
56+
const customParams = this.seqCipher.customParams();
57+
const encoding = options.encoding ? options.encoding : 'base64'
58+
customParams.setDataSafe(signatureKey, anyToBuffer(options.signature, encoding));
59+
}
3260
}
3361

3462
/**

src/streams/VirgilStreamCipherBase.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class VirgilStreamCipherBase {
1313
/**
1414
* Indicates whether the `final` method has been called.
1515
*/
16-
private isFinished: boolean = false;
16+
isFinished: boolean = false;
1717

1818
/**
1919
* Indicates whether the `dispose` method has been called.
@@ -51,14 +51,22 @@ export class VirgilStreamCipherBase {
5151
* or decrypt data, attempts to call any method including `final` will
5252
* result in an error being thrown.
5353
* This method also automatically calls `dispose`.
54+
* @param {boolean} dispose - Optional. Indicating whether to automatically
55+
* free the memory occupied by internal {@link seqSigner} object in the
56+
* browser.
57+
* Default is `true`. `false` is used to perform operations in inherited classes.
58+
* If you pass `false` as an argument you should call `dispose` method manually.
59+
*
60+
* In node.js this argument is ignored because the memory will be freed by the
61+
* garbage collector.
5462
*/
55-
final () {
63+
final (dispose: boolean = true) {
5664
this.ensureLegalState();
5765
try {
5866
return this.seqCipher.finishSafe();
5967
} finally {
6068
this.isFinished = true;
61-
this.dispose();
69+
if (dispose) this.dispose();
6270
}
6371
}
6472

src/streams/VirgilStreamDecipher.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { validatePrivateKey } from '../validators';
22
import { VirgilPrivateKey } from '../VirgilPrivateKey';
33
import { getPrivateKeyBytes } from '../privateKeyUtils';
44
import { VirgilStreamCipherBase } from './VirgilStreamCipherBase';
5+
import { DATA_SIGNATURE_KEY } from '../common/constants';
6+
import { StringEncoding } from '../utils/anyToBuffer';
57

68
/**
79
* Class responsible for decryption of streams of data.
@@ -32,4 +34,21 @@ export class VirgilStreamDecipher extends VirgilStreamCipherBase {
3234
Buffer.alloc(0)
3335
);
3436
}
37+
38+
/**
39+
* Get signature from content_info if it was added on encryption phase.
40+
*/
41+
getSignature(): Buffer | null {
42+
if (!this.isFinished) {
43+
throw new Error('Illegal state. Cannot get signature before the `final` method has been called.');
44+
}
45+
const customParams = this.seqCipher.customParams();
46+
let signature: Buffer;
47+
try {
48+
signature = customParams.getDataSafe(Buffer.from(DATA_SIGNATURE_KEY));
49+
} catch (err) {
50+
return null;
51+
}
52+
return signature;
53+
}
3554
}

src/streams/VirgilStreamVerifier.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class VirgilStreamVerifier extends VirgilStreamSignerBase {
1818
*
1919
* @param {Data} signature = The signature to be verified.
2020
* @param {StringEncoding} [encoding] - If `signature` is a string,
21-
* specifies its encoding, otherwise is ignored. Default is 'utf8'.
21+
* specifies its encoding, otherwise is ignored. Default is 'base64'.
2222
*/
2323
constructor(signature: Data, encoding: StringEncoding = 'base64') {
2424
const signatureBuf = anyToBuffer(signature, encoding, 'signature');

src/tests/streams/VirgilStreamCipher.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ describe ('VirgilStreamCipher', () => {
5353
}, 'Illegal state');
5454
});
5555

56+
5657
it ('final cannot be called after final', () => {
5758
streamCipher.start();
5859
streamCipher.update('test', 'utf8');

src/tests/streams/VirgilStreamDecipher.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ describe ('VirgilStreamDecipher', () => {
4141
streamDecipher.final();
4242
}, 'Illegal state');
4343
});
44+
45+
it ('should return null if not signed', () => {
46+
streamDecipher.update(ciphertext);
47+
streamDecipher.final(false);
48+
const signature = streamDecipher.getSignature();
49+
streamDecipher.dispose();
50+
assert.isNull(signature)
51+
});
4452
});
4553

4654
if (process.browser) {

src/tests/streams/streamSigning.test.ts

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { VirgilStreamVerifier } from '../../streams/VirgilStreamVerifier';
44
import { VirgilPrivateKey } from '../../VirgilPrivateKey';
55
import { VirgilPublicKey } from '../../VirgilPublicKey';
66
import { createAsyncIterable, splitIntoChunks, createVirgilKeyPair } from './utils';
7+
import { VirgilStreamCipher } from '../../streams/VirgilStreamCipher';
8+
import { VirgilStreamDecipher } from '../../streams/VirgilStreamDecipher';
9+
import { StringEncoding } from '../../utils/anyToBuffer';
710

811
const CHUNK_SIZE = 65536;
912

10-
describe ('stream signing', function () {
13+
describe('stream signing', function() {
1114
this.timeout(30 * 1000);
1215

13-
describe ('signature calculation', () => {
14-
15-
it ('calulates the signature', async () => {
16+
describe('signature calculation', () => {
17+
it('calculates the signature', async () => {
1618
const { privateKey } = createVirgilKeyPair();
1719
const streamSigner = new VirgilStreamSigner();
1820
const input = Buffer.alloc(3 * 1000 * 1000).fill('foo');
@@ -27,8 +29,78 @@ describe ('stream signing', function () {
2729
});
2830
});
2931

30-
describe ('signature verification', () => {
32+
describe('sign and encrypt, then decrypt and verify', () => {
33+
let publicKey: VirgilPublicKey;
34+
let privateKey: VirgilPrivateKey;
35+
let signature: Buffer;
36+
let inputChunks: Buffer[];
37+
38+
beforeEach(async () => {
39+
({ privateKey, publicKey } = createVirgilKeyPair());
40+
const streamSigner = new VirgilStreamSigner();
41+
const input = Buffer.alloc(3 * 1000).fill('foo');
42+
inputChunks = splitIntoChunks(input, CHUNK_SIZE);
43+
44+
for await (const chunk of createAsyncIterable(inputChunks)) {
45+
streamSigner.update(chunk);
46+
}
47+
48+
signature = streamSigner.sign(privateKey);
49+
});
50+
51+
const transferSignature = async (encoding?: StringEncoding) => {
52+
assert.isTrue(Buffer.isBuffer(signature));
53+
54+
const streamCipher = new VirgilStreamCipher(
55+
publicKey,
56+
{
57+
encoding,
58+
signature: encoding ? signature.toString(encoding) : signature,
59+
}
60+
);
61+
const encryptedBuffer: Buffer[] = [];
62+
63+
encryptedBuffer.push(streamCipher.start());
64+
65+
for await (const chunk of createAsyncIterable(inputChunks)) {
66+
encryptedBuffer.push(streamCipher.update(chunk));
67+
}
68+
encryptedBuffer.push(streamCipher.final());
69+
70+
const streamDecipher = new VirgilStreamDecipher(privateKey);
71+
const decryptedBuffer: Buffer[] = [];
72+
73+
for await (const chunk of createAsyncIterable(encryptedBuffer)) {
74+
decryptedBuffer.push(streamDecipher.update(chunk));
75+
}
76+
77+
decryptedBuffer.push(streamDecipher.final(false));
78+
let transferredSignature: Buffer | string = streamDecipher.getSignature()!;
79+
assert.isNotEmpty(transferredSignature);
80+
if (encoding) transferredSignature = transferredSignature.toString(encoding);
81+
streamDecipher.dispose();
82+
assert.exists(transferredSignature);
83+
const streamVerifier = new VirgilStreamVerifier(transferredSignature!, encoding);
84+
85+
for await (const chunk of createAsyncIterable(inputChunks)) {
86+
streamVerifier.update(chunk);
87+
}
88+
89+
return streamVerifier.verify(publicKey);
90+
};
91+
92+
it('transfer base64 signature', async () => {
93+
const isVerified = await transferSignature('base64');
94+
assert.isTrue(isVerified);
95+
});
96+
97+
it('transfer buffer signature', async () => {
98+
const isVerified = await transferSignature();
99+
assert.isTrue(isVerified);
100+
});
101+
});
31102

103+
describe('signature verification', () => {
32104
it ('verifies the signature', async () => {
33105
const keyPair = createVirgilKeyPair();
34106
const streamSigner = new VirgilStreamSigner();

0 commit comments

Comments
 (0)