@push.rocks/smartlinuxrepo 0.0.0-stage → 0.1.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.
Files changed (131) hide show
  1. package/.smartconfig.json +64 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/alpine/apkindex.d.ts +39 -0
  5. package/dist_ts/alpine/apkindex.js +211 -0
  6. package/dist_ts/alpine/pkginfo.d.ts +45 -0
  7. package/dist_ts/alpine/pkginfo.js +159 -0
  8. package/dist_ts/alpine/reader.d.ts +20 -0
  9. package/dist_ts/alpine/reader.js +89 -0
  10. package/dist_ts/arch/builder.d.ts +41 -0
  11. package/dist_ts/arch/builder.js +234 -0
  12. package/dist_ts/arch/reader.d.ts +16 -0
  13. package/dist_ts/arch/reader.js +271 -0
  14. package/dist_ts/arch/signer.d.ts +21 -0
  15. package/dist_ts/arch/signer.js +66 -0
  16. package/dist_ts/arch/vercmp.d.ts +12 -0
  17. package/dist_ts/arch/vercmp.js +170 -0
  18. package/dist_ts/debian/control.d.ts +22 -0
  19. package/dist_ts/debian/control.js +137 -0
  20. package/dist_ts/debian/gostring.d.ts +26 -0
  21. package/dist_ts/debian/gostring.js +145 -0
  22. package/dist_ts/debian/indexbuilder.d.ts +47 -0
  23. package/dist_ts/debian/indexbuilder.js +299 -0
  24. package/dist_ts/debian/mailaddress.d.ts +12 -0
  25. package/dist_ts/debian/mailaddress.js +736 -0
  26. package/dist_ts/debian/reader.d.ts +30 -0
  27. package/dist_ts/debian/reader.js +110 -0
  28. package/dist_ts/index.d.ts +23 -0
  29. package/dist_ts/index.js +18 -0
  30. package/dist_ts/plugins.d.ts +8 -0
  31. package/dist_ts/plugins.js +11 -0
  32. package/dist_ts/readers/ar.d.ts +31 -0
  33. package/dist_ts/readers/ar.js +87 -0
  34. package/dist_ts/readers/bytes.d.ts +58 -0
  35. package/dist_ts/readers/bytes.js +147 -0
  36. package/dist_ts/readers/bytesource.d.ts +40 -0
  37. package/dist_ts/readers/bytesource.js +154 -0
  38. package/dist_ts/readers/decompress.d.ts +43 -0
  39. package/dist_ts/readers/decompress.js +269 -0
  40. package/dist_ts/readers/gzipmember.d.ts +32 -0
  41. package/dist_ts/readers/gzipmember.js +176 -0
  42. package/dist_ts/readers/tar.d.ts +85 -0
  43. package/dist_ts/readers/tar.js +731 -0
  44. package/dist_ts/readers/text.d.ts +16 -0
  45. package/dist_ts/readers/text.js +96 -0
  46. package/dist_ts/rpm/builder.d.ts +28 -0
  47. package/dist_ts/rpm/builder.js +208 -0
  48. package/dist_ts/rpm/header.d.ts +138 -0
  49. package/dist_ts/rpm/header.js +546 -0
  50. package/dist_ts/rpm/reader.d.ts +20 -0
  51. package/dist_ts/rpm/reader.js +306 -0
  52. package/dist_ts/rpm/repoconfig.d.ts +15 -0
  53. package/dist_ts/rpm/repoconfig.js +38 -0
  54. package/dist_ts/rpm/repodata.d.ts +44 -0
  55. package/dist_ts/rpm/repodata.js +148 -0
  56. package/dist_ts/rpm/sign.d.ts +30 -0
  57. package/dist_ts/rpm/sign.js +183 -0
  58. package/dist_ts/rpm/updates.d.ts +38 -0
  59. package/dist_ts/rpm/updates.js +265 -0
  60. package/dist_ts/rpm/xml.d.ts +18 -0
  61. package/dist_ts/rpm/xml.js +90 -0
  62. package/dist_ts/shared/architecture.d.ts +8 -0
  63. package/dist_ts/shared/architecture.js +17 -0
  64. package/dist_ts/shared/errors.d.ts +38 -0
  65. package/dist_ts/shared/errors.js +22 -0
  66. package/dist_ts/shared/gopath.d.ts +13 -0
  67. package/dist_ts/shared/gopath.js +53 -0
  68. package/dist_ts/shared/gostrings.d.ts +14 -0
  69. package/dist_ts/shared/gostrings.js +29 -0
  70. package/dist_ts/shared/limits.d.ts +47 -0
  71. package/dist_ts/shared/limits.js +57 -0
  72. package/dist_ts/shared/types.d.ts +213 -0
  73. package/dist_ts/shared/types.js +13 -0
  74. package/dist_ts/shared/url.d.ts +18 -0
  75. package/dist_ts/shared/url.js +371 -0
  76. package/dist_ts/shared/validation.d.ts +21 -0
  77. package/dist_ts/shared/validation.js +58 -0
  78. package/dist_ts/signing/keys.d.ts +36 -0
  79. package/dist_ts/signing/keys.js +89 -0
  80. package/dist_ts/signing/openpgpframing.d.ts +33 -0
  81. package/dist_ts/signing/openpgpframing.js +93 -0
  82. package/dist_ts/signing/signers.d.ts +63 -0
  83. package/dist_ts/signing/signers.js +248 -0
  84. package/dist_ts/writers/gotar.d.ts +43 -0
  85. package/dist_ts/writers/gotar.js +218 -0
  86. package/license.md +19 -0
  87. package/package.json +56 -3
  88. package/readme.md +176 -0
  89. package/ts/00_commitinfo_data.ts +8 -0
  90. package/ts/alpine/apkindex.ts +241 -0
  91. package/ts/alpine/pkginfo.ts +183 -0
  92. package/ts/alpine/reader.ts +96 -0
  93. package/ts/arch/builder.ts +283 -0
  94. package/ts/arch/reader.ts +307 -0
  95. package/ts/arch/signer.ts +70 -0
  96. package/ts/arch/vercmp.ts +188 -0
  97. package/ts/debian/control.ts +150 -0
  98. package/ts/debian/gostring.ts +150 -0
  99. package/ts/debian/indexbuilder.ts +355 -0
  100. package/ts/debian/mailaddress.ts +754 -0
  101. package/ts/debian/reader.ts +126 -0
  102. package/ts/index.ts +46 -0
  103. package/ts/plugins.ts +13 -0
  104. package/ts/readers/ar.ts +102 -0
  105. package/ts/readers/bytes.ts +182 -0
  106. package/ts/readers/bytesource.ts +164 -0
  107. package/ts/readers/decompress.ts +332 -0
  108. package/ts/readers/gzipmember.ts +209 -0
  109. package/ts/readers/tar.ts +835 -0
  110. package/ts/readers/text.ts +91 -0
  111. package/ts/rpm/builder.ts +253 -0
  112. package/ts/rpm/header.ts +628 -0
  113. package/ts/rpm/reader.ts +317 -0
  114. package/ts/rpm/repoconfig.ts +45 -0
  115. package/ts/rpm/repodata.ts +219 -0
  116. package/ts/rpm/sign.ts +200 -0
  117. package/ts/rpm/updates.ts +314 -0
  118. package/ts/rpm/xml.ts +101 -0
  119. package/ts/shared/architecture.ts +16 -0
  120. package/ts/shared/errors.ts +78 -0
  121. package/ts/shared/gopath.ts +54 -0
  122. package/ts/shared/gostrings.ts +31 -0
  123. package/ts/shared/limits.ts +93 -0
  124. package/ts/shared/types.ts +251 -0
  125. package/ts/shared/url.ts +387 -0
  126. package/ts/shared/validation.ts +71 -0
  127. package/ts/signing/keys.ts +110 -0
  128. package/ts/signing/openpgpframing.ts +115 -0
  129. package/ts/signing/signers.ts +315 -0
  130. package/ts/writers/gotar.ts +256 -0
  131. package/README.md +0 -3
@@ -0,0 +1,93 @@
1
+ import { LinuxRepoError } from '../shared/errors.js';
2
+ import { assertAsyncIterable, assertBytes, assertString, assertTimestamp } from '../shared/validation.js';
3
+ import { singleChunk } from './signers.js';
4
+ /**
5
+ * The OpenPGP framing the codec owns: only the bytes of the signature packet
6
+ * may vary; framing and armor are deterministic.
7
+ * Armor has no headers, 64-column base64 and a CRC-24 line.
8
+ */
9
+ const BASE64_LINE = 64;
10
+ /**
11
+ * Signs the concatenation of the chunks of `data` and returns the signer's
12
+ * binary packet unchanged (Arch `.sig`, RPM header tags). The signer reads
13
+ * `data` to its end exactly once.
14
+ */
15
+ export async function createDetachedBinarySignature(inputArg) {
16
+ assertAsyncIterable(inputArg.data, 'data');
17
+ assertTimestamp(inputArg.timestamp);
18
+ const packet = await inputArg.signer.signBinary(inputArg.data, inputArg.timestamp);
19
+ assertSignatureBytes(packet);
20
+ return packet;
21
+ }
22
+ /** Signs `data` and returns an armored detached signature (`Release.gpg`, `repomd.xml.asc`). */
23
+ export async function createDetachedArmoredSignature(inputArg) {
24
+ assertBytes(inputArg.data, 'data');
25
+ return armorSignature(await createDetachedBinarySignature({
26
+ data: singleChunk(inputArg.data),
27
+ signer: inputArg.signer,
28
+ timestamp: inputArg.timestamp,
29
+ }));
30
+ }
31
+ /**
32
+ * Cleartext-signs `text` (`InRelease`). The text must be canonical: no CR and
33
+ * no trailing spaces or tabs on any line. The signed text is `text` without
34
+ * its final line break; the output always ends the text with one line break
35
+ * before the signature block, as GnuPG and Go do. Lines starting with `-` are
36
+ * dash-escaped. The header is always `Hash: SHA256`.
37
+ */
38
+ export async function createCleartextSignedMessage(inputArg) {
39
+ assertCanonicalText(inputArg.text);
40
+ assertTimestamp(inputArg.timestamp);
41
+ const signedText = inputArg.text.endsWith('\n') ? inputArg.text.slice(0, -1) : inputArg.text;
42
+ const packet = await inputArg.signer.signText(signedText, inputArg.timestamp);
43
+ assertSignatureBytes(packet);
44
+ const body = `${signedText}\n`.replace(/^-/gm, '- -');
45
+ return `-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\n${body}${armorSignature(packet)}`;
46
+ }
47
+ /** ASCII armor for one signature packet list. */
48
+ export function armorSignature(packetArg) {
49
+ const base64 = Buffer.from(packetArg.buffer, packetArg.byteOffset, packetArg.byteLength).toString('base64');
50
+ const lines = [];
51
+ for (let offset = 0; offset < base64.length; offset += BASE64_LINE) {
52
+ lines.push(base64.slice(offset, offset + BASE64_LINE));
53
+ }
54
+ return [
55
+ '-----BEGIN PGP SIGNATURE-----',
56
+ '',
57
+ ...lines,
58
+ `=${crc24Base64(packetArg)}`,
59
+ '-----END PGP SIGNATURE-----',
60
+ '',
61
+ ].join('\n');
62
+ }
63
+ /** Refuses text that is not canonical cleartext input. */
64
+ export function assertCanonicalText(textArg) {
65
+ assertString(textArg, 'text');
66
+ if (textArg.includes('\r')) {
67
+ throw new LinuxRepoError('invalid-input', 'cleartext must use LF line endings', { field: 'text' });
68
+ }
69
+ if (/[ \t]$/m.test(textArg)) {
70
+ throw new LinuxRepoError('invalid-input', 'cleartext lines must not end with spaces or tabs', { field: 'text' });
71
+ }
72
+ }
73
+ function assertSignatureBytes(packetArg) {
74
+ if (!(packetArg instanceof Uint8Array) || packetArg.length === 0) {
75
+ throw new LinuxRepoError('signing-failed', 'the signer returned no signature bytes', { field: 'signer' });
76
+ }
77
+ }
78
+ /** RFC 4880 section 6.1 CRC-24, base64-encoded. */
79
+ function crc24Base64(bytesArg) {
80
+ let crc = 0xb704ce;
81
+ for (let i = 0; i < bytesArg.length; i++) {
82
+ crc ^= bytesArg[i] << 16;
83
+ for (let bit = 0; bit < 8; bit++) {
84
+ crc <<= 1;
85
+ if (crc & 0x1000000) {
86
+ crc ^= 0x1864cfb;
87
+ }
88
+ }
89
+ }
90
+ crc &= 0xffffff;
91
+ return Buffer.from([(crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff]).toString('base64');
92
+ }
93
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbnBncGZyYW1pbmcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9zaWduaW5nL29wZW5wZ3BmcmFtaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxlQUFlLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUMxRyxPQUFPLEVBQUUsV0FBVyxFQUF1QixNQUFNLGNBQWMsQ0FBQztBQUVoRTs7OztHQUlHO0FBRUgsTUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDO0FBRXZCOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLDZCQUE2QixDQUFDLFFBSW5EO0lBQ0MsbUJBQW1CLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztJQUMzQyxlQUFlLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sTUFBTSxHQUFHLE1BQU0sUUFBUSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDbkYsb0JBQW9CLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDN0IsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELGdHQUFnRztBQUNoRyxNQUFNLENBQUMsS0FBSyxVQUFVLDhCQUE4QixDQUFDLFFBSXBEO0lBQ0MsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDbkMsT0FBTyxjQUFjLENBQ25CLE1BQU0sNkJBQTZCLENBQUM7UUFDbEMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDO1FBQ2hDLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTTtRQUN2QixTQUFTLEVBQUUsUUFBUSxDQUFDLFNBQVM7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
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Signs Alpine indexes. Consumers may implement it; adding a required member
3
+ * later needs a major release.
4
+ */
5
+ export interface IAlpineSigner {
6
+ /**
7
+ * The key name apk looks up in `/etc/apk/keys`; the index signature entry is
8
+ * `.SIGN.RSA.<keyName>`. Gitea's form is `<owner>@<sha256 hex of the SPKI
9
+ * DER public key>.rsa.pub` (see `giteaAlpineKeyName`).
10
+ */
11
+ readonly keyName: string;
12
+ /** RSASSA-PKCS1-v1_5 over SHA-1 of `data`; returns the raw signature bytes. */
13
+ sign(data: Uint8Array): Promise<Uint8Array>;
14
+ }
15
+ /**
16
+ * Signs Debian, RPM and Arch documents. Consumers may implement it; adding a
17
+ * required member later needs a major release.
18
+ *
19
+ * Contract: each call returns exactly one binary OpenPGP v4 signature packet
20
+ * (packet header included, no armor), hashed with SHA-256, whose creation time
21
+ * is `timestamp` (whole seconds). The codec does all framing (armor, cleartext
22
+ * dash-escaping, the `Hash: SHA256` header) and treats the packet as opaque.
23
+ */
24
+ export interface IOpenPgpSigner {
25
+ /**
26
+ * A signature of type 0x00 (binary document) over the concatenation of the
27
+ * chunks of `data`. The signer reads `data` to its end exactly once.
28
+ */
29
+ signBinary(data: AsyncIterable<Uint8Array>, timestamp: Date): Promise<Uint8Array>;
30
+ /**
31
+ * A signature of type 0x01 (canonical text document) over `text`. The codec
32
+ * passes canonical text only: LF line endings and no trailing spaces or tabs
33
+ * on any line; the signature hashes it with CRLF line endings.
34
+ */
35
+ signText(text: string, timestamp: Date): Promise<Uint8Array>;
36
+ }
37
+ /**
38
+ * Builds an Alpine signer from an unencrypted RSA private key in PEM form:
39
+ * PKCS#1 (`RSA PRIVATE KEY`, Gitea's stored form) or PKCS#8 (`PRIVATE KEY`).
40
+ * Anything else is `invalid-key`. Signatures are deterministic.
41
+ */
42
+ export declare function createAlpineSigner(inputArg: {
43
+ privateKeyPem: string;
44
+ keyName: string;
45
+ }): IAlpineSigner;
46
+ /**
47
+ * Builds an OpenPGP signer from an armored private key: exactly one key,
48
+ * OpenPGP v4, not encrypted, able to sign. Anything else is `invalid-key`.
49
+ * Signatures are SHA-256 and keep openpgp.js's salt notation, so their bytes
50
+ * vary per call; the produced packet is checked against the signer contract.
51
+ *
52
+ * The factory makes one trial `signBinary` and refuses the key with
53
+ * `invalid-key` when it fails, so a key that cannot meet the contract at all
54
+ * (one whose curve makes openpgp.js choose a stronger hash than SHA-256) is
55
+ * refused here. The trial signs at the latest creation time of the key, its
56
+ * subkeys and their self-signatures; a failure that depends on the signing
57
+ * time stays `signing-failed` at signing time.
58
+ */
59
+ export declare function createOpenPgpSigner(inputArg: {
60
+ privateKeyArmored: string;
61
+ }): Promise<IOpenPgpSigner>;
62
+ /** `bytesArg` as a one-chunk source, for callers that hold the whole document. */
63
+ export declare function singleChunk(bytesArg: Uint8Array): AsyncGenerator<Uint8Array>;
@@ -0,0 +1,248 @@
1
+ import * as plugins from '../plugins.js';
2
+ import { concatBytes } from '../readers/bytes.js';
3
+ import { LinuxRepoError } from '../shared/errors.js';
4
+ import { assertAsyncIterable, assertBytes, assertSegmentName, assertString, assertTimestamp, } from '../shared/validation.js';
5
+ const ALPINE_PRIVATE_KEY_LABELS = ['RSA PRIVATE KEY', 'PRIVATE KEY'];
6
+ /**
7
+ * Builds an Alpine signer from an unencrypted RSA private key in PEM form:
8
+ * PKCS#1 (`RSA PRIVATE KEY`, Gitea's stored form) or PKCS#8 (`PRIVATE KEY`).
9
+ * Anything else is `invalid-key`. Signatures are deterministic.
10
+ */
11
+ export function createAlpineSigner(inputArg) {
12
+ if (inputArg === null || typeof inputArg !== 'object') {
13
+ throw new LinuxRepoError('invalid-input', 'input must be an object', { field: 'input' });
14
+ }
15
+ assertString(inputArg.privateKeyPem, 'privateKeyPem');
16
+ assertSegmentName(inputArg.keyName, 'keyName');
17
+ const label = /-----BEGIN ([A-Z0-9 ]+)-----/.exec(inputArg.privateKeyPem)?.[1];
18
+ if (label === undefined || !ALPINE_PRIVATE_KEY_LABELS.includes(label)) {
19
+ throw new LinuxRepoError('invalid-key', 'the Alpine key must be an unencrypted PKCS#1 or PKCS#8 RSA private key PEM', {
20
+ field: 'privateKeyPem',
21
+ });
22
+ }
23
+ let key;
24
+ try {
25
+ key = plugins.crypto.createPrivateKey({ key: inputArg.privateKeyPem, format: 'pem' });
26
+ }
27
+ catch (error) {
28
+ throw new LinuxRepoError('invalid-key', 'the Alpine private key cannot be read', { field: 'privateKeyPem' }, {
29
+ cause: error,
30
+ });
31
+ }
32
+ if (key.asymmetricKeyType !== 'rsa') {
33
+ throw new LinuxRepoError('invalid-key', 'the Alpine key must be an RSA key', { field: 'privateKeyPem' });
34
+ }
35
+ const keyName = inputArg.keyName;
36
+ return {
37
+ keyName,
38
+ sign: async (data) => {
39
+ assertBytes(data, 'data');
40
+ try {
41
+ const signature = plugins.crypto.sign('sha1', data, {
42
+ key,
43
+ padding: plugins.crypto.constants.RSA_PKCS1_PADDING,
44
+ });
45
+ return new Uint8Array(signature.buffer, signature.byteOffset, signature.byteLength);
46
+ }
47
+ catch (error) {
48
+ throw new LinuxRepoError('signing-failed', 'RSA PKCS#1 v1.5 SHA-1 signing failed', { field: 'keyName' }, {
49
+ cause: error,
50
+ });
51
+ }
52
+ },
53
+ };
54
+ }
55
+ /**
56
+ * Builds an OpenPGP signer from an armored private key: exactly one key,
57
+ * OpenPGP v4, not encrypted, able to sign. Anything else is `invalid-key`.
58
+ * Signatures are SHA-256 and keep openpgp.js's salt notation, so their bytes
59
+ * vary per call; the produced packet is checked against the signer contract.
60
+ *
61
+ * The factory makes one trial `signBinary` and refuses the key with
62
+ * `invalid-key` when it fails, so a key that cannot meet the contract at all
63
+ * (one whose curve makes openpgp.js choose a stronger hash than SHA-256) is
64
+ * refused here. The trial signs at the latest creation time of the key, its
65
+ * subkeys and their self-signatures; a failure that depends on the signing
66
+ * time stays `signing-failed` at signing time.
67
+ */
68
+ export async function createOpenPgpSigner(inputArg) {
69
+ if (inputArg === null || typeof inputArg !== 'object') {
70
+ throw new LinuxRepoError('invalid-input', 'input must be an object', { field: 'input' });
71
+ }
72
+ assertString(inputArg.privateKeyArmored, 'privateKeyArmored');
73
+ // openpgp.js reads the first armor block and ignores anything after it
74
+ const armorBlocks = inputArg.privateKeyArmored.match(/^-----BEGIN PGP [A-Z ,/0-9]+-----\s*$/gm) ?? [];
75
+ if (armorBlocks.length > 1) {
76
+ throw new LinuxRepoError('invalid-key', `expected one armored OpenPGP private key block, got ${armorBlocks.length}`, {
77
+ field: 'privateKeyArmored',
78
+ });
79
+ }
80
+ let keys;
81
+ try {
82
+ keys = await plugins.openpgp.readPrivateKeys({ armoredKeys: inputArg.privateKeyArmored });
83
+ }
84
+ catch (error) {
85
+ throw new LinuxRepoError('invalid-key', 'the OpenPGP private key cannot be read', { field: 'privateKeyArmored' }, {
86
+ cause: error,
87
+ });
88
+ }
89
+ if (keys.length !== 1) {
90
+ throw new LinuxRepoError('invalid-key', `expected exactly one OpenPGP private key, got ${keys.length}`, {
91
+ field: 'privateKeyArmored',
92
+ });
93
+ }
94
+ const key = keys[0];
95
+ if (key.keyPacket.version !== 4) {
96
+ throw new LinuxRepoError('invalid-key', `only OpenPGP v4 keys are accepted, got v${key.keyPacket.version}`, {
97
+ field: 'privateKeyArmored',
98
+ });
99
+ }
100
+ if (!key.isDecrypted()) {
101
+ throw new LinuxRepoError('invalid-key', 'encrypted OpenPGP keys are not accepted', { field: 'privateKeyArmored' });
102
+ }
103
+ const sign = async (produceArg, timestampArg, signatureTypeArg, inputFailureArg = () => undefined) => {
104
+ let packet;
105
+ try {
106
+ packet = await produceArg();
107
+ }
108
+ catch (error) {
109
+ // an invalid chunk or the caller's own source error, not a signing failure
110
+ const inputFailure = inputFailureArg();
111
+ if (inputFailure !== undefined) {
112
+ throw inputFailure.error;
113
+ }
114
+ throw new LinuxRepoError('signing-failed', 'OpenPGP signing failed', { field: 'signingKey' }, { cause: error });
115
+ }
116
+ await assertSignaturePacket(packet, timestampArg, signatureTypeArg);
117
+ return packet;
118
+ };
119
+ const config = { preferredHashAlgorithm: plugins.openpgp.enums.hash.sha256 };
120
+ const signer = {
121
+ signBinary: async (data, timestamp) => {
122
+ assertAsyncIterable(data, 'data');
123
+ assertTimestamp(timestamp);
124
+ const source = new CheckedChunkSource(data);
125
+ return sign(async () => {
126
+ const signature = await plugins.openpgp.sign({
127
+ message: await plugins.openpgp.createMessage({ binary: source.toReadableStream() }),
128
+ signingKeys: key,
129
+ detached: true,
130
+ format: 'binary',
131
+ date: timestamp,
132
+ config,
133
+ });
134
+ const packet = await readAll(signature);
135
+ if (!source.ended) {
136
+ throw new Error('openpgp.js returned a signature before reading the data to its end');
137
+ }
138
+ return packet;
139
+ }, timestamp, plugins.openpgp.enums.signature.binary, () => source.failure);
140
+ },
141
+ signText: async (text, timestamp) => {
142
+ assertString(text, 'text');
143
+ assertTimestamp(timestamp);
144
+ return sign(async () => plugins.openpgp.sign({
145
+ message: await plugins.openpgp.createMessage({ text }),
146
+ signingKeys: key,
147
+ detached: true,
148
+ format: 'binary',
149
+ date: timestamp,
150
+ config,
151
+ }), timestamp, plugins.openpgp.enums.signature.text);
152
+ },
153
+ };
154
+ try {
155
+ await signer.signBinary(singleChunk(new Uint8Array(0)), latestKeyTime(key));
156
+ }
157
+ catch (error) {
158
+ throw new LinuxRepoError('invalid-key', 'the OpenPGP key cannot sign a v4 SHA-256 signature', { field: 'privateKeyArmored' }, {
159
+ cause: error,
160
+ });
161
+ }
162
+ return signer;
163
+ }
164
+ /** `bytesArg` as a one-chunk source, for callers that hold the whole document. */
165
+ export async function* singleChunk(bytesArg) {
166
+ yield bytesArg;
167
+ }
168
+ /**
169
+ * Pulls the caller's source with `next()` only (never `return()` or
170
+ * `throw()`, so a cancelled stream never cancels the source), refuses a chunk
171
+ * that is not a `Uint8Array` with `invalid-input`, and records the first
172
+ * error it raised or met so the signer can rethrow it unchanged.
173
+ */
174
+ class CheckedChunkSource {
175
+ ended = false;
176
+ failure = undefined;
177
+ source;
178
+ constructor(sourceArg) {
179
+ this.source = sourceArg;
180
+ }
181
+ toReadableStream() {
182
+ const iterator = this.source[Symbol.asyncIterator]();
183
+ const next = async () => {
184
+ let result;
185
+ try {
186
+ result = await iterator.next();
187
+ }
188
+ catch (error) {
189
+ this.failure ??= { error };
190
+ throw error;
191
+ }
192
+ if (result.done === true) {
193
+ this.ended = true;
194
+ return { done: true, value: undefined };
195
+ }
196
+ if (!(result.value instanceof Uint8Array)) {
197
+ const error = new LinuxRepoError('invalid-input', 'every chunk of data must be a Uint8Array', { field: 'data' });
198
+ this.failure ??= { error };
199
+ throw error;
200
+ }
201
+ return { done: false, value: result.value };
202
+ };
203
+ return plugins.streamWeb.ReadableStream.from({ [Symbol.asyncIterator]: () => ({ next }) });
204
+ }
205
+ }
206
+ /** Reads openpgp.js's streamed output to its end. */
207
+ async function readAll(streamArg) {
208
+ const reader = streamArg.getReader();
209
+ const chunks = [];
210
+ let total = 0;
211
+ for (let result = await reader.read(); !result.done; result = await reader.read()) {
212
+ chunks.push(result.value);
213
+ total += result.value.length;
214
+ }
215
+ return concatBytes(chunks, total);
216
+ }
217
+ /** The latest creation time of the key, its subkeys and their self-signatures: the key's current state applies. */
218
+ function latestKeyTime(keyArg) {
219
+ const times = [keyArg.getCreationTime().getTime()];
220
+ for (const user of keyArg.users) {
221
+ for (const signature of user.selfCertifications) {
222
+ times.push(signature.created?.getTime() ?? 0);
223
+ }
224
+ }
225
+ for (const subkey of keyArg.subkeys) {
226
+ times.push(subkey.getCreationTime().getTime());
227
+ for (const signature of subkey.bindingSignatures) {
228
+ times.push(signature.created?.getTime() ?? 0);
229
+ }
230
+ }
231
+ return new Date(Math.max(...times));
232
+ }
233
+ /** Checks that openpgp.js produced exactly the packet the signer contract promises. */
234
+ async function assertSignaturePacket(packetArg, timestampArg, signatureTypeArg) {
235
+ const signature = await plugins.openpgp.readSignature({ binarySignature: packetArg });
236
+ const packets = signature.packets;
237
+ const expectedSeconds = Math.floor(timestampArg.getTime() / 1000);
238
+ const ok = packets.length === 1 &&
239
+ packets[0].version === 4 &&
240
+ packets[0].signatureType === signatureTypeArg &&
241
+ packets[0].hashAlgorithm === plugins.openpgp.enums.hash.sha256 &&
242
+ packets[0].created !== null &&
243
+ Math.floor(packets[0].created.getTime() / 1000) === expectedSeconds;
244
+ if (!ok) {
245
+ throw new LinuxRepoError('signing-failed', 'the signing key cannot produce one v4 SHA-256 signature packet at the requested time', { field: 'signingKey' });
246
+ }
247
+ }
248
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbmVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3NpZ25pbmcvc2lnbmVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3JELE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsV0FBVyxFQUNYLGlCQUFpQixFQUNqQixZQUFZLEVBQ1osZUFBZSxHQUNoQixNQUFNLHlCQUF5QixDQUFDO0FBd0NqQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsaUJBQWlCLEVBQUUsYUFBYSxDQUFDLENBQUM7QUFFckU7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxrQkFBa0IsQ0FBQyxRQUFvRDtJQUNyRixJQUFJLFFBQVEsS0FBSyxJQUFJLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDdEQsTUFBTSxJQUFJLGNBQWMsQ0FBQyxlQUFlLEVBQUUseUJBQXlCLEVBQUUsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUMzRixDQUFDO0lBQ0QsWUFBWSxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDdEQsaUJBQWlCLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQztJQUMvQyxNQUFNLEtBQUssR0FBRyw4QkFBOEIsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0UsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLENBQUMseUJBQXlCLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDdEUsTUFBTSxJQUFJLGNBQWMsQ0FBQyxhQUFhLEVBQUUsNEVBQTRFLEVBQUU7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
@@ -0,0 +1,43 @@
1
+ export interface IGoTarFile {
2
+ name: string;
3
+ mode: number;
4
+ data: Uint8Array;
5
+ }
6
+ export interface IGoTarWriteOptions {
7
+ /**
8
+ * The two zero blocks Go `Writer.Close` writes. `false` for Gitea's Alpine
9
+ * signature member (`writeGzipStream` with `addTarEnd=false`). Gitea never
10
+ * calls `Close` there, so Go also leaves the last entry unpadded; the codec
11
+ * pads every entry to the block regardless (a difference from Gitea).
12
+ */
13
+ endOfArchive: boolean;
14
+ }
15
+ /**
16
+ * The subset of Go 1.27 `archive/tar` `Writer` that Gitea 28.2.0 uses for its
17
+ * index archives (Arch `services/packages/arch/repository.go:381-399`, Alpine
18
+ * `services/packages/alpine/repository.go:331-351`): regular files from
19
+ * `tar.Header{Name, Mode, Size}` with every other field zero, so the zero
20
+ * `ModTime` is written as 0, uid and gid are 0 and user and group names are
21
+ * empty.
22
+ *
23
+ * Format choice follows Go's `allowedFormats` and `WriteHeader`: USTAR when the
24
+ * name is ASCII and at most 100 bytes, USTAR with a prefix split when the name
25
+ * splits, else PAX with one `path` record (refused when the records exceed
26
+ * Go's 1 MiB `maxSpecialFileSize`, as Go returns `ErrFieldTooLong`).
27
+ *
28
+ * Refused with `invalid-input` where Go would not write a regular file in USTAR
29
+ * or PAX-`path` form:
30
+ * - a name with NUL (Go fails with an invalid PAX record) or ending with `/`
31
+ * (Go refuses it for a regular file; Gitea's untyped header would become a
32
+ * directory entry instead);
33
+ * - a mode outside 7 octal digits (Go falls back to the GNU format);
34
+ * - a size of 8 GiB or more (Go adds a PAX `size` record; the codec does not);
35
+ * - a string that is not well-formed UTF-16 (Go strings never carry one).
36
+ */
37
+ export declare function writeGoTar(filesArg: readonly IGoTarFile[], optionsArg?: IGoTarWriteOptions): Uint8Array;
38
+ /**
39
+ * The bytes Go `Writer.WriteHeader` writes for a regular file: the main
40
+ * header block, preceded by a PAX `x` header and its padded records when the
41
+ * name needs one.
42
+ */
43
+ export declare function goTarHeader(nameArg: string, modeArg: number, sizeArg: number): Uint8Array;