|
| 1 | +import wolfssl from '@src/addon/wolfssl'; |
| 2 | + |
| 3 | +import Events from 'events'; |
| 4 | +import StreamBuffers from 'stream-buffers'; |
| 5 | +import { CryptographicService } from '@src/cryptographic-service'; |
| 6 | +import { Decryptor } from '@src/decryptor'; |
| 7 | +import { WolfSSLDecryptor } from './wolfssl-decryptor'; |
| 8 | +import { DecryptionStream } from '@src/streams/decryption-stream'; |
| 9 | +import { cryptographicServiceFactory } from '@src/cryptographic-services'; |
| 10 | + |
| 11 | +describe('DecryptionStream', () => { |
| 12 | + test('should transform all the bytes when pipe is called with other stream', async () => { |
| 13 | + const input = new StreamBuffers.ReadableStreamBuffer(); |
| 14 | + const service = cryptographicServiceFactory.create('null'); |
| 15 | + const stream = new DecryptionStream( |
| 16 | + service, |
| 17 | + { 'cipher': 'AES-256-CBC', 'key': Buffer.alloc(0), 'iv': Buffer.alloc(0) } |
| 18 | + ); |
| 19 | + |
| 20 | + const spyTransform = jest.spyOn(stream, '_transform'); |
| 21 | + const spyFlush = jest.spyOn(stream, '_flush'); |
| 22 | + |
| 23 | + input.put(Buffer.from('test message')); |
| 24 | + input.stop(); |
| 25 | + input.pipe(stream); |
| 26 | + |
| 27 | + await Events.once(stream, 'finish'); |
| 28 | + |
| 29 | + expect(spyTransform).toHaveBeenCalledWith(Buffer.from('test message'), 'buffer', expect.anything()); |
| 30 | + expect(spyFlush).toHaveBeenCalledTimes(1); |
| 31 | + }); |
| 32 | +}); |
| 33 | + |
| 34 | +import { WolfSSLDecryptor } from '@src/cryptographic-services/wolfssl-decryptor'; |
| 35 | + |
| 36 | +describe('WolfSSLDecryptor', () => { |
| 37 | + test('should successfully decrypt an encrypted text by calling update and finalize', () => { |
| 38 | + const key = Buffer.from('12345678901234567890123456789012'); |
| 39 | + const iv = Buffer.from('1234567890123456'); |
| 40 | + const decrypt = new WolfSSLDecryptor('AES-256-CBC', key, iv); |
| 41 | + const expected = 'test'; |
| 42 | + |
| 43 | + const actual = Buffer.concat([ |
| 44 | + decrypt.update(Buffer.from('24d31b1e41fc8c40', 'hex')), |
| 45 | + decrypt.update(Buffer.from('e521531d67c72c20', 'hex')), |
| 46 | + decrypt.finalize() |
| 47 | + ]); |
| 48 | + |
| 49 | + expect(actual.toString('utf8')).toStrictEqual(expected); |
| 50 | + }); |
| 51 | + |
| 52 | + test('should throw an error when calling finalize and the decryption was invalid', () => { |
| 53 | + const key = Buffer.from('12345678901234567890123456789012'); |
| 54 | + const iv = Buffer.from('1234567890123456'); |
| 55 | + const decrypt = new WolfSSLDecryptor('AES-256-CBC', key, iv); |
| 56 | + |
| 57 | + decrypt.update(Buffer.from('not a valid encrypted text')); |
| 58 | + |
| 59 | + expect(() => decrypt.finalize()).toThrowError(); |
| 60 | + }); |
| 61 | +}); |
| 62 | + |
| 63 | + |
| 64 | +export class WolfSSLCryptographicService implements CryptographicService { |
| 65 | + /** |
| 66 | + * Initializes a new instance of the WolfSSLCryptographicService. |
| 67 | + */ |
| 68 | + public constructor() { |
| 69 | + if (!wolfssl.isFipsEnabled() && !wolfssl.enableFips()) { |
| 70 | + logger.logWarning('FIPS mode not available.'); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Encrypts the given data. |
| 76 | + * |
| 77 | + * @param data The data to encrypt. |
| 78 | + * |
| 79 | + * @returns The encrypted data blob. |
| 80 | + * |
| 81 | + * @throws {Error} If the operation fails. |
| 82 | + * |
| 83 | + * @remarks The encrypted data blob layout is defined by the cryptographic |
| 84 | + * service which could or not be standard. Decryption must be done with a |
| 85 | + * compatible cryptographic service. |
| 86 | + */ |
| 87 | + public async encrypt(data: Buffer): Promise<Buffer> { |
| 88 | + if (data.length > 0) { |
| 89 | + throw new Error('Encryption is not implemented.'); |
| 90 | + } |
| 91 | + |
| 92 | + throw new Error('Encryption is not implemented.'); |
| 93 | + } |
| 94 | + |
| 95 | + /** |
| 96 | + * Decrypts the given data. |
| 97 | + * |
| 98 | + * @param data The encrypted data to decrypt. |
| 99 | + * |
| 100 | + * @returns The decrypted data. |
| 101 | + * |
| 102 | + * @throws {Error} If the operation fails. |
| 103 | + */ |
| 104 | + public async decrypt(data: Buffer): Promise<Buffer> { |
| 105 | + if (data.length > 0) { |
| 106 | + throw new Error('Decryption is not implemented.'); |
| 107 | + } |
| 108 | + |
| 109 | + throw new Error('Decryption is not implemented.'); |
| 110 | + } |
| 111 | + |
| 112 | + /** |
| 113 | + * Creates a Decryption instance used to decrypt data. |
| 114 | + * |
| 115 | + * @param cipher The decryption cipher name to use. |
| 116 | + * @param key The decryption key to use. |
| 117 | + * @param iv The initialization vector. |
| 118 | + * |
| 119 | + * @returns A new instance of the Decryptor class. |
| 120 | + * |
| 121 | + * @throws {Error} If cipher is not available or unknown. |
| 122 | + * @throws {Error} If the creation of the Decryption object failed. |
| 123 | + */ |
| 124 | + public createDecryptor(cipher: string, key: Buffer, iv: Buffer): Decryptor { |
| 125 | + return new WolfSSLDecryptor(cipher, key, iv); |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | + |
| 130 | +/** |
| 131 | + * The WolfSSLDecryptor class that provides decryption functionalities for data in chunks of |
| 132 | + * arbitrary size using the WolfSSL addon. |
| 133 | + */ |
| 134 | +export class WolfSSLDecryptor implements Decryptor { |
| 135 | + private readonly decryption: wolfssl.Decryption; |
| 136 | + |
| 137 | + /** |
| 138 | + * Initializes a new instance of the WolfSSLDecryptor class. |
| 139 | + * |
| 140 | + * @param cipher The cipher name to use. |
| 141 | + * @param key The decryption key to use. |
| 142 | + * @param iv The initialization vector. |
| 143 | + * |
| 144 | + * @throws {Error} If cipher is not available or unknown. |
| 145 | + * @throws {Error} If the creation of the Decryption object failed. |
| 146 | + */ |
| 147 | + public constructor(cipher: string, key: Buffer, iv: Buffer) { |
| 148 | + this.enableFips(); |
| 149 | + this.decryption = new wolfssl.Decryption(cipher, key, iv); |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * Updates the internal state with data for decryption. |
| 154 | + * |
| 155 | + * @param data The data that will be added for decryption. |
| 156 | + * |
| 157 | + * @returns The decrypted data if possible. |
| 158 | + * |
| 159 | + * @throws {Error} If the decryption fails. |
| 160 | + * |
| 161 | + * @remarks This function should be called multiple times. |
| 162 | + */ |
| 163 | + public update(data: Buffer): Buffer { |
| 164 | + return this.decryption.update(data); |
| 165 | + } |
| 166 | + |
| 167 | + /** |
| 168 | + * Finalize the decryption process. |
| 169 | + * |
| 170 | + * @returns The last block of decrypted data. |
| 171 | + * |
| 172 | + * @throws {Error} If the decryption fails. |
| 173 | + * |
| 174 | + * @remarks This function should be called once to finalize the decryption |
| 175 | + * process. |
| 176 | + */ |
| 177 | + public finalize(): Buffer { |
| 178 | + return this.decryption.finalize(); |
| 179 | + } |
| 180 | + |
| 181 | + /** |
| 182 | + * Enables the FIPS mode. |
| 183 | + */ |
| 184 | + private enableFips(): void { |
| 185 | + if (!wolfssl.isFipsEnabled() && !wolfssl.enableFips()) { |
| 186 | + logger.logWarning('FIPS mode not available.'); |
| 187 | + } |
| 188 | + } |
| 189 | +} |
0 commit comments