pdf-lite 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.commitlintrc.cjs +25 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/workflows/docs.yaml +93 -0
- package/.github/workflows/prepare-release.yaml +79 -0
- package/.github/workflows/release.yaml +80 -0
- package/.github/workflows/test.yaml +35 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +4 -0
- package/CONTRIBUTING.md +109 -0
- package/EXAMPLES.md +1515 -0
- package/LICENSE +21 -0
- package/README.md +285 -0
- package/examples/001-create-pdf.ts +112 -0
- package/examples/002-create-encrypted-pdf.ts +121 -0
- package/examples/003-sign-pdf.ts +347 -0
- package/examples/004-incremental-update.ts +206 -0
- package/examples/005-modify-acroform.ts +374 -0
- package/examples/006-tokeniser-example.ts +131 -0
- package/examples/007-decoder-example.ts +197 -0
- package/package.json +72 -0
- package/packages/pdf-lite/README.md +3 -0
- package/packages/pdf-lite/package.json +68 -0
- package/packages/pdf-lite/scripts/create-encryption-tests.sh +41 -0
- package/packages/pdf-lite/scripts/gen-signing-keys.sh +290 -0
- package/packages/pdf-lite/scripts/generate-all-signing-keys.sh +70 -0
- package/packages/pdf-lite/src/core/decoder.ts +454 -0
- package/packages/pdf-lite/src/core/generators.ts +128 -0
- package/packages/pdf-lite/src/core/incremental-parser.ts +221 -0
- package/packages/pdf-lite/src/core/index.ts +2 -0
- package/packages/pdf-lite/src/core/objects/pdf-array.ts +54 -0
- package/packages/pdf-lite/src/core/objects/pdf-boolean.ts +19 -0
- package/packages/pdf-lite/src/core/objects/pdf-comment.ts +50 -0
- package/packages/pdf-lite/src/core/objects/pdf-date.ts +74 -0
- package/packages/pdf-lite/src/core/objects/pdf-dictionary.ts +171 -0
- package/packages/pdf-lite/src/core/objects/pdf-hexadecimal.ts +54 -0
- package/packages/pdf-lite/src/core/objects/pdf-indirect-object.ts +137 -0
- package/packages/pdf-lite/src/core/objects/pdf-name.ts +19 -0
- package/packages/pdf-lite/src/core/objects/pdf-null.ts +15 -0
- package/packages/pdf-lite/src/core/objects/pdf-number.ts +98 -0
- package/packages/pdf-lite/src/core/objects/pdf-object-reference.ts +30 -0
- package/packages/pdf-lite/src/core/objects/pdf-object.ts +107 -0
- package/packages/pdf-lite/src/core/objects/pdf-start-xref.ts +39 -0
- package/packages/pdf-lite/src/core/objects/pdf-stream.ts +687 -0
- package/packages/pdf-lite/src/core/objects/pdf-string.ts +38 -0
- package/packages/pdf-lite/src/core/objects/pdf-trailer.ts +57 -0
- package/packages/pdf-lite/src/core/objects/pdf-xref-table.ts +264 -0
- package/packages/pdf-lite/src/core/parser.ts +22 -0
- package/packages/pdf-lite/src/core/ref.ts +102 -0
- package/packages/pdf-lite/src/core/serializer.ts +68 -0
- package/packages/pdf-lite/src/core/streams/object-stream.ts +20 -0
- package/packages/pdf-lite/src/core/tokeniser.ts +687 -0
- package/packages/pdf-lite/src/core/tokens/boolean-token.ts +20 -0
- package/packages/pdf-lite/src/core/tokens/byte-offset-token.ts +20 -0
- package/packages/pdf-lite/src/core/tokens/comment-token.ts +32 -0
- package/packages/pdf-lite/src/core/tokens/end-array-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-dictionary-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-object-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-stream-token.ts +11 -0
- package/packages/pdf-lite/src/core/tokens/hexadecimal-token.ts +22 -0
- package/packages/pdf-lite/src/core/tokens/name-token.ts +19 -0
- package/packages/pdf-lite/src/core/tokens/null-token.ts +9 -0
- package/packages/pdf-lite/src/core/tokens/number-token.ts +164 -0
- package/packages/pdf-lite/src/core/tokens/object-reference-token.ts +24 -0
- package/packages/pdf-lite/src/core/tokens/start-array-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/start-dictionary-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/start-object-token.ts +28 -0
- package/packages/pdf-lite/src/core/tokens/start-stream-token.ts +52 -0
- package/packages/pdf-lite/src/core/tokens/start-xref-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/stream-chunk-token.ts +8 -0
- package/packages/pdf-lite/src/core/tokens/string-token.ts +17 -0
- package/packages/pdf-lite/src/core/tokens/token.ts +43 -0
- package/packages/pdf-lite/src/core/tokens/trailer-token.ts +12 -0
- package/packages/pdf-lite/src/core/tokens/whitespace-token.ts +43 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-entry-token.ts +65 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-section-start-token.ts +31 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-start-token.ts +13 -0
- package/packages/pdf-lite/src/crypto/ciphers/aes128.ts +63 -0
- package/packages/pdf-lite/src/crypto/ciphers/aes256.ts +50 -0
- package/packages/pdf-lite/src/crypto/ciphers/rc4.ts +82 -0
- package/packages/pdf-lite/src/crypto/constants.ts +10 -0
- package/packages/pdf-lite/src/crypto/key-derivation/key-derivation-aes256.ts +213 -0
- package/packages/pdf-lite/src/crypto/key-derivation/key-derivation.ts +122 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-aes256.ts +79 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-128.ts +190 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-40.ts +129 -0
- package/packages/pdf-lite/src/crypto/types.ts +6 -0
- package/packages/pdf-lite/src/crypto/utils.ts +81 -0
- package/packages/pdf-lite/src/filters/ascii85.ts +128 -0
- package/packages/pdf-lite/src/filters/asciihex.ts +55 -0
- package/packages/pdf-lite/src/filters/flate.ts +39 -0
- package/packages/pdf-lite/src/filters/lzw.ts +144 -0
- package/packages/pdf-lite/src/filters/pass-through.ts +37 -0
- package/packages/pdf-lite/src/filters/runlength.ts +92 -0
- package/packages/pdf-lite/src/filters/types.ts +21 -0
- package/packages/pdf-lite/src/index.ts +4 -0
- package/packages/pdf-lite/src/pdf/errors.ts +5 -0
- package/packages/pdf-lite/src/pdf/index.ts +4 -0
- package/packages/pdf-lite/src/pdf/pdf-document.ts +924 -0
- package/packages/pdf-lite/src/pdf/pdf-reader.ts +57 -0
- package/packages/pdf-lite/src/pdf/pdf-revision.ts +234 -0
- package/packages/pdf-lite/src/pdf/pdf-xref-lookup.ts +527 -0
- package/packages/pdf-lite/src/security/crypt-filters/aesv2.ts +58 -0
- package/packages/pdf-lite/src/security/crypt-filters/aesv3.ts +56 -0
- package/packages/pdf-lite/src/security/crypt-filters/base.ts +140 -0
- package/packages/pdf-lite/src/security/crypt-filters/identity.ts +40 -0
- package/packages/pdf-lite/src/security/crypt-filters/v2.ts +59 -0
- package/packages/pdf-lite/src/security/handlers/base.ts +625 -0
- package/packages/pdf-lite/src/security/handlers/pubSec.ts +413 -0
- package/packages/pdf-lite/src/security/handlers/utils.ts +304 -0
- package/packages/pdf-lite/src/security/handlers/v1.ts +225 -0
- package/packages/pdf-lite/src/security/handlers/v2.ts +128 -0
- package/packages/pdf-lite/src/security/handlers/v4.ts +379 -0
- package/packages/pdf-lite/src/security/handlers/v5.ts +298 -0
- package/packages/pdf-lite/src/security/types.ts +158 -0
- package/packages/pdf-lite/src/signing/document-security-store.ts +224 -0
- package/packages/pdf-lite/src/signing/index.ts +3 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-detached.ts +154 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-sha1.ts +161 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-x509-rsa-sha1.ts +106 -0
- package/packages/pdf-lite/src/signing/signatures/base.ts +229 -0
- package/packages/pdf-lite/src/signing/signatures/etsi-cades-detached.ts +229 -0
- package/packages/pdf-lite/src/signing/signatures/etsi-rfc3161.ts +92 -0
- package/packages/pdf-lite/src/signing/signatures/index.ts +6 -0
- package/packages/pdf-lite/src/signing/signer.ts +120 -0
- package/packages/pdf-lite/src/signing/types.ts +86 -0
- package/packages/pdf-lite/src/signing/utils.ts +71 -0
- package/packages/pdf-lite/src/types.ts +44 -0
- package/packages/pdf-lite/src/utils/IterableReadableStream.ts +30 -0
- package/packages/pdf-lite/src/utils/algos.ts +446 -0
- package/packages/pdf-lite/src/utils/assert.ts +42 -0
- package/packages/pdf-lite/src/utils/bytesToHex.ts +18 -0
- package/packages/pdf-lite/src/utils/bytesToHexBytes.ts +27 -0
- package/packages/pdf-lite/src/utils/bytesToString.ts +17 -0
- package/packages/pdf-lite/src/utils/concatUint8Arrays.ts +26 -0
- package/packages/pdf-lite/src/utils/escapeString.ts +49 -0
- package/packages/pdf-lite/src/utils/hexBytesToBytes.ts +22 -0
- package/packages/pdf-lite/src/utils/hexBytesToString.ts +21 -0
- package/packages/pdf-lite/src/utils/hexToBytes.ts +18 -0
- package/packages/pdf-lite/src/utils/padBytes.ts +25 -0
- package/packages/pdf-lite/src/utils/predictors.ts +332 -0
- package/packages/pdf-lite/src/utils/replaceInBuffer.ts +56 -0
- package/packages/pdf-lite/src/utils/stringToBytes.ts +22 -0
- package/packages/pdf-lite/src/utils/stringToHexBytes.ts +23 -0
- package/packages/pdf-lite/src/utils/unescapeString.ts +123 -0
- package/packages/pdf-lite/test/acceptance/__snapshots__/versions.node.test.ts.snap +60766 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.3/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-128.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-256.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-128.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-40.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.5/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.6/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.7/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-128.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-256.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-128.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-40.pdf +44 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic.pdf +79 -0
- package/packages/pdf-lite/test/acceptance/versions.node.test.ts +41 -0
- package/packages/pdf-lite/test/unit/__snapshots__/decoder.node.test.ts.snap +86947 -0
- package/packages/pdf-lite/test/unit/__snapshots__/tokeniser.node.test.ts.snap +131829 -0
- package/packages/pdf-lite/test/unit/ciphers.test.ts +61 -0
- package/packages/pdf-lite/test/unit/decoder.node.test.ts +21 -0
- package/packages/pdf-lite/test/unit/decoder.test.ts +567 -0
- package/packages/pdf-lite/test/unit/filters.test.ts +67 -0
- package/packages/pdf-lite/test/unit/fixtures/basic.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-128.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-256.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-128.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-40.pdf +43 -0
- package/packages/pdf-lite/test/unit/fixtures/protectedAdobeLivecycle.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/rsa-2048/index.ts +187 -0
- package/packages/pdf-lite/test/unit/fixtures/template.pdf +0 -0
- package/packages/pdf-lite/test/unit/incremental-update.test.ts +0 -0
- package/packages/pdf-lite/test/unit/objects.test.ts +0 -0
- package/packages/pdf-lite/test/unit/pdf-document-signing.test.ts +0 -0
- package/packages/pdf-lite/test/unit/pdf-revision.test.ts +195 -0
- package/packages/pdf-lite/test/unit/pdf.browser.test.ts +0 -0
- package/packages/pdf-lite/test/unit/predictors.test.ts +226 -0
- package/packages/pdf-lite/test/unit/ref.test.ts +158 -0
- package/packages/pdf-lite/test/unit/security-handlers.test.ts +645 -0
- package/packages/pdf-lite/test/unit/serializer.test.ts +81 -0
- package/packages/pdf-lite/test/unit/signature-objects.test.ts +814 -0
- package/packages/pdf-lite/test/unit/string-escaping.test.ts +84 -0
- package/packages/pdf-lite/test/unit/tokeniser.node.test.ts +38 -0
- package/packages/pdf-lite/test/unit/tokeniser.test.ts +1213 -0
- package/packages/pdf-lite/test/unit/utils.test.ts +248 -0
- package/packages/pdf-lite/test/unit/xref-lookup.test.ts +72 -0
- package/packages/pdf-lite/tsconfig.json +4 -0
- package/packages/pdf-lite/tsconfig.prod.json +8 -0
- package/packages/pdf-lite/typedoc.json +14 -0
- package/packages/pdf-lite/vitest.config.ts +43 -0
- package/pnpm-workspace.yaml +2 -0
- package/renovate.json +34 -0
- package/scripts/build-examples.ts +30 -0
- package/scripts/bump-version.sh +56 -0
- package/scripts/gen-html-docs.sh +21 -0
- package/scripts/gen-md-docs.sh +15 -0
- package/scripts/prepare-release.sh +33 -0
- package/tsconfig.json +22 -0
- package/tsconfig.prod.json +12 -0
- package/typedoc.json +34 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { deflate, Inflate } from 'pako'
|
|
2
|
+
import { bytesToHex } from './bytesToHex.js'
|
|
3
|
+
import { AlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier'
|
|
4
|
+
import 'pki-lite-crypto-extended'
|
|
5
|
+
import { ByteArray } from '../types.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Computes the SHA-1 hash of the input data.
|
|
9
|
+
*
|
|
10
|
+
* @param input - The data to hash.
|
|
11
|
+
* @returns A promise that resolves to the SHA-1 hash as a byte array.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const hash = await sha1(new Uint8Array([1, 2, 3]))
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export async function sha1(input: ByteArray): Promise<ByteArray> {
|
|
19
|
+
return AlgorithmIdentifier.digestAlgorithm('SHA-1').digest(input)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Computes the SHA-256 hash of the input data.
|
|
24
|
+
*
|
|
25
|
+
* @param input - The data to hash.
|
|
26
|
+
* @returns A promise that resolves to the SHA-256 hash as a byte array.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const hash = await sha256(new Uint8Array([1, 2, 3]))
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export async function sha256(input: ByteArray): Promise<ByteArray> {
|
|
34
|
+
return AlgorithmIdentifier.digestAlgorithm('SHA-256').digest(input)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Computes the SHA-384 hash of the input data.
|
|
39
|
+
*
|
|
40
|
+
* @param input - The data to hash.
|
|
41
|
+
* @returns A promise that resolves to the SHA-384 hash as a byte array.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const hash = await sha384(new Uint8Array([1, 2, 3]))
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export async function sha384(input: ByteArray): Promise<ByteArray> {
|
|
49
|
+
return AlgorithmIdentifier.digestAlgorithm('SHA-384').digest(input)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Computes the SHA-512 hash of the input data.
|
|
54
|
+
*
|
|
55
|
+
* @param input - The data to hash.
|
|
56
|
+
* @returns A promise that resolves to the SHA-512 hash as a byte array.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const hash = await sha512(new Uint8Array([1, 2, 3]))
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export async function sha512(input: ByteArray): Promise<ByteArray> {
|
|
64
|
+
return AlgorithmIdentifier.digestAlgorithm('SHA-512').digest(input)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Computes the MD5 hash of the input data.
|
|
69
|
+
*
|
|
70
|
+
* @param input - The data to hash.
|
|
71
|
+
* @returns A promise that resolves to the MD5 hash as a byte array.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const hash = await md5(new Uint8Array([1, 2, 3]))
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export async function md5(input: ByteArray): Promise<ByteArray> {
|
|
79
|
+
return AlgorithmIdentifier.digestAlgorithm('MD5').digest(input)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Computes a cryptographic hash of the input data using the specified algorithm.
|
|
84
|
+
*
|
|
85
|
+
* @param input - The data to hash.
|
|
86
|
+
* @param algorithm - The hash algorithm to use. Defaults to 'SHA-256'.
|
|
87
|
+
* @returns A promise that resolves to the hash as a byte array.
|
|
88
|
+
* @throws Error if an unsupported hash algorithm is specified.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const hash = await hash(data, 'SHA-256')
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export async function hash(
|
|
96
|
+
input: ByteArray,
|
|
97
|
+
algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512' | 'MD5' | 'SHA-1' = 'SHA-256',
|
|
98
|
+
): Promise<ByteArray> {
|
|
99
|
+
switch (algorithm) {
|
|
100
|
+
case 'SHA-256':
|
|
101
|
+
return sha256(input)
|
|
102
|
+
case 'SHA-384':
|
|
103
|
+
return sha384(input)
|
|
104
|
+
case 'SHA-512':
|
|
105
|
+
return sha512(input)
|
|
106
|
+
case 'MD5':
|
|
107
|
+
return md5(input)
|
|
108
|
+
case 'SHA-1':
|
|
109
|
+
return sha1(input)
|
|
110
|
+
default:
|
|
111
|
+
throw new Error(`Unsupported hash algorithm: ${algorithm}`)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Generates cryptographically secure random bytes.
|
|
117
|
+
*
|
|
118
|
+
* @param length - The number of random bytes to generate.
|
|
119
|
+
* @returns A byte array containing random bytes.
|
|
120
|
+
* @throws Error if length is not a positive integer.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const randomBytes = getRandomBytes(16) // 16 random bytes
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function getRandomBytes(length: number): ByteArray {
|
|
128
|
+
if (length <= 0) throw new Error('Length must be a positive integer')
|
|
129
|
+
return AlgorithmIdentifier.randomBytes(length)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Encrypts data using AES-128-CBC mode.
|
|
134
|
+
*
|
|
135
|
+
* @param key - The 16-byte encryption key.
|
|
136
|
+
* @param data - The data to encrypt.
|
|
137
|
+
* @param iv - The 16-byte initialization vector. Defaults to zero IV.
|
|
138
|
+
* @returns A promise that resolves to the encrypted data.
|
|
139
|
+
* @throws Error if the key is not exactly 16 bytes.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const encrypted = await aes128cbcEncrypt(key, plaintext, iv)
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export async function aes128cbcEncrypt(
|
|
147
|
+
key: ByteArray,
|
|
148
|
+
data: ByteArray,
|
|
149
|
+
iv: ByteArray = new Uint8Array(16), // Zero IV, as per PDF spec
|
|
150
|
+
): Promise<ByteArray> {
|
|
151
|
+
if (key.length !== 16) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`AES-128 key must be exactly 16 bytes, got ${key.length}`,
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
158
|
+
type: 'AES_128_CBC',
|
|
159
|
+
params: {
|
|
160
|
+
nonce: iv,
|
|
161
|
+
},
|
|
162
|
+
}).encrypt(data, key)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Decrypts data using AES-128-CBC mode.
|
|
167
|
+
*
|
|
168
|
+
* @param key - The 16-byte decryption key.
|
|
169
|
+
* @param encrypted - The encrypted data to decrypt.
|
|
170
|
+
* @param iv - The 16-byte initialization vector. Defaults to zero IV.
|
|
171
|
+
* @returns A promise that resolves to the decrypted data.
|
|
172
|
+
* @throws Error if the key is not exactly 16 bytes or encrypted data is too short.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const decrypted = await aes128cbcDecrypt(key, ciphertext, iv)
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export async function aes128cbcDecrypt(
|
|
180
|
+
key: ByteArray,
|
|
181
|
+
encrypted: ByteArray,
|
|
182
|
+
iv: ByteArray = new Uint8Array(16), // Zero IV, as per PDF spec
|
|
183
|
+
): Promise<ByteArray> {
|
|
184
|
+
if (key.length !== 16) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`AES-128 key must be exactly 16 bytes, got ${key.length}`,
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
if (encrypted.length < 16) {
|
|
190
|
+
throw new Error('Encrypted stream too short — no IV found')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
194
|
+
type: 'AES_128_CBC',
|
|
195
|
+
params: {
|
|
196
|
+
nonce: iv,
|
|
197
|
+
},
|
|
198
|
+
}).decrypt(encrypted, key)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Encrypts data using AES-256-CBC mode.
|
|
203
|
+
*
|
|
204
|
+
* @param fileKey - The 32-byte encryption key.
|
|
205
|
+
* @param block - The data to encrypt.
|
|
206
|
+
* @param iv - The 16-byte initialization vector. Defaults to zero IV.
|
|
207
|
+
* @returns A promise that resolves to the encrypted data.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const encrypted = await aes256cbcEncrypt(key, plaintext, iv)
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export async function aes256cbcEncrypt(
|
|
215
|
+
fileKey: ByteArray,
|
|
216
|
+
block: ByteArray,
|
|
217
|
+
iv: ByteArray = new Uint8Array(16), // Zero IV, as per PDF spec
|
|
218
|
+
): Promise<ByteArray> {
|
|
219
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
220
|
+
type: 'AES_256_CBC',
|
|
221
|
+
params: {
|
|
222
|
+
nonce: iv,
|
|
223
|
+
},
|
|
224
|
+
}).encrypt(block, fileKey)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Decrypts data using AES-256-CBC mode.
|
|
229
|
+
*
|
|
230
|
+
* @param fileKey - The 32-byte decryption key.
|
|
231
|
+
* @param ciphertext - The encrypted data to decrypt.
|
|
232
|
+
* @param iv - The 16-byte initialization vector. Defaults to zero IV.
|
|
233
|
+
* @returns A promise that resolves to the decrypted data.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* const decrypted = await aes256cbcDecrypt(key, ciphertext, iv)
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export async function aes256cbcDecrypt(
|
|
241
|
+
fileKey: ByteArray,
|
|
242
|
+
ciphertext: ByteArray,
|
|
243
|
+
iv: ByteArray = new Uint8Array(16), // Zero IV, as per PDF spec
|
|
244
|
+
): Promise<ByteArray> {
|
|
245
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
246
|
+
type: 'AES_256_CBC',
|
|
247
|
+
params: {
|
|
248
|
+
nonce: iv,
|
|
249
|
+
},
|
|
250
|
+
}).decrypt(ciphertext, fileKey)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Encrypts data using AES-256-ECB mode.
|
|
255
|
+
*
|
|
256
|
+
* @param fileKey - The 32-byte encryption key.
|
|
257
|
+
* @param data - The data to encrypt.
|
|
258
|
+
* @returns A promise that resolves to the encrypted data.
|
|
259
|
+
* @throws Error if the key is not exactly 32 bytes.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* const encrypted = await aes256ecbEncrypt(key, plaintext)
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
export async function aes256ecbEncrypt(
|
|
267
|
+
fileKey: ByteArray,
|
|
268
|
+
data: ByteArray,
|
|
269
|
+
): Promise<ByteArray> {
|
|
270
|
+
if (fileKey.length !== 32) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`AES-256 key must be exactly 32 bytes, got ${fileKey.length}`,
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
277
|
+
type: 'AES_256_ECB',
|
|
278
|
+
params: {},
|
|
279
|
+
}).encrypt(data, fileKey)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Decrypts data using AES-256-ECB mode.
|
|
284
|
+
*
|
|
285
|
+
* @param fileKey - The 32-byte decryption key.
|
|
286
|
+
* @param encrypted - The encrypted data to decrypt.
|
|
287
|
+
* @returns A promise that resolves to the decrypted data.
|
|
288
|
+
* @throws Error if the key is not exactly 32 bytes.
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```typescript
|
|
292
|
+
* const decrypted = await aes256ecbDecrypt(key, ciphertext)
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
export async function aes256ecbDecrypt(
|
|
296
|
+
fileKey: ByteArray,
|
|
297
|
+
encrypted: ByteArray,
|
|
298
|
+
): Promise<ByteArray> {
|
|
299
|
+
if (fileKey.length !== 32) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
`AES-256 key must be exactly 32 bytes, got ${fileKey.length}`,
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
306
|
+
type: 'AES_256_ECB',
|
|
307
|
+
params: {},
|
|
308
|
+
}).decrypt(encrypted, fileKey)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Encrypts data using AES-128-CBC mode without PKCS#7 padding.
|
|
313
|
+
*
|
|
314
|
+
* @param key - The 16-byte encryption key.
|
|
315
|
+
* @param data - The data to encrypt. Must be a multiple of 16 bytes.
|
|
316
|
+
* @param iv - The 16-byte initialization vector.
|
|
317
|
+
* @returns A promise that resolves to the encrypted data.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* const encrypted = await aes128CbcNoPaddingEncrypt(key, data, iv)
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
export async function aes128CbcNoPaddingEncrypt(
|
|
325
|
+
key: ByteArray,
|
|
326
|
+
data: ByteArray,
|
|
327
|
+
iv: ByteArray,
|
|
328
|
+
): Promise<ByteArray> {
|
|
329
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
330
|
+
type: 'AES_128_CBC',
|
|
331
|
+
params: {
|
|
332
|
+
nonce: iv,
|
|
333
|
+
disablePadding: true,
|
|
334
|
+
},
|
|
335
|
+
}).encrypt(data, key)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Encrypts data using AES-256-CBC mode without PKCS#7 padding.
|
|
340
|
+
*
|
|
341
|
+
* @param key - The 32-byte encryption key.
|
|
342
|
+
* @param data - The data to encrypt. Must be a multiple of 16 bytes.
|
|
343
|
+
* @param iv - The 16-byte initialization vector.
|
|
344
|
+
* @returns A promise that resolves to the encrypted data.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```typescript
|
|
348
|
+
* const encrypted = await aes256CbcNoPaddingEncrypt(key, data, iv)
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
export async function aes256CbcNoPaddingEncrypt(
|
|
352
|
+
key: ByteArray,
|
|
353
|
+
data: ByteArray,
|
|
354
|
+
iv: ByteArray,
|
|
355
|
+
): Promise<ByteArray> {
|
|
356
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
357
|
+
type: 'AES_256_CBC',
|
|
358
|
+
params: {
|
|
359
|
+
nonce: iv,
|
|
360
|
+
disablePadding: true,
|
|
361
|
+
},
|
|
362
|
+
}).encrypt(data, key)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Decrypts data using AES-256-CBC mode without PKCS#7 padding.
|
|
367
|
+
*
|
|
368
|
+
* @param key - The 32-byte decryption key.
|
|
369
|
+
* @param ciphertext - The encrypted data to decrypt.
|
|
370
|
+
* @param iv - The 16-byte initialization vector. Defaults to zero IV.
|
|
371
|
+
* @returns A promise that resolves to the decrypted data.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* const decrypted = await aes256CbcNoPaddingDecrypt(key, ciphertext, iv)
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
export async function aes256CbcNoPaddingDecrypt(
|
|
379
|
+
key: ByteArray,
|
|
380
|
+
ciphertext: ByteArray,
|
|
381
|
+
iv: ByteArray = new Uint8Array(16),
|
|
382
|
+
): Promise<ByteArray> {
|
|
383
|
+
return AlgorithmIdentifier.contentEncryptionAlgorithm({
|
|
384
|
+
type: 'AES_256_CBC',
|
|
385
|
+
params: {
|
|
386
|
+
nonce: iv,
|
|
387
|
+
disablePadding: true,
|
|
388
|
+
},
|
|
389
|
+
}).decrypt(ciphertext, key)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Compresses data using the DEFLATE algorithm.
|
|
394
|
+
*
|
|
395
|
+
* @param data - The data to compress.
|
|
396
|
+
* @returns The compressed data as a byte array.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* const compressed = deflateData(rawData)
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
export function deflateData(data: ByteArray): ByteArray {
|
|
404
|
+
const output = deflate(data)
|
|
405
|
+
if (!output) {
|
|
406
|
+
return new Uint8Array(0)
|
|
407
|
+
}
|
|
408
|
+
return output
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Decompresses data using the INFLATE algorithm (reverses DEFLATE).
|
|
413
|
+
*
|
|
414
|
+
* @param data - The compressed data to decompress.
|
|
415
|
+
* @returns The decompressed data as a byte array.
|
|
416
|
+
* @throws Error if inflation fails due to invalid or corrupted data.
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```typescript
|
|
420
|
+
* const decompressed = inflateData(compressedData)
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
export function inflateData(data: ByteArray): ByteArray {
|
|
424
|
+
if (data.length === 0) {
|
|
425
|
+
return new Uint8Array(0) // Return empty array for empty input
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const isWhitespace = (byte: number): boolean => {
|
|
429
|
+
return byte === 0x20 || byte === 0x09 || byte === 0x0a || byte === 0x0d
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const inflate = new Inflate()
|
|
433
|
+
inflate.push(data, true)
|
|
434
|
+
|
|
435
|
+
if (inflate.err || !inflate.result) {
|
|
436
|
+
if (isWhitespace(data[data.length - 1])) {
|
|
437
|
+
data = data.slice(0, -1) // Remove trailing whitespace]
|
|
438
|
+
return inflateData(data) // Retry inflation after removing whitespace
|
|
439
|
+
}
|
|
440
|
+
throw new Error(
|
|
441
|
+
`Inflate error: ${inflate.err} - ${inflate.msg}. Data hex: ${bytesToHex(data)}`,
|
|
442
|
+
) // Log the hex representation of the data for debugging
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return inflate.result as ByteArray
|
|
446
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asserts that a value is truthy, throwing an error if it is not.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The value to check.
|
|
5
|
+
* @param message - Optional error message to throw if the assertion fails.
|
|
6
|
+
* @throws Error if the value is falsy.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* assert(user !== null, 'User must be defined')
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export function assert(value: unknown, message?: string): asserts value {
|
|
14
|
+
if (!value) {
|
|
15
|
+
throw new Error(message)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Conditionally asserts a condition only when a value is defined.
|
|
21
|
+
* Does nothing if the value is undefined or null.
|
|
22
|
+
*
|
|
23
|
+
* @param value - The value to check for definedness.
|
|
24
|
+
* @param condition - The condition to assert if value is defined.
|
|
25
|
+
* @param message - Optional error message to throw if the assertion fails.
|
|
26
|
+
* @throws Error if value is defined and condition is falsy.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* assertIfDefined(user, user?.age >= 18, 'User must be an adult')
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function assertIfDefined(
|
|
34
|
+
value: unknown,
|
|
35
|
+
condition: unknown,
|
|
36
|
+
message?: string,
|
|
37
|
+
): asserts condition {
|
|
38
|
+
if (value === undefined || value === null) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
assert(condition, message)
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
import { bytesToHexBytes } from './bytesToHexBytes'
|
|
3
|
+
import { bytesToString } from './bytesToString'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a byte array to a hexadecimal string.
|
|
7
|
+
*
|
|
8
|
+
* @param bytes - The byte array to convert.
|
|
9
|
+
* @returns A hexadecimal string representation of the bytes.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* bytesToHex(new Uint8Array([255, 0, 127])) // Returns 'FF007F'
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function bytesToHex(bytes: ByteArray): string {
|
|
17
|
+
return bytesToString(bytesToHexBytes(bytes))
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
|
|
3
|
+
const hexChars = '0123456789ABCDEF'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a byte array to a byte array containing hexadecimal character codes.
|
|
7
|
+
* Each byte becomes two bytes representing its hex digits.
|
|
8
|
+
*
|
|
9
|
+
* @param bytes - The byte array to convert.
|
|
10
|
+
* @returns A byte array with hexadecimal character codes.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* bytesToHexBytes(new Uint8Array([255])) // Returns bytes for 'FF'
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function bytesToHexBytes(bytes: ByteArray): ByteArray {
|
|
18
|
+
const result = new Uint8Array(bytes.length * 2)
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
21
|
+
const byte = bytes[i]
|
|
22
|
+
result[i * 2] = hexChars.charCodeAt((byte >> 4) & 0x0f)
|
|
23
|
+
result[i * 2 + 1] = hexChars.charCodeAt(byte & 0x0f)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts a byte array to a string using UTF-8 decoding.
|
|
5
|
+
*
|
|
6
|
+
* @param bytes - The byte array to decode.
|
|
7
|
+
* @returns The decoded string.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* bytesToString(new Uint8Array([72, 101, 108, 108, 111])) // Returns 'Hello'
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function bytesToString(bytes: ByteArray): string {
|
|
15
|
+
const decoder = new TextDecoder()
|
|
16
|
+
return decoder.decode(bytes)
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Concatenates multiple Uint8Array instances into a single ByteArray.
|
|
5
|
+
*
|
|
6
|
+
* @param arrays - The arrays to concatenate.
|
|
7
|
+
* @returns A new ByteArray containing all input arrays concatenated in order.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const result = concatUint8Arrays(
|
|
12
|
+
* new Uint8Array([1, 2]),
|
|
13
|
+
* new Uint8Array([3, 4])
|
|
14
|
+
* ) // Returns Uint8Array([1, 2, 3, 4])
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function concatUint8Arrays(...arrays: Uint8Array[]): ByteArray {
|
|
18
|
+
const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0)
|
|
19
|
+
const result = new Uint8Array(totalLength)
|
|
20
|
+
let offset = 0
|
|
21
|
+
for (const arr of arrays) {
|
|
22
|
+
result.set(arr, offset)
|
|
23
|
+
offset += arr.length
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
import { stringToBytes } from './stringToBytes'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Escapes special characters in a PDF string according to PDF specification.
|
|
6
|
+
* Escapes parentheses, backslashes, line feeds, and carriage returns.
|
|
7
|
+
*
|
|
8
|
+
* @param bytes - The byte array or string to escape.
|
|
9
|
+
* @returns A new byte array with escaped characters.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* escapeString('Hello (World)') // Escapes the parentheses
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function escapeString(bytes: ByteArray | string): ByteArray {
|
|
17
|
+
if (typeof bytes === 'string') {
|
|
18
|
+
bytes = stringToBytes(bytes)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result: number[] = []
|
|
22
|
+
const BACKSLASH = 0x5c
|
|
23
|
+
|
|
24
|
+
for (const b of bytes) {
|
|
25
|
+
switch (b) {
|
|
26
|
+
case 0x28:
|
|
27
|
+
result.push(BACKSLASH, 0x28)
|
|
28
|
+
break // (
|
|
29
|
+
case 0x29:
|
|
30
|
+
result.push(BACKSLASH, 0x29)
|
|
31
|
+
break // )
|
|
32
|
+
case BACKSLASH:
|
|
33
|
+
result.push(BACKSLASH, BACKSLASH)
|
|
34
|
+
break // \
|
|
35
|
+
case 0x0a:
|
|
36
|
+
result.push(BACKSLASH, 0x6e)
|
|
37
|
+
break // LF
|
|
38
|
+
case 0x0d:
|
|
39
|
+
result.push(BACKSLASH, 0x72)
|
|
40
|
+
break // CR
|
|
41
|
+
default:
|
|
42
|
+
result.push(b)
|
|
43
|
+
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new Uint8Array(result)
|
|
49
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts a byte array containing hexadecimal ASCII characters to raw bytes.
|
|
5
|
+
* Each pair of hex character bytes is converted to a single byte value.
|
|
6
|
+
*
|
|
7
|
+
* @param hex - The byte array containing hex character codes.
|
|
8
|
+
* @returns A byte array with decoded values.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // 'FF' as bytes (0x46, 0x46) becomes 0xFF
|
|
13
|
+
* hexBytesToBytes(new Uint8Array([70, 70])) // Returns Uint8Array([255])
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function hexBytesToBytes(hex: ByteArray): ByteArray {
|
|
17
|
+
const bytes = new Uint8Array(hex.length / 2)
|
|
18
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
19
|
+
bytes[i / 2] = parseInt(String.fromCharCode(hex[i], hex[i + 1]), 16)
|
|
20
|
+
}
|
|
21
|
+
return bytes
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts a byte array to a lowercase hexadecimal string.
|
|
5
|
+
*
|
|
6
|
+
* @param bytes - The byte array to convert.
|
|
7
|
+
* @returns A lowercase hexadecimal string representation.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* hexBytesToString(new Uint8Array([255, 0])) // Returns 'ff00'
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function hexBytesToString(bytes: ByteArray): string {
|
|
15
|
+
let hex = ''
|
|
16
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
17
|
+
const byte = bytes[i].toString(16)
|
|
18
|
+
hex += byte.length === 1 ? '0' + byte : byte
|
|
19
|
+
}
|
|
20
|
+
return hex
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
import { hexBytesToBytes } from './hexBytesToBytes'
|
|
3
|
+
import { stringToBytes } from './stringToBytes'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a hexadecimal string to a byte array.
|
|
7
|
+
*
|
|
8
|
+
* @param hex - The hexadecimal string to convert.
|
|
9
|
+
* @returns A byte array containing the decoded values.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* hexToBytes('FF00') // Returns Uint8Array([255, 0])
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function hexToBytes(hex: string): ByteArray {
|
|
17
|
+
return hexBytesToBytes(stringToBytes(hex))
|
|
18
|
+
}
|