( envelope: EncryptedEnvelope, privateKeyPem: string | Buffer, aad?: string )
| 102 | * 2. Decrypt data using decrypted DEK |
| 103 | */ |
| 104 | export function decryptWithPrivateKey( |
| 105 | envelope: EncryptedEnvelope, |
| 106 | privateKeyPem: string | Buffer, |
| 107 | aad?: string |
| 108 | ): string { |
| 109 | if (!privateKeyPem) { |
| 110 | throw new EncryptionConfigurationError('Private key parameter is required'); |
| 111 | } |
| 112 | |
| 113 | if (!envelope || typeof envelope !== 'object') { |
| 114 | throw new EncryptionFormatError('Invalid envelope: must be an object'); |
| 115 | } |
| 116 | |
| 117 | if (envelope.algorithm !== 'rsa-aes-256-gcm') { |
| 118 | throw new EncryptionFormatError( |
| 119 | `Unsupported algorithm: ${String(envelope.algorithm)}. Expected: rsa-aes-256-gcm` |
| 120 | ); |
| 121 | } |
| 122 | |
| 123 | if (envelope.version !== 1) { |
| 124 | throw new EncryptionFormatError( |
| 125 | `Unsupported version: ${String(envelope.version)}. Expected: 1` |
| 126 | ); |
| 127 | } |
| 128 | |
| 129 | if (!envelope.encryptedData || !envelope.encryptedDEK) { |
| 130 | throw new EncryptionFormatError('Invalid envelope: missing encryptedData or encryptedDEK'); |
| 131 | } |
| 132 | |
| 133 | try { |
| 134 | // Decrypt DEK using private key |
| 135 | const encryptedDEKBuffer = Buffer.from(envelope.encryptedDEK, 'base64'); |
| 136 | const dekBuffer = privateDecrypt( |
| 137 | { |
| 138 | key: privateKeyPem, |
| 139 | padding: constants.RSA_PKCS1_OAEP_PADDING, |
| 140 | oaepHash: 'sha256', |
| 141 | }, |
| 142 | encryptedDEKBuffer |
| 143 | ); |
| 144 | |
| 145 | // Decrypt data using decrypted DEK |
| 146 | const encryptedDataBuffer = Buffer.from(envelope.encryptedData, 'base64'); |
| 147 | |
| 148 | // Extract iv (first 16 bytes), encrypted data, and authTag (last 16 bytes) |
| 149 | if (encryptedDataBuffer.length < 32) { |
| 150 | throw new EncryptionFormatError('Invalid encrypted data: too short'); |
| 151 | } |
| 152 | |
| 153 | const iv = encryptedDataBuffer.subarray(0, 16); |
| 154 | const authTag = encryptedDataBuffer.subarray(encryptedDataBuffer.length - 16); |
| 155 | const encryptedData = encryptedDataBuffer.subarray(16, encryptedDataBuffer.length - 16); |
| 156 | |
| 157 | const decipher = createDecipheriv('aes-256-gcm', dekBuffer, iv); |
| 158 | if (aad !== undefined) { |
| 159 | decipher.setAAD(Buffer.from(aad, 'utf8')); |
| 160 | } |
| 161 | decipher.setAuthTag(authTag); |
no test coverage detected