Verified Commit acd29436 authored by Samuel Elliott's avatar Samuel Elliott
Browse files

Export signcryption functions/streams and add armor helpers

parent 60b131d9
Pipeline #800 passed with stages
in 10 minutes and 52 seconds
......@@ -10,71 +10,75 @@ import {INPUT_STRING, SIGNED, DETACHED_SIGNATURE} from './data/signing-tests';
SignedMessageHeader.debug_fix_nonce = Buffer.alloc(32).fill('\x00');
test('sign', () => {
const signed = sign(INPUT_STRING, KEYPAIR);
describe('attached signing', () => {
test('sign', () => {
const signed = sign(INPUT_STRING, KEYPAIR);
expect(signed).toStrictEqual(SIGNED);
});
expect(signed).toStrictEqual(SIGNED);
});
test('sign stream', async () => {
const stream = new SignStream(KEYPAIR);
const result: Buffer[] = [];
test('sign stream', async () => {
const stream = new SignStream(KEYPAIR);
const result: Buffer[] = [];
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
stream.end(INPUT_STRING);
stream.end(INPUT_STRING);
});
expect(Buffer.concat(result)).toStrictEqual(SIGNED);
});
expect(Buffer.concat(result)).toStrictEqual(SIGNED);
});
test('verify', async () => {
const data = await verify(SIGNED, KEYPAIR.publicKey);
test('verify', async () => {
const data = await verify(SIGNED, KEYPAIR.publicKey);
expect(data.toString()).toBe(INPUT_STRING);
});
expect(data.toString()).toBe(INPUT_STRING);
});
test('verify stream', async () => {
const stream = new VerifyStream(KEYPAIR.publicKey);
const result: Buffer[] = [];
test('verify stream', async () => {
const stream = new VerifyStream(KEYPAIR.publicKey);
const result: Buffer[] = [];
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
stream.end(SIGNED);
});
stream.end(SIGNED);
expect(Buffer.concat(result).toString()).toBe(INPUT_STRING);
});
expect(Buffer.concat(result).toString()).toBe(INPUT_STRING);
});
test('verify with wrong public key fails', () => {
const public_key = new Uint8Array(KEYPAIR.publicKey);
public_key[0] = 0;
test('verify with wrong public key fails', () => {
const public_key = new Uint8Array(KEYPAIR.publicKey);
public_key[0] = 0;
expect(async () => {
await verify(SIGNED, public_key);
}).rejects.toThrow();
expect(async () => {
await verify(SIGNED, public_key);
}).rejects.toThrow();
});
});
test('sign detached', () => {
const signed = signDetached(INPUT_STRING, KEYPAIR);
expect(signed).toStrictEqual(DETACHED_SIGNATURE);
});
describe('detached signing', () => {
test('sign detached', () => {
const signed = signDetached(INPUT_STRING, KEYPAIR);
expect(signed).toStrictEqual(DETACHED_SIGNATURE);
});
test('verify detached', async () => {
await verifyDetached(DETACHED_SIGNATURE, INPUT_STRING, KEYPAIR.publicKey);
});
test('verify detached', async () => {
await verifyDetached(DETACHED_SIGNATURE, INPUT_STRING, KEYPAIR.publicKey);
});
test('verify detached with wrong public key fails', () => {
const public_key = KEYPAIR.publicKey;
public_key[0] = 0;
test('verify detached with wrong public key fails', () => {
const public_key = KEYPAIR.publicKey;
public_key[0] = 0;
expect(async () => {
await verifyDetached(DETACHED_SIGNATURE, INPUT_STRING, public_key);
}).rejects.toThrow();
expect(async () => {
await verifyDetached(DETACHED_SIGNATURE, INPUT_STRING, public_key);
}).rejects.toThrow();
});
});
......@@ -8,15 +8,19 @@ import {
EncryptAndArmorStream, DearmorAndDecryptStream,
signAndArmor, verifyArmored,
SignAndArmorStream, DearmorAndVerifyStream,
signcryptAndArmor, dearmorAndDesigncrypt,
SigncryptAndArmorStream, DearmorAndDesigncryptStream,
} from '../with-armor';
import * as Encryption from '../encryption';
import SignedMessageHeader from '../signing/header';
import * as Signcryption from '../signcryption';
import {INPUT_STRING} from './data/common';
import {KEYPAIR as ENCRYPTION_KEYPAIR, KEYPAIR_ALICE, KEYPAIR_BOB, KEYPAIR_MALLORY} from './data/encryption-keys';
import {ENCRYPTED} from './data/encryption-tests';
import {KEYPAIR as SIGNING_KEYPAIR} from './data/signing-keys';
import {KEYPAIR as SIGNING_KEYPAIR, KEYPAIR_ALICE as KEYPAIR_ALICE_S} from './data/signing-keys';
import {SIGNED} from './data/signing-tests';
import {SIGNCRYPTED} from './data/signcryption-tests';
// @ts-ignore
Encryption.debug_fix_key = Buffer.alloc(32).fill('\x00');
......@@ -25,122 +29,199 @@ Encryption.debug_fix_keypair = ENCRYPTION_KEYPAIR;
SignedMessageHeader.debug_fix_nonce = Buffer.alloc(32).fill('\x00');
test('encryption and armoring', async () => {
const expected = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
// @ts-ignore
Signcryption.debug_fix_key = Buffer.alloc(32).fill('\x00');
// @ts-ignore
Signcryption.debug_fix_keypair = ENCRYPTION_KEYPAIR;
const encrypted = await encryptAndArmor(INPUT_STRING, KEYPAIR_ALICE, [
KEYPAIR_BOB.publicKey,
]);
describe('encryption', () => {
test('encryption and armoring', async () => {
const expected = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
expect(encrypted).toBe(expected);
});
const encrypted = await encryptAndArmor(INPUT_STRING, KEYPAIR_ALICE, [
KEYPAIR_BOB.publicKey,
]);
expect(encrypted).toBe(expected);
});
test('streaming encryption and armoring', async () => {
const expected = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const result: string[] = [];
test('streaming encryption and armoring', async () => {
const expected = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const result: string[] = [];
const stream = new EncryptAndArmorStream(KEYPAIR_ALICE, [
KEYPAIR_BOB.publicKey,
]);
const stream = new EncryptAndArmorStream(KEYPAIR_ALICE, [
KEYPAIR_BOB.publicKey,
]);
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
stream.end(INPUT_STRING);
stream.end(INPUT_STRING);
});
expect(result.join('')).toBe(expected);
});
expect(result.join('')).toBe(expected);
});
test('dearmoring and decryption', async () => {
const encrypted = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
test('dearmoring and decryption', async () => {
const encrypted = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const decrypted = await dearmorAndDecrypt(encrypted, KEYPAIR_BOB);
const decrypted = await dearmorAndDecrypt(encrypted, KEYPAIR_BOB);
expect(decrypted.toString()).toBe(INPUT_STRING);
expect(decrypted.sender_public_key).toStrictEqual(KEYPAIR_ALICE.publicKey);
});
expect(decrypted.toString()).toBe(INPUT_STRING);
expect(decrypted.sender_public_key).toStrictEqual(KEYPAIR_ALICE.publicKey);
});
test('streaming dearmoring and decryption', async () => {
const armored = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const result: string[] = [];
test('streaming dearmoring and decryption', async () => {
const armored = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const result: string[] = [];
const stream = new DearmorAndDecryptStream(KEYPAIR_BOB);
const stream = new DearmorAndDecryptStream(KEYPAIR_BOB);
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
stream.end(armored);
});
stream.end(armored);
expect(result.join('')).toBe(INPUT_STRING);
expect(stream.info.message_type).toBe(MessageType.ENCRYPTED_MESSAGE);
expect(stream.info.app_name).toBe(null);
expect(stream.sender_public_key).toStrictEqual(KEYPAIR_ALICE.publicKey);
});
expect(result.join('')).toBe(INPUT_STRING);
expect(stream.info.message_type).toBe(MessageType.ENCRYPTED_MESSAGE);
expect(stream.info.app_name).toBe(null);
expect(stream.sender_public_key).toStrictEqual(KEYPAIR_ALICE.publicKey);
test('dearmor and decrypt with wrong keypair fails', async () => {
await expect(async () => {
const encrypted = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
await dearmorAndDecrypt(encrypted, KEYPAIR_MALLORY);
}).rejects.toThrow();
});
});
test('dearmor and decrypt with wrong keypair fails', async () => {
await expect(async () => {
const encrypted = armor(ENCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
describe('signing', () => {
test('signing and armoring', async () => {
const expected = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
await dearmorAndDecrypt(encrypted, KEYPAIR_MALLORY);
}).rejects.toThrow();
});
const signed = await signAndArmor(INPUT_STRING, SIGNING_KEYPAIR);
expect(signed).toBe(expected);
});
test('signing and armoring', async () => {
const expected = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
test('streaming signing and armoring', async () => {
const expected = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
const result: string[] = [];
const signed = await signAndArmor(INPUT_STRING, SIGNING_KEYPAIR);
const stream = new SignAndArmorStream(SIGNING_KEYPAIR);
expect(signed).toBe(expected);
});
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
test('streaming signing and armoring', async () => {
const expected = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
const result: string[] = [];
stream.end(INPUT_STRING);
});
const stream = new SignAndArmorStream(SIGNING_KEYPAIR);
expect(result.join('')).toBe(expected);
});
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
test('dearmoring and verifying', async () => {
const signed = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
stream.end(INPUT_STRING);
const verified = await verifyArmored(signed, SIGNING_KEYPAIR.publicKey);
expect(verified.toString()).toStrictEqual(INPUT_STRING);
// expect(verified.info.message_type).toBe('DETACHED SIGNATURE');
// expect(verified.info.app_name).toBe(null);
});
expect(result.join('')).toBe(expected);
});
test('streaming dearmoring and verifying', async () => {
const armored = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
const result: string[] = [];
test('dearmoring and verifying', async () => {
const signed = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
const stream = new DearmorAndVerifyStream(SIGNING_KEYPAIR.publicKey);
const verified = await verifyArmored(signed, SIGNING_KEYPAIR.publicKey);
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
expect(verified.toString()).toStrictEqual(INPUT_STRING);
// expect(verified.info.message_type).toBe('DETACHED SIGNATURE');
// expect(verified.info.app_name).toBe(null);
stream.end(armored);
});
expect(result.join('')).toBe(INPUT_STRING);
expect(stream.info.message_type).toBe(MessageType.SIGNED_MESSAGE);
expect(stream.info.app_name).toBe(null);
});
});
test('streaming dearmoring and verifying', async () => {
const armored = armor(SIGNED, {message_type: MessageType.SIGNED_MESSAGE});
const result: string[] = [];
describe('signcryption', () => {
test('encryption and armoring', async () => {
const expected = armor(SIGNCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const encrypted = await signcryptAndArmor(INPUT_STRING, KEYPAIR_ALICE_S, [
KEYPAIR_BOB.publicKey,
]);
expect(encrypted).toBe(expected);
});
test('streaming encryption and armoring', async () => {
const expected = armor(SIGNCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const result: string[] = [];
const stream = new SigncryptAndArmorStream(KEYPAIR_ALICE_S, [
KEYPAIR_BOB.publicKey,
]);
const stream = new DearmorAndVerifyStream(SIGNING_KEYPAIR.publicKey);
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk.toString()));
stream.end(INPUT_STRING);
});
stream.end(armored);
expect(result.join('')).toBe(expected);
});
expect(result.join('')).toBe(INPUT_STRING);
expect(stream.info.message_type).toBe(MessageType.SIGNED_MESSAGE);
expect(stream.info.app_name).toBe(null);
test('dearmoring and decryption', async () => {
const encrypted = armor(SIGNCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const decrypted = await dearmorAndDesigncrypt(encrypted, KEYPAIR_BOB);
expect(decrypted.toString()).toBe(INPUT_STRING);
expect(decrypted.sender_public_key).toStrictEqual(KEYPAIR_ALICE_S.publicKey);
});
test('streaming dearmoring and decryption', async () => {
const armored = armor(SIGNCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
const result: string[] = [];
const stream = new DearmorAndDesigncryptStream(KEYPAIR_BOB);
await new Promise((rs, rj) => {
stream.on('error', rj);
stream.on('end', rs);
stream.on('data', chunk => result.push(chunk));
stream.end(armored);
});
expect(result.join('')).toBe(INPUT_STRING);
expect(stream.info.message_type).toBe(MessageType.ENCRYPTED_MESSAGE);
expect(stream.info.app_name).toBe(null);
expect(stream.sender_public_key).toStrictEqual(KEYPAIR_ALICE_S.publicKey);
});
test('dearmor and decrypt with wrong keypair fails', async () => {
await expect(async () => {
const encrypted = armor(SIGNCRYPTED, {message_type: MessageType.ENCRYPTED_MESSAGE});
await dearmorAndDecrypt(encrypted, KEYPAIR_MALLORY);
}).rejects.toThrow();
});
});
......@@ -33,6 +33,16 @@ export {
verifyDetached,
} from './signing';
export {
signcrypt,
SigncryptStream,
designcrypt,
DesigncryptStream,
DesigncryptResult,
} from './signcryption';
export {
encryptAndArmor,
dearmorAndDecrypt,
......
......@@ -142,11 +142,11 @@ export class SigncryptStream extends Transform {
}
}
export interface DecryptResult extends Buffer {
export interface DesigncryptResult extends Buffer {
sender_public_key: Uint8Array | null;
}
export async function designcrypt(signcrypted: Uint8Array, keypair: tweetnacl.BoxKeyPair): Promise<DecryptResult> {
export async function designcrypt(signcrypted: Uint8Array, keypair: tweetnacl.BoxKeyPair): Promise<DesigncryptResult> {
const stream = new Readable();
stream.push(signcrypted);
stream.push(null);
......
import {encrypt, decrypt, EncryptStream, DecryptStream, DecryptResult} from './encryption';
import {sign, verify, SignStream, VerifyStream, signDetached, verifyDetached} from './signing';
import {signcrypt, designcrypt, SigncryptStream, DesigncryptStream, DesigncryptResult} from './signcryption';
import {
armor, dearmor, ArmorStream, DearmorStream, Options as ArmorOptions, MessageType, ArmorHeaderInfo, DearmorResult,
} from './armor';
......@@ -122,3 +123,55 @@ export interface DearmorAndVerifyDetachedResult {
remaining: Buffer;
header_info: ArmorHeaderInfo;
}
export async function signcryptAndArmor(
data: Uint8Array | string, keypair: tweetnacl.BoxKeyPair | null, recipients_keys: Uint8Array[]
) {
const encrypted = await signcrypt(data, keypair, recipients_keys);
return armor(encrypted, {message_type: MessageType.ENCRYPTED_MESSAGE});
}
export async function dearmorAndDesigncrypt(
encrypted: string, keypair: tweetnacl.BoxKeyPair
): Promise<DearmorAndDesigncryptResult> {
const dearmored = dearmor(encrypted);
return Object.assign(await designcrypt(dearmored, keypair), {
remaining: dearmored.remaining,
header_info: dearmored.header_info,
});
}
export type DearmorAndDesigncryptResult = DearmorResult & DesigncryptResult;
export class SigncryptAndArmorStream extends Pumpify {
constructor(
keypair: tweetnacl.BoxKeyPair | null, recipients_keys: Uint8Array[], armor_options?: Partial<ArmorOptions>
) {
const encrypt = new SigncryptStream(keypair, recipients_keys);
const armor = new ArmorStream(Object.assign({
message_type: MessageType.ENCRYPTED_MESSAGE,
}, armor_options));
super(encrypt, armor);
}
}
export class DearmorAndDesigncryptStream extends Pumpify {
readonly dearmor: DearmorStream;
readonly decrypt: DesigncryptStream;
constructor(keypair: tweetnacl.BoxKeyPair, armor_options?: Partial<ArmorOptions>) {
const dearmor = new DearmorStream(armor_options);
const decrypt = new DesigncryptStream(keypair);
super(dearmor, decrypt);
this.dearmor = dearmor;
this.decrypt = decrypt;
}
get info(): ArmorHeaderInfo {
return this.dearmor.info;
}
get sender_public_key(): Uint8Array | null {
return this.decrypt.sender_public_key;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment