1
- import { decryptBulk } from '@cipherstash/protect-ffi'
1
+ import {
2
+ decryptBulkFallible ,
3
+ type DecryptResult ,
4
+ } from '@cipherstash/protect-ffi'
2
5
import { withResult , type Result } from '@byteslice/result'
3
6
import { noClientError } from '../index'
4
7
import { type ProtectError , ProtectErrorTypes } from '../..'
5
8
import { logger } from '../../../../utils/logger'
6
- import type { LockContext } from '../../identify'
7
- import type { Client , EncryptedPayload } from '../../types'
9
+ import type { LockContext , Context } from '../../identify'
10
+ import type { Client , BulkDecryptPayload , BulkDecryptedData } from '../../types'
8
11
import { ProtectOperation } from './base-operation'
9
12
10
- export type BulkDecryptPayload =
11
- | Array < { id ?: string ; c : EncryptedPayload } >
12
- | Array < EncryptedPayload | null >
13
- export type BulkDecryptedData =
14
- | Array < { id ?: string ; plaintext : string | null } >
15
- | Array < string | null >
13
+ // Helper functions for better composability
14
+ const createDecryptPayloads = (
15
+ encryptedPayloads : BulkDecryptPayload ,
16
+ lockContext ?: Context ,
17
+ ) => {
18
+ return encryptedPayloads
19
+ . map ( ( item , index ) => ( { ...item , originalIndex : index } ) )
20
+ . filter ( ( { data } ) => data !== null )
21
+ . map ( ( { id, data, originalIndex } ) => ( {
22
+ id,
23
+ ciphertext : ( typeof data === 'object' && data !== null
24
+ ? data . c
25
+ : data ) as string ,
26
+ originalIndex,
27
+ ...( lockContext && { lockContext } ) ,
28
+ } ) )
29
+ }
30
+
31
+ const createNullResult = (
32
+ encryptedPayloads : BulkDecryptPayload ,
33
+ ) : BulkDecryptedData => {
34
+ return encryptedPayloads . map ( ( { id } ) => ( {
35
+ id,
36
+ data : null ,
37
+ } ) )
38
+ }
39
+
40
+ const mapDecryptedDataToResult = (
41
+ encryptedPayloads : BulkDecryptPayload ,
42
+ decryptedData : DecryptResult [ ] ,
43
+ ) : BulkDecryptedData => {
44
+ const result : BulkDecryptedData = new Array ( encryptedPayloads . length )
45
+ let decryptedIndex = 0
46
+
47
+ for ( let i = 0 ; i < encryptedPayloads . length ; i ++ ) {
48
+ if ( encryptedPayloads [ i ] . data === null ) {
49
+ result [ i ] = { id : encryptedPayloads [ i ] . id , data : null }
50
+ } else {
51
+ const decryptResult = decryptedData [ decryptedIndex ]
52
+ if ( 'error' in decryptResult ) {
53
+ result [ i ] = {
54
+ id : encryptedPayloads [ i ] . id ,
55
+ error : decryptResult . error ,
56
+ }
57
+ } else {
58
+ result [ i ] = {
59
+ id : encryptedPayloads [ i ] . id ,
60
+ data : decryptResult . data ,
61
+ }
62
+ }
63
+ decryptedIndex ++
64
+ }
65
+ }
66
+
67
+ return result
68
+ }
16
69
17
70
export class BulkDecryptOperation extends ProtectOperation < BulkDecryptedData > {
18
71
private client : Client
@@ -38,70 +91,17 @@ export class BulkDecryptOperation extends ProtectOperation<BulkDecryptedData> {
38
91
if ( ! this . encryptedPayloads || this . encryptedPayloads . length === 0 )
39
92
return [ ]
40
93
41
- const isSimpleArray =
42
- typeof this . encryptedPayloads [ 0 ] !== 'object' ||
43
- this . encryptedPayloads [ 0 ] === null ||
44
- ! ( 'c' in ( this . encryptedPayloads [ 0 ] || { } ) )
45
- if ( isSimpleArray ) {
46
- const simplePayloads = this
47
- . encryptedPayloads as Array < EncryptedPayload | null >
48
- const nonNullPayloads = simplePayloads
49
- . map ( ( c , index ) => ( { c, index } ) )
50
- . filter ( ( { c } ) => c !== null )
51
- . map ( ( { c, index } ) => ( {
52
- ciphertext : ( typeof c === 'object' && c !== null
53
- ? c . c
54
- : c ) as string ,
55
- originalIndex : index ,
56
- } ) )
57
- if ( nonNullPayloads . length === 0 )
58
- return simplePayloads . map ( ( ) => null )
59
- const decryptedData = await decryptBulk ( this . client , nonNullPayloads )
60
- const result : Array < string | null > = new Array ( simplePayloads . length )
61
- let decryptedIndex = 0
62
- for ( let i = 0 ; i < simplePayloads . length ; i ++ ) {
63
- if ( simplePayloads [ i ] === null ) {
64
- result [ i ] = null
65
- } else {
66
- result [ i ] = decryptedData [ decryptedIndex ]
67
- decryptedIndex ++
68
- }
69
- }
70
- return result
71
- }
72
- // Array of objects (with or without id)
73
- const objPayloads = this . encryptedPayloads as Array < {
74
- id ?: string
75
- c : EncryptedPayload
76
- } >
77
- const nonNullPayloads = objPayloads
78
- . map ( ( item , index ) => ( { ...item , originalIndex : index } ) )
79
- . filter ( ( { c } ) => c !== null )
80
- . map ( ( { id, c, originalIndex } ) => ( {
81
- id,
82
- ciphertext : ( typeof c === 'object' && c !== null
83
- ? c . c
84
- : c ) as string ,
85
- originalIndex,
86
- } ) )
87
- if ( nonNullPayloads . length === 0 )
88
- return objPayloads . map ( ( { id } ) => ( { id, plaintext : null } ) )
89
- const decryptedData = await decryptBulk ( this . client , nonNullPayloads )
90
- const result : Array < { id ?: string ; plaintext : string | null } > =
91
- new Array ( objPayloads . length )
92
- let decryptedIndex = 0
93
- for ( let i = 0 ; i < objPayloads . length ; i ++ ) {
94
- if ( objPayloads [ i ] . c === null ) {
95
- result [ i ] = { id : objPayloads [ i ] . id , plaintext : null }
96
- } else {
97
- result [ i ] = {
98
- id : objPayloads [ i ] . id ,
99
- plaintext : decryptedData [ decryptedIndex ] ,
100
- }
101
- decryptedIndex ++
102
- }
94
+ const nonNullPayloads = createDecryptPayloads ( this . encryptedPayloads )
95
+
96
+ if ( nonNullPayloads . length === 0 ) {
97
+ return createNullResult ( this . encryptedPayloads )
103
98
}
104
- return result
99
+
100
+ const decryptedData = await decryptBulkFallible (
101
+ this . client ,
102
+ nonNullPayloads ,
103
+ )
104
+ return mapDecryptedDataToResult ( this . encryptedPayloads , decryptedData )
105
105
} ,
106
106
( error : unknown ) => ( {
107
107
type : ProtectErrorTypes . DecryptionError ,
@@ -136,85 +136,31 @@ export class BulkDecryptOperationWithLockContext extends ProtectOperation<BulkDe
136
136
async ( ) => {
137
137
const { client, encryptedPayloads } = this . operation . getOperation ( )
138
138
logger . debug ( 'Bulk decrypting data WITH a lock context' )
139
+
139
140
if ( ! client ) throw noClientError ( )
140
141
if ( ! encryptedPayloads || encryptedPayloads . length === 0 ) return [ ]
142
+
141
143
const context = await this . lockContext . getLockContext ( )
142
- if ( context . failure )
144
+ if ( context . failure ) {
143
145
throw new Error ( `[protect]: ${ context . failure . message } ` )
144
- const isSimpleArray =
145
- typeof encryptedPayloads [ 0 ] !== 'object' ||
146
- encryptedPayloads [ 0 ] === null ||
147
- ! ( 'c' in ( encryptedPayloads [ 0 ] || { } ) )
148
- if ( isSimpleArray ) {
149
- const simplePayloads =
150
- encryptedPayloads as Array < EncryptedPayload | null >
151
- const nonNullPayloads = simplePayloads
152
- . map ( ( c , index ) => ( { c, index } ) )
153
- . filter ( ( { c } ) => c !== null )
154
- . map ( ( { c, index } ) => ( {
155
- ciphertext : ( typeof c === 'object' && c !== null
156
- ? c . c
157
- : c ) as string ,
158
- originalIndex : index ,
159
- lockContext : context . data . context ,
160
- } ) )
161
- if ( nonNullPayloads . length === 0 )
162
- return simplePayloads . map ( ( ) => null )
163
- const decryptedData = await decryptBulk (
164
- client ,
165
- nonNullPayloads ,
166
- context . data . ctsToken ,
167
- )
168
- const result : Array < string | null > = new Array ( simplePayloads . length )
169
- let decryptedIndex = 0
170
- for ( let i = 0 ; i < simplePayloads . length ; i ++ ) {
171
- if ( simplePayloads [ i ] === null ) {
172
- result [ i ] = null
173
- } else {
174
- result [ i ] = decryptedData [ decryptedIndex ]
175
- decryptedIndex ++
176
- }
177
- }
178
- return result
179
146
}
180
- // Array of objects (with or without id)
181
- const objPayloads = encryptedPayloads as Array < {
182
- id ?: string
183
- c : EncryptedPayload
184
- } >
185
- const nonNullPayloads = objPayloads
186
- . map ( ( item , index ) => ( { ...item , originalIndex : index } ) )
187
- . filter ( ( { c } ) => c !== null )
188
- . map ( ( { id, c, originalIndex } ) => ( {
189
- id,
190
- ciphertext : ( typeof c === 'object' && c !== null
191
- ? c . c
192
- : c ) as string ,
193
- originalIndex,
194
- lockContext : context . data . context ,
195
- } ) )
196
- if ( nonNullPayloads . length === 0 )
197
- return objPayloads . map ( ( { id } ) => ( { id, plaintext : null } ) )
198
- const decryptedData = await decryptBulk (
147
+
148
+ const nonNullPayloads = createDecryptPayloads (
149
+ encryptedPayloads ,
150
+ context . data . context ,
151
+ )
152
+
153
+ if ( nonNullPayloads . length === 0 ) {
154
+ return createNullResult ( encryptedPayloads )
155
+ }
156
+
157
+ const decryptedData = await decryptBulkFallible (
199
158
client ,
200
159
nonNullPayloads ,
201
160
context . data . ctsToken ,
202
161
)
203
- const result : Array < { id ?: string ; plaintext : string | null } > =
204
- new Array ( objPayloads . length )
205
- let decryptedIndex = 0
206
- for ( let i = 0 ; i < objPayloads . length ; i ++ ) {
207
- if ( objPayloads [ i ] . c === null ) {
208
- result [ i ] = { id : objPayloads [ i ] . id , plaintext : null }
209
- } else {
210
- result [ i ] = {
211
- id : objPayloads [ i ] . id ,
212
- plaintext : decryptedData [ decryptedIndex ] ,
213
- }
214
- decryptedIndex ++
215
- }
216
- }
217
- return result
162
+
163
+ return mapDecryptedDataToResult ( encryptedPayloads , decryptedData )
218
164
} ,
219
165
( error : unknown ) => ( {
220
166
type : ProtectErrorTypes . DecryptionError ,
0 commit comments