PacificaSigner.unit.test.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /**
  2. * Unit Tests for PacificaSigner
  3. *
  4. * Tests the Pacifica signing strategy implementation in isolation.
  5. */
  6. import { PacificaSigner } from '@/core/credential-manager/signers/PacificaSigner'
  7. import { Platform } from '@/types/credential'
  8. describe('PacificaSigner Unit Tests', () => {
  9. let signer: PacificaSigner
  10. beforeEach(() => {
  11. signer = new PacificaSigner()
  12. })
  13. describe('Platform Properties', () => {
  14. test('should have correct platform identifier', () => {
  15. expect(signer.platform).toBe(Platform.PACIFICA)
  16. })
  17. test('should have correct algorithm identifier', () => {
  18. expect(signer.algorithm).toBe('ed25519')
  19. })
  20. })
  21. describe('Public Key Derivation', () => {
  22. test('should derive public key from private key', async () => {
  23. const privateKey = 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  24. const publicKey = await signer.derivePublicKey(privateKey)
  25. expect(publicKey).toBeDefined()
  26. expect(typeof publicKey).toBe('string')
  27. expect(publicKey.length).toBe(64) // 32 bytes in hex
  28. })
  29. test('should return consistent public key for same private key', async () => {
  30. const privateKey = 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  31. const publicKey1 = await signer.derivePublicKey(privateKey)
  32. const publicKey2 = await signer.derivePublicKey(privateKey)
  33. expect(publicKey1).toBe(publicKey2)
  34. })
  35. test('should throw error for invalid private key format', async () => {
  36. const invalidKeys = [
  37. '', // Empty
  38. 'invalid-hex', // Non-hex
  39. '123', // Too short
  40. 'f'.repeat(63), // Wrong length (31 bytes)
  41. 'g'.repeat(64) // Invalid hex characters
  42. ]
  43. for (const invalidKey of invalidKeys) {
  44. await expect(signer.derivePublicKey(invalidKey))
  45. .rejects.toThrow()
  46. }
  47. })
  48. })
  49. describe('Signing Operations', () => {
  50. test('should sign message with valid credentials', async () => {
  51. const message = new TextEncoder().encode('test message')
  52. const credentials = {
  53. type: 'ed25519' as const,
  54. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  55. }
  56. const signature = await signer.sign(message, credentials)
  57. expect(signature).toBeDefined()
  58. expect(typeof signature).toBe('string')
  59. expect(signature.length).toBeGreaterThan(0)
  60. })
  61. test('should produce deterministic signatures', async () => {
  62. const message = new TextEncoder().encode('deterministic test')
  63. const credentials = {
  64. type: 'ed25519' as const,
  65. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  66. }
  67. const signature1 = await signer.sign(message, credentials)
  68. const signature2 = await signer.sign(message, credentials)
  69. expect(signature1).toBe(signature2)
  70. })
  71. test('should produce different signatures for different messages', async () => {
  72. const message1 = new TextEncoder().encode('message one')
  73. const message2 = new TextEncoder().encode('message two')
  74. const credentials = {
  75. type: 'ed25519' as const,
  76. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  77. }
  78. const signature1 = await signer.sign(message1, credentials)
  79. const signature2 = await signer.sign(message2, credentials)
  80. expect(signature1).not.toBe(signature2)
  81. })
  82. test('should produce different signatures for different private keys', async () => {
  83. const message = new TextEncoder().encode('same message')
  84. const credentials1 = {
  85. type: 'ed25519' as const,
  86. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  87. }
  88. const credentials2 = {
  89. type: 'ed25519' as const,
  90. privateKey: 'a26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  91. }
  92. const signature1 = await signer.sign(message, credentials1)
  93. const signature2 = await signer.sign(message, credentials2)
  94. expect(signature1).not.toBe(signature2)
  95. })
  96. test('should handle empty messages', async () => {
  97. const emptyMessage = new Uint8Array(0)
  98. const credentials = {
  99. type: 'ed25519' as const,
  100. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  101. }
  102. const signature = await signer.sign(emptyMessage, credentials)
  103. expect(signature).toBeDefined()
  104. expect(typeof signature).toBe('string')
  105. })
  106. test('should handle large messages', async () => {
  107. const largeMessage = new Uint8Array(1024 * 1024) // 1MB
  108. largeMessage.fill(42) // Fill with test data
  109. const credentials = {
  110. type: 'ed25519' as const,
  111. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  112. }
  113. const signature = await signer.sign(largeMessage, credentials)
  114. expect(signature).toBeDefined()
  115. expect(typeof signature).toBe('string')
  116. })
  117. test('should throw error for invalid credentials', async () => {
  118. const message = new TextEncoder().encode('test message')
  119. const invalidCredentials = [
  120. {
  121. type: 'ed25519' as const,
  122. privateKey: '' // Empty key
  123. },
  124. {
  125. type: 'ed25519' as const,
  126. privateKey: 'invalid-hex'
  127. },
  128. {
  129. type: 'ed25519' as const,
  130. privateKey: 'f'.repeat(63) // Wrong length
  131. },
  132. {
  133. type: 'wrong-type' as any,
  134. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  135. }
  136. ]
  137. for (const invalidCred of invalidCredentials) {
  138. await expect(signer.sign(message, invalidCred))
  139. .rejects.toThrow()
  140. }
  141. })
  142. })
  143. describe('Verification Operations', () => {
  144. test('should verify valid signature', async () => {
  145. const message = new TextEncoder().encode('verification test')
  146. const credentials = {
  147. type: 'ed25519' as const,
  148. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  149. }
  150. const signature = await signer.sign(message, credentials)
  151. const publicKey = await signer.derivePublicKey(credentials.privateKey)
  152. const isValid = await signer.verify(message, signature, publicKey)
  153. expect(isValid).toBe(true)
  154. })
  155. test('should reject invalid signature', async () => {
  156. const message = new TextEncoder().encode('verification test')
  157. const credentials = {
  158. type: 'ed25519' as const,
  159. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  160. }
  161. const signature = await signer.sign(message, credentials)
  162. const publicKey = await signer.derivePublicKey(credentials.privateKey)
  163. // Modify signature to make it invalid
  164. const invalidSignature = signature.slice(0, -2) + '00'
  165. const isValid = await signer.verify(message, invalidSignature, publicKey)
  166. expect(isValid).toBe(false)
  167. })
  168. test('should reject signature with wrong public key', async () => {
  169. const message = new TextEncoder().encode('verification test')
  170. const credentials1 = {
  171. type: 'ed25519' as const,
  172. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  173. }
  174. const credentials2 = {
  175. type: 'ed25519' as const,
  176. privateKey: 'a26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  177. }
  178. const signature = await signer.sign(message, credentials1)
  179. const wrongPublicKey = await signer.derivePublicKey(credentials2.privateKey)
  180. const isValid = await signer.verify(message, signature, wrongPublicKey)
  181. expect(isValid).toBe(false)
  182. })
  183. test('should reject signature with wrong message', async () => {
  184. const message1 = new TextEncoder().encode('original message')
  185. const message2 = new TextEncoder().encode('different message')
  186. const credentials = {
  187. type: 'ed25519' as const,
  188. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  189. }
  190. const signature = await signer.sign(message1, credentials)
  191. const publicKey = await signer.derivePublicKey(credentials.privateKey)
  192. const isValid = await signer.verify(message2, signature, publicKey)
  193. expect(isValid).toBe(false)
  194. })
  195. test('should handle malformed signature gracefully', async () => {
  196. const message = new TextEncoder().encode('test message')
  197. const publicKey = await signer.derivePublicKey('f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5')
  198. const malformedSignatures = [
  199. '', // Empty
  200. 'invalid', // Not hex
  201. '12345', // Too short
  202. 'g'.repeat(128) // Invalid hex characters
  203. ]
  204. for (const malformedSig of malformedSignatures) {
  205. const isValid = await signer.verify(message, malformedSig, publicKey)
  206. expect(isValid).toBe(false)
  207. }
  208. })
  209. test('should handle malformed public key gracefully', async () => {
  210. const message = new TextEncoder().encode('test message')
  211. const credentials = {
  212. type: 'ed25519' as const,
  213. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  214. }
  215. const signature = await signer.sign(message, credentials)
  216. const malformedPublicKeys = [
  217. '', // Empty
  218. 'invalid', // Not hex
  219. '12345', // Too short
  220. 'g'.repeat(64) // Invalid hex characters
  221. ]
  222. for (const malformedPubKey of malformedPublicKeys) {
  223. const isValid = await signer.verify(message, signature, malformedPubKey)
  224. expect(isValid).toBe(false)
  225. }
  226. })
  227. })
  228. describe('Performance Requirements', () => {
  229. test('should sign within performance requirements', async () => {
  230. const message = new TextEncoder().encode('performance test')
  231. const credentials = {
  232. type: 'ed25519' as const,
  233. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  234. }
  235. const startTime = performance.now()
  236. await signer.sign(message, credentials)
  237. const duration = performance.now() - startTime
  238. expect(duration).toBeLessThan(50) // Should be very fast for Ed25519
  239. })
  240. test('should verify within performance requirements', async () => {
  241. const message = new TextEncoder().encode('verification performance test')
  242. const credentials = {
  243. type: 'ed25519' as const,
  244. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  245. }
  246. const signature = await signer.sign(message, credentials)
  247. const publicKey = await signer.derivePublicKey(credentials.privateKey)
  248. const startTime = performance.now()
  249. await signer.verify(message, signature, publicKey)
  250. const duration = performance.now() - startTime
  251. expect(duration).toBeLessThan(50) // Should be fast
  252. })
  253. test('should handle concurrent operations efficiently', async () => {
  254. const message = new TextEncoder().encode('concurrent test')
  255. const credentials = {
  256. type: 'ed25519' as const,
  257. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  258. }
  259. const startTime = performance.now()
  260. const promises = Array.from({ length: 10 }, () => signer.sign(message, credentials))
  261. const signatures = await Promise.all(promises)
  262. const duration = performance.now() - startTime
  263. expect(signatures).toHaveLength(10)
  264. expect(signatures.every(sig => sig === signatures[0])).toBe(true) // Deterministic
  265. expect(duration).toBeLessThan(100) // 10 concurrent operations
  266. })
  267. })
  268. describe('Credential Validation', () => {
  269. test('should accept valid Ed25519 credentials', () => {
  270. const validCredentials = [
  271. {
  272. type: 'ed25519' as const,
  273. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  274. },
  275. {
  276. type: 'ed25519' as const,
  277. privateKey: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
  278. },
  279. {
  280. type: 'ed25519' as const,
  281. privateKey: 'ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789'
  282. }
  283. ]
  284. validCredentials.forEach(creds => {
  285. expect(() => signer.validateCredentials(creds)).not.toThrow()
  286. })
  287. })
  288. test('should reject invalid credential types', () => {
  289. const invalidCredentials = [
  290. {
  291. type: 'secp256k1' as any,
  292. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  293. },
  294. {
  295. type: 'hmac' as any,
  296. apiKey: 'test',
  297. secretKey: 'test'
  298. },
  299. {
  300. type: 'rsa' as any,
  301. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  302. }
  303. ]
  304. invalidCredentials.forEach(creds => {
  305. expect(() => signer.validateCredentials(creds)).toThrow()
  306. })
  307. })
  308. test('should reject malformed private keys', () => {
  309. const invalidPrivateKeys = [
  310. '', // Empty
  311. 'invalid-hex',
  312. '12345', // Too short
  313. 'f'.repeat(63), // 31 bytes
  314. 'f'.repeat(65), // 33 bytes
  315. 'g'.repeat(64) // Invalid hex
  316. ]
  317. invalidPrivateKeys.forEach(privateKey => {
  318. const credentials = {
  319. type: 'ed25519' as const,
  320. privateKey
  321. }
  322. expect(() => signer.validateCredentials(credentials)).toThrow()
  323. })
  324. })
  325. })
  326. describe('Edge Cases', () => {
  327. test('should handle null and undefined inputs gracefully', async () => {
  328. const credentials = {
  329. type: 'ed25519' as const,
  330. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  331. }
  332. // These should throw appropriate errors, not crash
  333. await expect(signer.sign(null as any, credentials)).rejects.toThrow()
  334. await expect(signer.sign(undefined as any, credentials)).rejects.toThrow()
  335. const message = new TextEncoder().encode('test')
  336. await expect(signer.sign(message, null as any)).rejects.toThrow()
  337. await expect(signer.sign(message, undefined as any)).rejects.toThrow()
  338. })
  339. test('should handle very large messages efficiently', async () => {
  340. const megaMessage = new Uint8Array(10 * 1024 * 1024) // 10MB
  341. megaMessage.fill(123)
  342. const credentials = {
  343. type: 'ed25519' as const,
  344. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  345. }
  346. const startTime = performance.now()
  347. const signature = await signer.sign(megaMessage, credentials)
  348. const duration = performance.now() - startTime
  349. expect(signature).toBeDefined()
  350. expect(duration).toBeLessThan(1000) // Should handle large messages within 1 second
  351. })
  352. test('should maintain consistency across multiple instances', async () => {
  353. const signer1 = new PacificaSigner()
  354. const signer2 = new PacificaSigner()
  355. const message = new TextEncoder().encode('consistency test')
  356. const credentials = {
  357. type: 'ed25519' as const,
  358. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  359. }
  360. const signature1 = await signer1.sign(message, credentials)
  361. const signature2 = await signer2.sign(message, credentials)
  362. expect(signature1).toBe(signature2)
  363. })
  364. })
  365. })