goscript 0.2.1 → 0.2.3

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 (164) hide show
  1. package/compiler/gotest/runner.go +98 -0
  2. package/compiler/gotest/runner_test.go +45 -0
  3. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  4. package/compiler/lowering.go +227 -11
  5. package/compiler/override-registry_test.go +50 -0
  6. package/compiler/protobuf-ts-binding.go +155 -7
  7. package/compiler/protobuf-ts-binding_test.go +116 -2
  8. package/compiler/runtime-contract.go +2 -0
  9. package/compiler/runtime-contract_test.go +1 -0
  10. package/compiler/semantic-model.go +16 -0
  11. package/compiler/semantic-model_test.go +38 -0
  12. package/compiler/skeleton_test.go +477 -16
  13. package/compiler/typescript-emitter.go +4 -0
  14. package/dist/gs/builtin/builtin.js +7 -9
  15. package/dist/gs/builtin/builtin.js.map +1 -1
  16. package/dist/gs/builtin/defer.js +2 -2
  17. package/dist/gs/builtin/hostio.js +5 -5
  18. package/dist/gs/builtin/hostio.js.map +1 -1
  19. package/dist/gs/builtin/map.js +2 -1
  20. package/dist/gs/builtin/map.js.map +1 -1
  21. package/dist/gs/builtin/slice.d.ts +3 -0
  22. package/dist/gs/builtin/slice.js +39 -0
  23. package/dist/gs/builtin/slice.js.map +1 -1
  24. package/dist/gs/builtin/type.js +49 -0
  25. package/dist/gs/builtin/type.js.map +1 -1
  26. package/dist/gs/compress/zlib/index.js +5 -2
  27. package/dist/gs/compress/zlib/index.js.map +1 -1
  28. package/dist/gs/crypto/aes/index.d.ts +15 -0
  29. package/dist/gs/crypto/aes/index.js +57 -0
  30. package/dist/gs/crypto/aes/index.js.map +1 -0
  31. package/dist/gs/crypto/cipher/index.d.ts +41 -0
  32. package/dist/gs/crypto/cipher/index.js +255 -0
  33. package/dist/gs/crypto/cipher/index.js.map +1 -0
  34. package/dist/gs/crypto/ecdh/index.js +27 -8
  35. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  36. package/dist/gs/crypto/ed25519/index.js +3 -3
  37. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  38. package/dist/gs/crypto/rand/index.js +6 -3
  39. package/dist/gs/crypto/rand/index.js.map +1 -1
  40. package/dist/gs/embed/index.js +9 -3
  41. package/dist/gs/embed/index.js.map +1 -1
  42. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  43. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  44. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  45. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  46. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  47. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
  48. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
  49. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
  50. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  51. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  52. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  53. package/dist/gs/hash/fnv/index.js +13 -5
  54. package/dist/gs/hash/fnv/index.js.map +1 -1
  55. package/dist/gs/io/fs/glob.d.ts +3 -3
  56. package/dist/gs/io/fs/glob.js +8 -8
  57. package/dist/gs/io/fs/glob.js.map +1 -1
  58. package/dist/gs/io/fs/readdir.d.ts +2 -2
  59. package/dist/gs/io/fs/readdir.js +13 -74
  60. package/dist/gs/io/fs/readdir.js.map +1 -1
  61. package/dist/gs/io/fs/sub.js +4 -4
  62. package/dist/gs/io/fs/sub.js.map +1 -1
  63. package/dist/gs/io/fs/walk.js +1 -1
  64. package/dist/gs/io/fs/walk.js.map +1 -1
  65. package/dist/gs/io/io.js +18 -2
  66. package/dist/gs/io/io.js.map +1 -1
  67. package/dist/gs/maps/iter.js.map +1 -1
  68. package/dist/gs/maps/maps.js.map +1 -1
  69. package/dist/gs/mime/index.js +5 -2
  70. package/dist/gs/mime/index.js.map +1 -1
  71. package/dist/gs/net/http/httptest/index.js +6 -3
  72. package/dist/gs/net/http/httptest/index.js.map +1 -1
  73. package/dist/gs/net/http/index.d.ts +16 -4
  74. package/dist/gs/net/http/index.js +236 -40
  75. package/dist/gs/net/http/index.js.map +1 -1
  76. package/dist/gs/net/http/pprof/index.js.map +1 -1
  77. package/dist/gs/reflect/iter.js +1 -1
  78. package/dist/gs/reflect/iter.js.map +1 -1
  79. package/dist/gs/reflect/type.d.ts +2 -0
  80. package/dist/gs/reflect/type.js +53 -21
  81. package/dist/gs/reflect/type.js.map +1 -1
  82. package/dist/gs/runtime/debug/index.js +2 -1
  83. package/dist/gs/runtime/debug/index.js.map +1 -1
  84. package/dist/gs/runtime/pprof/index.js.map +1 -1
  85. package/dist/gs/runtime/runtime.js +2 -2
  86. package/dist/gs/runtime/runtime.js.map +1 -1
  87. package/dist/gs/runtime/trace/index.js.map +1 -1
  88. package/dist/gs/slices/slices.d.ts +1 -1
  89. package/dist/gs/slices/slices.js +37 -4
  90. package/dist/gs/slices/slices.js.map +1 -1
  91. package/go.mod +2 -2
  92. package/go.sum +2 -0
  93. package/gs/builtin/builtin.ts +11 -14
  94. package/gs/builtin/defer.ts +2 -2
  95. package/gs/builtin/hostio.test.ts +8 -3
  96. package/gs/builtin/hostio.ts +5 -7
  97. package/gs/builtin/map.ts +4 -1
  98. package/gs/builtin/slice.test.ts +14 -0
  99. package/gs/builtin/slice.ts +64 -0
  100. package/gs/builtin/type.ts +72 -0
  101. package/gs/bytes/bytes.test.ts +14 -13
  102. package/gs/compress/zlib/index.test.ts +19 -5
  103. package/gs/compress/zlib/index.ts +16 -7
  104. package/gs/context/context.test.ts +3 -1
  105. package/gs/crypto/aes/index.test.ts +120 -0
  106. package/gs/crypto/aes/index.ts +76 -0
  107. package/gs/crypto/cipher/index.ts +345 -0
  108. package/gs/crypto/cipher/meta.json +6 -0
  109. package/gs/crypto/ecdh/index.test.ts +6 -2
  110. package/gs/crypto/ecdh/index.ts +49 -12
  111. package/gs/crypto/ed25519/index.ts +20 -7
  112. package/gs/crypto/rand/index.ts +6 -3
  113. package/gs/embed/index.test.ts +3 -3
  114. package/gs/embed/index.ts +9 -3
  115. package/gs/fmt/fmt.test.ts +29 -4
  116. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  117. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  118. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  119. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  120. package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
  121. package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
  122. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  123. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  124. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  125. package/gs/hash/fnv/index.test.ts +1 -8
  126. package/gs/hash/fnv/index.ts +27 -10
  127. package/gs/io/fs/glob.ts +13 -10
  128. package/gs/io/fs/meta.json +2 -0
  129. package/gs/io/fs/readdir.test.ts +63 -2
  130. package/gs/io/fs/readdir.ts +33 -30
  131. package/gs/io/fs/sub.ts +4 -4
  132. package/gs/io/fs/walk.ts +1 -1
  133. package/gs/io/io.test.ts +56 -1
  134. package/gs/io/io.ts +19 -2
  135. package/gs/maps/iter.ts +9 -9
  136. package/gs/maps/maps.ts +4 -4
  137. package/gs/math/bits/index.test.ts +10 -1
  138. package/gs/mime/index.test.ts +33 -15
  139. package/gs/mime/index.ts +9 -2
  140. package/gs/net/http/httptest/index.test.ts +17 -3
  141. package/gs/net/http/httptest/index.ts +8 -3
  142. package/gs/net/http/index.test.ts +645 -123
  143. package/gs/net/http/index.ts +548 -113
  144. package/gs/net/http/pprof/index.ts +24 -6
  145. package/gs/os/file_unix_js.test.ts +22 -0
  146. package/gs/reflect/iter.ts +4 -2
  147. package/gs/reflect/map.test.ts +56 -1
  148. package/gs/reflect/type.ts +76 -37
  149. package/gs/runtime/debug/index.test.ts +32 -4
  150. package/gs/runtime/debug/index.ts +5 -2
  151. package/gs/runtime/pprof/index.test.ts +7 -1
  152. package/gs/runtime/pprof/index.ts +5 -1
  153. package/gs/runtime/runtime.test.ts +7 -0
  154. package/gs/runtime/runtime.ts +2 -4
  155. package/gs/runtime/trace/index.test.ts +9 -1
  156. package/gs/runtime/trace/index.ts +5 -1
  157. package/gs/slices/meta.json +3 -0
  158. package/gs/slices/slices.test.ts +59 -21
  159. package/gs/slices/slices.ts +61 -20
  160. package/gs/strconv/complex.test.ts +17 -3
  161. package/gs/sync/atomic/doc_64.test.ts +2 -9
  162. package/gs/sync/sync.test.ts +18 -8
  163. package/gs/syscall/js/index.test.ts +9 -4
  164. package/package.json +13 -5
@@ -119,22 +119,36 @@ describe('compress/zlib override', () => {
119
119
  test('writer and reader honor preset dictionaries', async () => {
120
120
  const dict = $.stringToBytes('hello dictionary')
121
121
  const compressed = $.markAsStructValue(new bytes.Buffer())
122
- const [writer, writerErr] = NewWriterLevelDict(compressed, DefaultCompression, dict)
122
+ const [writer, writerErr] = NewWriterLevelDict(
123
+ compressed,
124
+ DefaultCompression,
125
+ dict,
126
+ )
123
127
  expect(writerErr).toBeNull()
124
- expect(writer!.Write($.stringToBytes('hello dictionary payload'))[1]).toBeNull()
128
+ expect(
129
+ writer!.Write($.stringToBytes('hello dictionary payload'))[1],
130
+ ).toBeNull()
125
131
  expect(await writer!.Close()).toBeNull()
126
132
 
127
- const [missingDictReader, missingDictErr] = NewReader(bytes.NewReader(compressed.Bytes()))
133
+ const [missingDictReader, missingDictErr] = NewReader(
134
+ bytes.NewReader(compressed.Bytes()),
135
+ )
128
136
  expect(missingDictReader).toBeNull()
129
137
  expect(missingDictErr).toBe(ErrDictionary)
130
138
 
131
- const [reader, readerErr] = NewReaderDict(bytes.NewReader(compressed.Bytes()), dict)
139
+ const [reader, readerErr] = NewReaderDict(
140
+ bytes.NewReader(compressed.Bytes()),
141
+ dict,
142
+ )
132
143
  expect(readerErr).toBeNull()
133
144
  const [out, readErr] = await io.ReadAll(reader!)
134
145
  expect(readErr).toBeNull()
135
146
  expect($.bytesToString(out)).toBe('hello dictionary payload')
136
147
 
137
- const [, wrongDictErr] = NewReaderDict(bytes.NewReader(compressed.Bytes()), $.stringToBytes('wrong dictionary'))
148
+ const [, wrongDictErr] = NewReaderDict(
149
+ bytes.NewReader(compressed.Bytes()),
150
+ $.stringToBytes('wrong dictionary'),
151
+ )
138
152
  expect(wrongDictErr).toBe(ErrDictionary)
139
153
 
140
154
  const corrupt = Uint8Array.from(compressed.Bytes())
@@ -37,9 +37,8 @@ export function __goscript_set_ErrHeader(value: $.GoError): void {
37
37
  class zlibReader implements Resetter {
38
38
  private data: Uint8Array = new Uint8Array(0)
39
39
  private offset = 0
40
- private pending:
41
- | Promise<{ data: Uint8Array | null; err: $.GoError }>
42
- | null = null
40
+ private pending: Promise<{ data: Uint8Array | null; err: $.GoError }> | null =
41
+ null
43
42
 
44
43
  constructor(data?: Uint8Array) {
45
44
  if (data != null) {
@@ -180,7 +179,11 @@ export function NewReaderDict(
180
179
  return [reader as any, null]
181
180
  }
182
181
 
183
- function deflate(data: Uint8Array, dict: $.Bytes | null, level: number): Uint8Array {
182
+ function deflate(
183
+ data: Uint8Array,
184
+ dict: $.Bytes | null,
185
+ level: number,
186
+ ): Uint8Array {
184
187
  const zlib = nodeZlib()
185
188
  const opts: Record<string, unknown> = {}
186
189
  if (dict != null && $.len(dict) > 0) {
@@ -197,7 +200,10 @@ function deflate(data: Uint8Array, dict: $.Bytes | null, level: number): Uint8Ar
197
200
 
198
201
  function inflate(data: Uint8Array, dict: $.Bytes | null): Uint8Array {
199
202
  const zlib = nodeZlib()
200
- const opts = dict != null && $.len(dict) > 0 ? { dictionary: $.bytesToUint8Array(dict) } : undefined
203
+ const opts =
204
+ dict != null && $.len(dict) > 0 ?
205
+ { dictionary: $.bytesToUint8Array(dict) }
206
+ : undefined
201
207
  return new Uint8Array(zlib.inflateSync(data, opts))
202
208
  }
203
209
 
@@ -302,11 +308,14 @@ function recordCompressedBytes(
302
308
  }
303
309
 
304
310
  function classifyInflateError(err: unknown): $.GoError {
305
- const zerr = err instanceof Error ? err as nodeZlibError : null
311
+ const zerr = err instanceof Error ? (err as nodeZlibError) : null
306
312
  if (zerr?.code === 'Z_NEED_DICT') {
307
313
  return ErrDictionary
308
314
  }
309
- if (zerr?.code === 'Z_DATA_ERROR' && zerr.message.includes('incorrect data check')) {
315
+ if (
316
+ zerr?.code === 'Z_DATA_ERROR' &&
317
+ zerr.message.includes('incorrect data check')
318
+ ) {
310
319
  return ErrChecksum
311
320
  }
312
321
  return ErrHeader
@@ -29,7 +29,9 @@ describe('context override', () => {
29
29
  )
30
30
 
31
31
  expect(
32
- ctx.Value($.interfaceValue($.markAsStructValue(new Key()), 'example.key')),
32
+ ctx.Value(
33
+ $.interfaceValue($.markAsStructValue(new Key()), 'example.key'),
34
+ ),
33
35
  ).toBe('stored')
34
36
  })
35
37
 
@@ -0,0 +1,120 @@
1
+ import { describe, expect, test } from 'vitest'
2
+
3
+ import * as $ from '@goscript/builtin/index.js'
4
+ import * as cipher from '@goscript/crypto/cipher/index.js'
5
+
6
+ import { BlockSize, KeySizeError_Error, NewCipher } from './index.js'
7
+
8
+ describe('crypto/aes WebCrypto override', () => {
9
+ test.each([
10
+ [
11
+ 'AES-128',
12
+ '000102030405060708090a0b0c0d0e0f',
13
+ '00112233445566778899aabbccddeeff',
14
+ '69c4e0d86a7b0430d8cdb78070b4c55a',
15
+ ],
16
+ [
17
+ 'AES-192',
18
+ '000102030405060708090a0b0c0d0e0f1011121314151617',
19
+ '00112233445566778899aabbccddeeff',
20
+ 'dda97ca4864cdfe06eaf70a0ec0d7191',
21
+ ],
22
+ [
23
+ 'AES-256',
24
+ '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
25
+ '00112233445566778899aabbccddeeff',
26
+ '8ea2b7ca516745bfeafc49904b496089',
27
+ ],
28
+ ])(
29
+ 'encrypts and decrypts a raw %s block',
30
+ (_name, key, plaintext, ciphertext) => {
31
+ const [block, err] = NewCipher(hex(key))
32
+ expect(err).toBeNull()
33
+
34
+ const encrypted = new Uint8Array(BlockSize)
35
+ block!.Encrypt(encrypted, hex(plaintext))
36
+ expect(toHex(encrypted)).toBe(ciphertext)
37
+
38
+ const decrypted = new Uint8Array(BlockSize)
39
+ block!.Decrypt(decrypted, encrypted)
40
+ expect(toHex(decrypted)).toBe(plaintext)
41
+ },
42
+ )
43
+
44
+ test('seals and opens the NIST AES-256-GCM vector', async () => {
45
+ const [block, blockErr] = NewCipher(
46
+ hex('0000000000000000000000000000000000000000000000000000000000000000'),
47
+ )
48
+ expect(blockErr).toBeNull()
49
+ expect(block?.BlockSize()).toBe(BlockSize)
50
+
51
+ const [aead, aeadErr] = cipher.NewGCM(block)
52
+ expect(aeadErr).toBeNull()
53
+ expect(aead?.NonceSize()).toBe(12)
54
+ expect(aead?.Overhead()).toBe(16)
55
+
56
+ const sealed = await aead!.Seal(
57
+ null,
58
+ hex('000000000000000000000000'),
59
+ hex('00000000000000000000000000000000'),
60
+ null,
61
+ )
62
+ expect(toHex(sealed)).toBe(
63
+ 'cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919',
64
+ )
65
+
66
+ const [opened, openErr] = await aead!.Open(
67
+ $.stringToBytes('prefix:'),
68
+ hex('000000000000000000000000'),
69
+ sealed,
70
+ null,
71
+ )
72
+ expect(openErr).toBeNull()
73
+ expect(toHex(opened)).toBe(
74
+ toHex($.stringToBytes('prefix:')) + '00000000000000000000000000000000',
75
+ )
76
+ })
77
+
78
+ test('rejects tampered ciphertext', async () => {
79
+ const [block] = NewCipher(
80
+ hex('0000000000000000000000000000000000000000000000000000000000000000'),
81
+ )
82
+ const [aead] = cipher.NewGCM(block)
83
+ const sealed = await aead!.Seal(
84
+ null,
85
+ hex('000000000000000000000000'),
86
+ $.stringToBytes('hello'),
87
+ $.stringToBytes('aad'),
88
+ )
89
+ sealed![0] ^= 1
90
+
91
+ const [opened, openErr] = await aead!.Open(
92
+ null,
93
+ hex('000000000000000000000000'),
94
+ sealed,
95
+ $.stringToBytes('aad'),
96
+ )
97
+ expect(opened).toBeNull()
98
+ expect(openErr?.Error()).toBe('cipher: message authentication failed')
99
+ })
100
+
101
+ test('rejects invalid key lengths', () => {
102
+ const [block, err] = NewCipher(new Uint8Array(31))
103
+ expect(block).toBeNull()
104
+ expect(err?.Error()).toBe(KeySizeError_Error(31))
105
+ })
106
+ })
107
+
108
+ function hex(input: string): Uint8Array {
109
+ const out = new Uint8Array(input.length / 2)
110
+ for (let idx = 0; idx < out.length; idx++) {
111
+ out[idx] = Number.parseInt(input.slice(idx * 2, idx * 2 + 2), 16)
112
+ }
113
+ return out
114
+ }
115
+
116
+ function toHex(input: Uint8Array | number[] | null): string {
117
+ return Array.from(input ?? [])
118
+ .map((value) => value.toString(16).padStart(2, '0'))
119
+ .join('')
120
+ }
@@ -0,0 +1,76 @@
1
+ import * as $ from '@goscript/builtin/index.js'
2
+ import type * as cipher from '@goscript/crypto/cipher/index.js'
3
+ import { ecb } from '@noble/ciphers/aes.js'
4
+
5
+ export const BlockSize = 16
6
+
7
+ export type KeySizeError = number
8
+
9
+ export class AESBlock implements cipher.Block {
10
+ private keyPromise: Promise<CryptoKey> | null = null
11
+
12
+ constructor(private readonly key: Uint8Array) {}
13
+
14
+ BlockSize(): number {
15
+ return BlockSize
16
+ }
17
+
18
+ Encrypt(dst: $.Bytes, src: $.Bytes): void {
19
+ const srcBytes = $.bytesToUint8Array(src)
20
+ if (srcBytes.length < BlockSize) {
21
+ $.panic('crypto/aes: input not full block')
22
+ }
23
+ if ($.len(dst) < BlockSize) {
24
+ $.panic('crypto/aes: output not full block')
25
+ }
26
+ const out = ecb(this.key, { disablePadding: true }).encrypt(
27
+ srcBytes.subarray(0, BlockSize),
28
+ )
29
+ $.copy(dst as Uint8Array, out)
30
+ }
31
+
32
+ Decrypt(dst: $.Bytes, src: $.Bytes): void {
33
+ const srcBytes = $.bytesToUint8Array(src)
34
+ if (srcBytes.length < BlockSize) {
35
+ $.panic('crypto/aes: input not full block')
36
+ }
37
+ if ($.len(dst) < BlockSize) {
38
+ $.panic('crypto/aes: output not full block')
39
+ }
40
+ const out = ecb(this.key, { disablePadding: true }).decrypt(
41
+ srcBytes.subarray(0, BlockSize),
42
+ )
43
+ $.copy(dst as Uint8Array, out)
44
+ }
45
+
46
+ async webCryptoKey(): Promise<CryptoKey> {
47
+ this.keyPromise ??= subtleCrypto().importKey(
48
+ 'raw',
49
+ this.key as unknown as BufferSource,
50
+ 'AES-GCM',
51
+ false,
52
+ ['encrypt', 'decrypt'],
53
+ )
54
+ return this.keyPromise
55
+ }
56
+ }
57
+
58
+ export function KeySizeError_Error(k: KeySizeError): string {
59
+ return `crypto/aes: invalid key size ${k}`
60
+ }
61
+
62
+ export function NewCipher(key: $.Bytes): [cipher.Block | null, $.GoError] {
63
+ const k = $.len(key)
64
+ if (k !== 16 && k !== 24 && k !== 32) {
65
+ return [null, $.newError(KeySizeError_Error(k))]
66
+ }
67
+ return [new AESBlock($.bytesToUint8Array(key).slice()), null]
68
+ }
69
+
70
+ function subtleCrypto(): SubtleCrypto {
71
+ const subtle = globalThis.crypto?.subtle
72
+ if (subtle == null) {
73
+ throw new Error('crypto/aes: WebCrypto AES-GCM is unavailable')
74
+ }
75
+ return subtle
76
+ }
@@ -0,0 +1,345 @@
1
+ import * as $ from '@goscript/builtin/index.js'
2
+
3
+ export type Block = {
4
+ BlockSize(): number
5
+ Decrypt(dst: $.Bytes, src: $.Bytes): void
6
+ Encrypt(dst: $.Bytes, src: $.Bytes): void
7
+ }
8
+
9
+ $.registerInterfaceType('cipher.Block', null, [
10
+ {
11
+ name: 'BlockSize',
12
+ args: [],
13
+ returns: [{ name: '_r0', type: { kind: $.TypeKind.Basic, name: 'int' } }],
14
+ },
15
+ {
16
+ name: 'Decrypt',
17
+ args: [
18
+ {
19
+ name: 'dst',
20
+ type: {
21
+ kind: $.TypeKind.Slice,
22
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
23
+ },
24
+ },
25
+ {
26
+ name: 'src',
27
+ type: {
28
+ kind: $.TypeKind.Slice,
29
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
30
+ },
31
+ },
32
+ ],
33
+ returns: [],
34
+ },
35
+ {
36
+ name: 'Encrypt',
37
+ args: [
38
+ {
39
+ name: 'dst',
40
+ type: {
41
+ kind: $.TypeKind.Slice,
42
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
43
+ },
44
+ },
45
+ {
46
+ name: 'src',
47
+ type: {
48
+ kind: $.TypeKind.Slice,
49
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
50
+ },
51
+ },
52
+ ],
53
+ returns: [],
54
+ },
55
+ ])
56
+
57
+ export type AEAD = {
58
+ NonceSize(): number
59
+ Open(
60
+ dst: $.Bytes,
61
+ nonce: $.Bytes,
62
+ ciphertext: $.Bytes,
63
+ additionalData: $.Bytes,
64
+ ): [$.Bytes, $.GoError] | Promise<[$.Bytes, $.GoError]>
65
+ Overhead(): number
66
+ Seal(
67
+ dst: $.Bytes,
68
+ nonce: $.Bytes,
69
+ plaintext: $.Bytes,
70
+ additionalData: $.Bytes,
71
+ ): $.Bytes | Promise<$.Bytes>
72
+ }
73
+
74
+ $.registerInterfaceType('cipher.AEAD', null, [
75
+ {
76
+ name: 'NonceSize',
77
+ args: [],
78
+ returns: [{ name: '_r0', type: { kind: $.TypeKind.Basic, name: 'int' } }],
79
+ },
80
+ {
81
+ name: 'Open',
82
+ args: [
83
+ {
84
+ name: 'dst',
85
+ type: {
86
+ kind: $.TypeKind.Slice,
87
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
88
+ },
89
+ },
90
+ {
91
+ name: 'nonce',
92
+ type: {
93
+ kind: $.TypeKind.Slice,
94
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
95
+ },
96
+ },
97
+ {
98
+ name: 'ciphertext',
99
+ type: {
100
+ kind: $.TypeKind.Slice,
101
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
102
+ },
103
+ },
104
+ {
105
+ name: 'additionalData',
106
+ type: {
107
+ kind: $.TypeKind.Slice,
108
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
109
+ },
110
+ },
111
+ ],
112
+ returns: [
113
+ {
114
+ name: '_r0',
115
+ type: {
116
+ kind: $.TypeKind.Slice,
117
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
118
+ },
119
+ },
120
+ { name: '_r1', type: 'error' },
121
+ ],
122
+ },
123
+ {
124
+ name: 'Overhead',
125
+ args: [],
126
+ returns: [{ name: '_r0', type: { kind: $.TypeKind.Basic, name: 'int' } }],
127
+ },
128
+ {
129
+ name: 'Seal',
130
+ args: [
131
+ {
132
+ name: 'dst',
133
+ type: {
134
+ kind: $.TypeKind.Slice,
135
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
136
+ },
137
+ },
138
+ {
139
+ name: 'nonce',
140
+ type: {
141
+ kind: $.TypeKind.Slice,
142
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
143
+ },
144
+ },
145
+ {
146
+ name: 'plaintext',
147
+ type: {
148
+ kind: $.TypeKind.Slice,
149
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
150
+ },
151
+ },
152
+ {
153
+ name: 'additionalData',
154
+ type: {
155
+ kind: $.TypeKind.Slice,
156
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
157
+ },
158
+ },
159
+ ],
160
+ returns: [
161
+ {
162
+ name: '_r0',
163
+ type: {
164
+ kind: $.TypeKind.Slice,
165
+ elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
166
+ },
167
+ },
168
+ ],
169
+ },
170
+ ])
171
+
172
+ export type Stream = {
173
+ XORKeyStream(dst: $.Bytes, src: $.Bytes): void
174
+ }
175
+
176
+ export type BlockMode = {
177
+ BlockSize(): number
178
+ CryptBlocks(dst: $.Bytes, src: $.Bytes): void
179
+ }
180
+
181
+ type WebCryptoBlock = Block & {
182
+ webCryptoKey(): Promise<CryptoKey>
183
+ }
184
+
185
+ class webCryptoGCM implements AEAD {
186
+ constructor(
187
+ private readonly block: WebCryptoBlock,
188
+ private readonly nonceSize: number,
189
+ private readonly tagSize: number,
190
+ ) {}
191
+
192
+ NonceSize(): number {
193
+ return this.nonceSize
194
+ }
195
+
196
+ Overhead(): number {
197
+ return this.tagSize
198
+ }
199
+
200
+ async Seal(
201
+ dst: $.Bytes,
202
+ nonce: $.Bytes,
203
+ plaintext: $.Bytes,
204
+ additionalData: $.Bytes,
205
+ ): Promise<$.Bytes> {
206
+ if ($.len(nonce) !== this.nonceSize) {
207
+ throw new Error('crypto/cipher: incorrect nonce length given to GCM')
208
+ }
209
+ const encrypted = await globalThis.crypto.subtle.encrypt(
210
+ this.algorithm(nonce, additionalData),
211
+ await this.block.webCryptoKey(),
212
+ $.bytesToUint8Array(plaintext) as unknown as BufferSource,
213
+ )
214
+ return appendBytes(dst, new Uint8Array(encrypted))
215
+ }
216
+
217
+ async Open(
218
+ dst: $.Bytes,
219
+ nonce: $.Bytes,
220
+ ciphertext: $.Bytes,
221
+ additionalData: $.Bytes,
222
+ ): Promise<[$.Bytes, $.GoError]> {
223
+ if ($.len(nonce) !== this.nonceSize) {
224
+ throw new Error('crypto/cipher: incorrect nonce length given to GCM')
225
+ }
226
+ try {
227
+ const decrypted = await globalThis.crypto.subtle.decrypt(
228
+ this.algorithm(nonce, additionalData),
229
+ await this.block.webCryptoKey(),
230
+ $.bytesToUint8Array(ciphertext) as unknown as BufferSource,
231
+ )
232
+ return [appendBytes(dst, new Uint8Array(decrypted)), null]
233
+ } catch {
234
+ return [null, $.newError('cipher: message authentication failed')]
235
+ }
236
+ }
237
+
238
+ private algorithm(nonce: $.Bytes, additionalData: $.Bytes): AesGcmParams {
239
+ const params: AesGcmParams = {
240
+ name: 'AES-GCM',
241
+ iv: $.bytesToUint8Array(nonce) as unknown as BufferSource,
242
+ tagLength: this.tagSize * 8,
243
+ }
244
+ if ($.len(additionalData) !== 0) {
245
+ params.additionalData = $.bytesToUint8Array(
246
+ additionalData,
247
+ ) as unknown as BufferSource
248
+ }
249
+ return params
250
+ }
251
+ }
252
+
253
+ export function NewGCM(block: Block | null): [AEAD | null, $.GoError] {
254
+ return NewGCMWithNonceSize(block, 12)
255
+ }
256
+
257
+ export function NewGCMWithNonceSize(
258
+ block: Block | null,
259
+ size: number,
260
+ ): [AEAD | null, $.GoError] {
261
+ return newGCM(block, size, 16)
262
+ }
263
+
264
+ export function NewGCMWithTagSize(
265
+ block: Block | null,
266
+ tagSize: number,
267
+ ): [AEAD | null, $.GoError] {
268
+ if (tagSize < 12 || tagSize > 16) {
269
+ return [null, $.newError('crypto/cipher: incorrect GCM tag size')]
270
+ }
271
+ return newGCM(block, 12, tagSize)
272
+ }
273
+
274
+ export function NewGCMWithRandomNonce(
275
+ _block: Block | null,
276
+ ): [AEAD | null, $.GoError] {
277
+ return [
278
+ null,
279
+ $.newError(
280
+ 'crypto/cipher: NewGCMWithRandomNonce is not implemented in GoScript',
281
+ ),
282
+ ]
283
+ }
284
+
285
+ export function NewCBCDecrypter(_b: Block | null, _iv: $.Bytes): BlockMode {
286
+ throw new Error('crypto/cipher: CBC is not implemented in GoScript')
287
+ }
288
+
289
+ export function NewCBCEncrypter(_b: Block | null, _iv: $.Bytes): BlockMode {
290
+ throw new Error('crypto/cipher: CBC is not implemented in GoScript')
291
+ }
292
+
293
+ export function NewCFBDecrypter(_b: Block | null, _iv: $.Bytes): Stream {
294
+ throw new Error('crypto/cipher: CFB is not implemented in GoScript')
295
+ }
296
+
297
+ export function NewCFBEncrypter(_b: Block | null, _iv: $.Bytes): Stream {
298
+ throw new Error('crypto/cipher: CFB is not implemented in GoScript')
299
+ }
300
+
301
+ export function NewCTR(_b: Block | null, _iv: $.Bytes): Stream {
302
+ throw new Error('crypto/cipher: CTR is not implemented in GoScript')
303
+ }
304
+
305
+ export function NewOFB(_b: Block | null, _iv: $.Bytes): Stream {
306
+ throw new Error('crypto/cipher: OFB is not implemented in GoScript')
307
+ }
308
+
309
+ export class StreamReader {
310
+ constructor(_init?: Partial<{ S: Stream; R: unknown }>) {}
311
+ }
312
+
313
+ export class StreamWriter {
314
+ constructor(_init?: Partial<{ S: Stream; W: unknown }>) {}
315
+ }
316
+
317
+ function newGCM(
318
+ block: Block | null,
319
+ nonceSize: number,
320
+ tagSize: number,
321
+ ): [AEAD | null, $.GoError] {
322
+ if (block == null || block.BlockSize() !== 16) {
323
+ return [null, $.newError('cipher: NewGCM requires 128-bit block cipher')]
324
+ }
325
+ if (nonceSize <= 0) {
326
+ return [null, $.newError('crypto/cipher: incorrect GCM nonce size')]
327
+ }
328
+ if (!isWebCryptoBlock(block)) {
329
+ return [
330
+ null,
331
+ $.newError(
332
+ 'crypto/cipher: AES-GCM requires a WebCrypto AES block in GoScript',
333
+ ),
334
+ ]
335
+ }
336
+ return [new webCryptoGCM(block, nonceSize, tagSize), null]
337
+ }
338
+
339
+ function isWebCryptoBlock(block: Block): block is WebCryptoBlock {
340
+ return typeof (block as Partial<WebCryptoBlock>).webCryptoKey === 'function'
341
+ }
342
+
343
+ function appendBytes(dst: $.Bytes, bytes: Uint8Array): $.Bytes {
344
+ return $.append(dst as any, ...bytes) as $.Bytes
345
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "asyncMethods": {
3
+ "AEAD.Open": true,
4
+ "AEAD.Seal": true
5
+ }
6
+ }
@@ -35,9 +35,13 @@ describe('crypto/ecdh override', () => {
35
35
  })
36
36
 
37
37
  function hex(value: string): Uint8Array {
38
- return new Uint8Array(value.match(/../g)!.map((byte) => Number.parseInt(byte, 16)))
38
+ return new Uint8Array(
39
+ value.match(/../g)!.map((byte) => Number.parseInt(byte, 16)),
40
+ )
39
41
  }
40
42
 
41
43
  function toHex(value: Uint8Array | null): string {
42
- return Array.from(value ?? [], (byte) => byte.toString(16).padStart(2, '0')).join('')
44
+ return Array.from(value ?? [], (byte) =>
45
+ byte.toString(16).padStart(2, '0'),
46
+ ).join('')
43
47
  }