11import { isHex } from "viem" ;
22import { privateKeyToAccount , generatePrivateKey } from "viem/accounts" ;
33import { encrypt , decrypt } from "../src/crypt.ts" ;
4- import * as fs from "fs" ;
54
65describe ( "crypt" , ( ) => {
76 const privKey = generatePrivateKey ( ) ;
87 const account = privateKeyToAccount ( privKey ) ;
98
109 const hexData = `0xa5eaba8f6b292d059d9e8c3a2f1b16af` ;
11- const jsonData = fs . readFileSync ( "./test/mocks/arbitraryData.json" , "utf8" ) ;
1210
1311 describe ( "encrypt" , ( ) => {
1412 it ( "should encrypt hex string" , ( ) => {
1513 expect ( isHex ( encrypt ( hexData , account . publicKey ) ) ) . toBeTruthy ( ) ;
1614 } ) ;
1715
18- it ( "should encrypt json string" , ( ) => {
19- expect ( isHex ( encrypt ( jsonData , account . publicKey ) ) ) . toBeTruthy ( ) ;
16+ it ( "should encrypt json string (small)" , ( ) => {
17+ const smallJson = JSON . stringify ( {
18+ name : "Alice" ,
19+ age : 30 ,
20+ active : true ,
21+ } ) ;
22+ const encrypted = encrypt ( smallJson , account . publicKey ) ;
23+ expect ( isHex ( encrypted ) ) . toBeTruthy ( ) ;
24+ expect ( decrypt ( privKey , encrypted ) ) . toBe ( smallJson ) ;
2025 } ) ;
2126
2227 it ( "should encrypt empty string" , ( ) => {
@@ -25,11 +30,18 @@ describe("crypt", () => {
2530 expect ( decrypt ( privKey , encrypted ) ) . toBe ( "" ) ;
2631 } ) ;
2732
28- it ( "should encrypt large data" , ( ) => {
29- const largeData = "x" . repeat ( 10000 ) ;
30- const encrypted = encrypt ( largeData , account . publicKey ) ;
33+ it ( "should encrypt maximum allowed data (254 bytes) " , ( ) => {
34+ const maxData = "x" . repeat ( 254 ) ;
35+ const encrypted = encrypt ( maxData , account . publicKey ) ;
3136 expect ( isHex ( encrypted ) ) . toBeTruthy ( ) ;
32- expect ( decrypt ( privKey , encrypted ) ) . toBe ( largeData ) ;
37+ expect ( decrypt ( privKey , encrypted ) ) . toBe ( maxData ) ;
38+ } ) ;
39+
40+ it ( "should throw error for data >= 255 bytes" , ( ) => {
41+ const tooLarge = "x" . repeat ( 255 ) ;
42+ expect ( ( ) => encrypt ( tooLarge , account . publicKey ) ) . toThrow (
43+ "Data length must be less than 255" ,
44+ ) ;
3345 } ) ;
3446
3547 it ( "should encrypt unicode data" , ( ) => {
@@ -70,20 +82,25 @@ describe("crypt", () => {
7082 expect ( decrypt ( privKey , encryptedData ) ) . toBe ( hexData ) ;
7183 } ) ;
7284
73- it ( "should decrypt json string" , ( ) => {
74- const encryptedData = encrypt ( jsonData , account . publicKey ) ;
75- expect ( decrypt ( privKey , encryptedData ) ) . toBe ( jsonData ) ;
85+ it ( "should decrypt json string (small)" , ( ) => {
86+ const smallJson = JSON . stringify ( {
87+ name : "Bob" ,
88+ id : 123 ,
89+ verified : false ,
90+ } ) ;
91+ const encryptedData = encrypt ( smallJson , account . publicKey ) ;
92+ expect ( decrypt ( privKey , encryptedData ) ) . toBe ( smallJson ) ;
7693 } ) ;
7794
7895 it ( "should decrypt empty string" , ( ) => {
7996 const encryptedData = encrypt ( "" , account . publicKey ) ;
8097 expect ( decrypt ( privKey , encryptedData ) ) . toBe ( "" ) ;
8198 } ) ;
8299
83- it ( "should decrypt large data" , ( ) => {
84- const largeData = "x" . repeat ( 10000 ) ;
85- const encryptedData = encrypt ( largeData , account . publicKey ) ;
86- expect ( decrypt ( privKey , encryptedData ) ) . toBe ( largeData ) ;
100+ it ( "should decrypt maximum allowed data (254 bytes) " , ( ) => {
101+ const maxData = "x" . repeat ( 254 ) ;
102+ const encryptedData = encrypt ( maxData , account . publicKey ) ;
103+ expect ( decrypt ( privKey , encryptedData ) ) . toBe ( maxData ) ;
87104 } ) ;
88105
89106 it ( "should decrypt unicode data" , ( ) => {
@@ -147,10 +164,17 @@ describe("crypt", () => {
147164 } ) ;
148165
149166 describe ( "edge cases" , ( ) => {
150- it ( "should handle very long strings" , ( ) => {
151- const longString = "a" . repeat ( 100000 ) ;
152- const encrypted = encrypt ( longString , account . publicKey ) ;
153- expect ( decrypt ( privKey , encrypted ) ) . toBe ( longString ) ;
167+ it ( "should handle maximum size strings (254 bytes)" , ( ) => {
168+ const maxString = "a" . repeat ( 254 ) ;
169+ const encrypted = encrypt ( maxString , account . publicKey ) ;
170+ expect ( decrypt ( privKey , encrypted ) ) . toBe ( maxString ) ;
171+ } ) ;
172+
173+ it ( "should throw for strings over 254 bytes" , ( ) => {
174+ const tooLong = "a" . repeat ( 255 ) ;
175+ expect ( ( ) => encrypt ( tooLong , account . publicKey ) ) . toThrow (
176+ "Data length must be less than 255" ,
177+ ) ;
154178 } ) ;
155179
156180 it ( "should handle null bytes in data" , ( ) => {
@@ -197,4 +221,85 @@ describe("crypt", () => {
197221 expect ( encrypted1 ) . not . toBe ( encrypted2 ) ;
198222 } ) ;
199223 } ) ;
224+
225+ describe ( "length obfuscation" , ( ) => {
226+ it ( "should produce same length for all plaintexts (fixed 255 bytes)" , ( ) => {
227+ const data1 = "short" ;
228+ const data2 = "x" . repeat ( 254 ) ;
229+ const data3 = "" ;
230+
231+ const encrypted1 = encrypt ( data1 , account . publicKey ) ;
232+ const encrypted2 = encrypt ( data2 , account . publicKey ) ;
233+ const encrypted3 = encrypt ( data3 , account . publicKey ) ;
234+
235+ // All should be same length (255 bytes padded + overhead)
236+ expect ( encrypted1 . length ) . toBe ( encrypted2 . length ) ;
237+ expect ( encrypted2 . length ) . toBe ( encrypted3 . length ) ;
238+ } ) ;
239+
240+ it ( "should pad to exactly 255 bytes before encryption" , ( ) => {
241+ // All encrypted data should be same length regardless of input
242+ const data1 = "test" ;
243+ const data2 = "" ;
244+ const data3 = "x" . repeat ( 100 ) ;
245+
246+ const encrypted1 = encrypt ( data1 , account . publicKey ) ;
247+ const encrypted2 = encrypt ( data2 , account . publicKey ) ;
248+ const encrypted3 = encrypt ( data3 , account . publicKey ) ;
249+
250+ // All should be exactly the same length
251+ expect ( encrypted1 . length ) . toBe ( encrypted2 . length ) ;
252+ expect ( encrypted2 . length ) . toBe ( encrypted3 . length ) ;
253+ } ) ;
254+
255+ it ( "should correctly decrypt data with different sizes" , ( ) => {
256+ const testData = [
257+ "" ,
258+ "a" ,
259+ "test data" ,
260+ "x" . repeat ( 50 ) ,
261+ "x" . repeat ( 100 ) ,
262+ "x" . repeat ( 254 ) ,
263+ ] ;
264+
265+ for ( const data of testData ) {
266+ const encrypted = encrypt ( data , account . publicKey ) ;
267+ const decrypted = decrypt ( privKey , encrypted ) ;
268+ expect ( decrypted ) . toBe ( data ) ;
269+ }
270+ } ) ;
271+
272+ it ( "should handle maximum padding (254 bytes for empty string)" , ( ) => {
273+ const data = "" ;
274+ const encrypted = encrypt ( data , account . publicKey ) ;
275+ const decrypted = decrypt ( privKey , encrypted ) ;
276+ expect ( decrypted ) . toBe ( data ) ;
277+
278+ // Should be same length as any other encryption
279+ const other = encrypt ( "test" , account . publicKey ) ;
280+ expect ( encrypted . length ) . toBe ( other . length ) ;
281+ } ) ;
282+
283+ it ( "should handle minimum padding (1 byte for 254-byte string)" , ( ) => {
284+ const data = "x" . repeat ( 254 ) ;
285+ const encrypted = encrypt ( data , account . publicKey ) ;
286+ const decrypted = decrypt ( privKey , encrypted ) ;
287+ expect ( decrypted ) . toBe ( data ) ;
288+
289+ // Should be same length as any other encryption
290+ const other = encrypt ( "test" , account . publicKey ) ;
291+ expect ( encrypted . length ) . toBe ( other . length ) ;
292+ } ) ;
293+
294+ it ( "should throw error for corrupted data" , ( ) => {
295+ const data = "test data" ;
296+ const encrypted = encrypt ( data , account . publicKey ) ;
297+
298+ // Corrupt the encrypted data
299+ const corruptedData = encrypted . slice ( 0 , - 10 ) + "ff" . repeat ( 5 ) ;
300+
301+ // Should fail at AES decryption
302+ expect ( ( ) => decrypt ( privKey , corruptedData as `0x${string } `) ) . toThrow ( ) ;
303+ } ) ;
304+ } ) ;
200305} ) ;
0 commit comments