goscript 0.2.0 → 0.2.2
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/cmd/goscript-wasm/main.go +38 -6
- package/compiler/diagnostic.go +104 -12
- package/compiler/diagnostic_test.go +106 -0
- package/compiler/gotest/runner.go +99 -17
- package/compiler/gotest/runner_test.go +65 -0
- package/compiler/index.test.ts +23 -0
- package/compiler/lowered-program.go +9 -7
- package/compiler/lowering.go +361 -72
- package/compiler/lowering_bench_test.go +1 -0
- package/compiler/lowering_internal_test.go +18 -0
- package/compiler/protobuf-ts-binding.go +65 -12
- package/compiler/protobuf-ts-binding_test.go +339 -0
- package/compiler/runtime-contract.go +4 -0
- package/compiler/runtime-contract_test.go +2 -0
- package/compiler/service.go +1 -0
- package/compiler/skeleton_test.go +60 -3
- package/compiler/wasm/compile_test.go +37 -4
- package/compiler/wasm-api.go +57 -7
- package/dist/gs/builtin/hostio.js +6 -1
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +11 -1
- package/dist/gs/builtin/slice.js +158 -2
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/crypto/aes/index.d.ts +15 -0
- package/dist/gs/crypto/aes/index.js +57 -0
- package/dist/gs/crypto/aes/index.js.map +1 -0
- package/dist/gs/crypto/cipher/index.d.ts +41 -0
- package/dist/gs/crypto/cipher/index.js +255 -0
- package/dist/gs/crypto/cipher/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +30 -5
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/io/io.js +18 -2
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/reflect/type.js +57 -0
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/runtime/debug/index.js +2 -1
- package/dist/gs/runtime/debug/index.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/go.mod +2 -2
- package/go.sum +2 -0
- package/gs/builtin/hostio.test.ts +22 -1
- package/gs/builtin/hostio.ts +6 -1
- package/gs/builtin/runtime-contract.test.ts +28 -0
- package/gs/builtin/slice.ts +225 -20
- package/gs/crypto/aes/index.test.ts +120 -0
- package/gs/crypto/aes/index.ts +76 -0
- package/gs/crypto/cipher/index.ts +345 -0
- package/gs/crypto/cipher/meta.json +6 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +162 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +41 -5
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
- package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
- package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
- package/gs/internal/byteorder/index.test.ts +2 -2
- package/gs/internal/byteorder/index.ts +2 -2
- package/gs/io/io.test.ts +56 -1
- package/gs/io/io.ts +19 -2
- package/gs/reflect/type.ts +64 -0
- package/gs/reflect/typefor.test.ts +21 -1
- package/gs/runtime/debug/index.test.ts +32 -4
- package/gs/runtime/debug/index.ts +5 -2
- package/gs/sync/atomic/doc_64.gs.ts +6 -7
- package/gs/sync/atomic/doc_64.test.ts +43 -0
- package/package.json +10 -3
|
@@ -345,6 +345,7 @@ export function EqualVTMapImplicit<K, V extends EqualVT<V>>(
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
type BoundMessageType = {
|
|
348
|
+
typeName?: string
|
|
348
349
|
fields?: { list(): readonly BoundFieldInfo[] }
|
|
349
350
|
clone?: (value: any) => any
|
|
350
351
|
equals?: (a: any, b: any) => boolean
|
|
@@ -371,6 +372,8 @@ type BoundMessageCtor<T = any> = {
|
|
|
371
372
|
__protobufTypeScriptMessage?: BoundMessageType
|
|
372
373
|
}
|
|
373
374
|
|
|
375
|
+
const protobufScalarTypeBytes = 12
|
|
376
|
+
|
|
374
377
|
function boundMessageType(ctor: BoundMessageCtor): BoundMessageType {
|
|
375
378
|
const messageType = ctor.__protobufTypeScriptMessage
|
|
376
379
|
if (messageType == null) {
|
|
@@ -401,8 +404,16 @@ function boundFieldValue(source: any, field: BoundFieldInfo): any {
|
|
|
401
404
|
return source[boundFieldGoName(field)]
|
|
402
405
|
}
|
|
403
406
|
|
|
404
|
-
function toTypeScriptScalarValue(value: any): any {
|
|
407
|
+
function toTypeScriptScalarValue(value: any, field?: BoundFieldInfo): any {
|
|
405
408
|
const unwrapped = $.pointerValueOrNil(value)
|
|
409
|
+
if (
|
|
410
|
+
field?.kind === 'scalar' &&
|
|
411
|
+
field.T === protobufScalarTypeBytes &&
|
|
412
|
+
unwrapped != null &&
|
|
413
|
+
!(unwrapped instanceof Uint8Array)
|
|
414
|
+
) {
|
|
415
|
+
return Uint8Array.from($.asArray(unwrapped))
|
|
416
|
+
}
|
|
406
417
|
if (unwrapped instanceof Uint8Array) {
|
|
407
418
|
return unwrapped
|
|
408
419
|
}
|
|
@@ -440,7 +451,7 @@ function toTypeScriptFieldValue(
|
|
|
440
451
|
const messageType = resolveBoundMessageType(field.T)
|
|
441
452
|
return toTypeScriptMessage(value, messageType, ctor)
|
|
442
453
|
}
|
|
443
|
-
return toTypeScriptScalarValue(value)
|
|
454
|
+
return toTypeScriptScalarValue(value, field)
|
|
444
455
|
}
|
|
445
456
|
|
|
446
457
|
function toTypeScriptMessage(
|
|
@@ -520,6 +531,23 @@ function fromTypeScriptFieldValue(
|
|
|
520
531
|
return fromTypeScriptScalarValue(value)
|
|
521
532
|
}
|
|
522
533
|
|
|
534
|
+
function fromTypeScriptWellKnownMessage(
|
|
535
|
+
value: any,
|
|
536
|
+
ctor: BoundMessageCtor,
|
|
537
|
+
messageType?: BoundMessageType,
|
|
538
|
+
): any {
|
|
539
|
+
if (
|
|
540
|
+
messageType?.typeName === 'google.protobuf.Timestamp' &&
|
|
541
|
+
value instanceof Date
|
|
542
|
+
) {
|
|
543
|
+
const ms = value.getTime()
|
|
544
|
+
const seconds = Math.floor(ms / 1000)
|
|
545
|
+
const nanos = Math.trunc((ms - seconds * 1000) * 1000000)
|
|
546
|
+
return new ctor({ Seconds: seconds, Nanos: nanos })
|
|
547
|
+
}
|
|
548
|
+
return undefined
|
|
549
|
+
}
|
|
550
|
+
|
|
523
551
|
function fromTypeScriptMessage(
|
|
524
552
|
value: any,
|
|
525
553
|
ctor: BoundMessageCtor,
|
|
@@ -528,6 +556,10 @@ function fromTypeScriptMessage(
|
|
|
528
556
|
if (value == null) {
|
|
529
557
|
return null
|
|
530
558
|
}
|
|
559
|
+
const wellKnown = fromTypeScriptWellKnownMessage(value, ctor, messageType)
|
|
560
|
+
if (wellKnown !== undefined) {
|
|
561
|
+
return wellKnown
|
|
562
|
+
}
|
|
531
563
|
const out = new ctor()
|
|
532
564
|
const fields = (messageType ?? boundMessageType(ctor)).fields?.list()
|
|
533
565
|
if (fields == null) {
|
|
@@ -579,14 +611,17 @@ export function MarshalBoundMessageVT<T>(
|
|
|
579
611
|
ctor: BoundMessageCtor<T>,
|
|
580
612
|
value: T | $.VarRef<T> | null,
|
|
581
613
|
): [$.Slice<number>, $.GoError] {
|
|
614
|
+
let typeName = ctor.name
|
|
582
615
|
try {
|
|
583
616
|
const messageType = boundMessageType(ctor)
|
|
617
|
+
typeName = messageType.typeName ?? typeName
|
|
584
618
|
return [
|
|
585
619
|
messageType.toBinary(toTypeScriptMessage(value, messageType, ctor)),
|
|
586
620
|
null,
|
|
587
621
|
]
|
|
588
622
|
} catch (err) {
|
|
589
|
-
|
|
623
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
624
|
+
return [null, $.toGoError(new Error(`marshal ${typeName}: ${msg}`))]
|
|
590
625
|
}
|
|
591
626
|
}
|
|
592
627
|
|
|
@@ -599,12 +634,13 @@ export function MarshalBoundMessageToSizedBufferVT<T>(
|
|
|
599
634
|
if (err != null) {
|
|
600
635
|
return [0, err]
|
|
601
636
|
}
|
|
602
|
-
const
|
|
637
|
+
const byteCount = $.len(bytes)
|
|
638
|
+
const offset = $.len(dAtA) - byteCount
|
|
603
639
|
const src = $.asArray(bytes)
|
|
604
640
|
for (let i = 0; i < src.length; i++) {
|
|
605
641
|
;(dAtA as any)[offset + i] = src[i]
|
|
606
642
|
}
|
|
607
|
-
return [
|
|
643
|
+
return [byteCount, null]
|
|
608
644
|
}
|
|
609
645
|
|
|
610
646
|
export function SizeBoundMessageVT<T>(
|
|
@@ -113,6 +113,24 @@ describe('protobuf-go-lite/json override', () => {
|
|
|
113
113
|
])
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
+
it('matches jsoniter value types while reading object fields', () => {
|
|
117
|
+
const state = NewUnmarshalState(
|
|
118
|
+
jsonBytes('{"id":"step","config":{"kind":"inline"}}'),
|
|
119
|
+
DefaultUnmarshalerConfig,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
expect(state?.ReadObjectField()).toBe('id')
|
|
123
|
+
expect(state?.WhatIsNext()).toBe(1)
|
|
124
|
+
expect(state?.ReadString()).toBe('step')
|
|
125
|
+
expect(state?.ReadObjectField()).toBe('config')
|
|
126
|
+
expect(state?.WhatIsNext()).toBe(6)
|
|
127
|
+
expect(
|
|
128
|
+
new TextDecoder().decode(new Uint8Array(state?.SkipAndReturnBytes())),
|
|
129
|
+
).toBe('{"kind":"inline"}')
|
|
130
|
+
expect(state?.ReadObjectField()).toBe('')
|
|
131
|
+
expect(state?.Err()).toBeNull()
|
|
132
|
+
})
|
|
133
|
+
|
|
116
134
|
it('tracks unmarshaled field masks through nested fields', () => {
|
|
117
135
|
const state = NewUnmarshalState(jsonBytes('{}'), DefaultUnmarshalerConfig)
|
|
118
136
|
state?.AddField('top')
|
|
@@ -423,6 +423,7 @@ export class UnmarshalState {
|
|
|
423
423
|
private path: string[]
|
|
424
424
|
private objectEntries: Array<[string, unknown]> | null = null
|
|
425
425
|
private objectIndex = 0
|
|
426
|
+
private objectValue: unknown = undefined
|
|
426
427
|
|
|
427
428
|
constructor(
|
|
428
429
|
init?: Partial<{
|
|
@@ -612,16 +613,21 @@ export class UnmarshalState {
|
|
|
612
613
|
}
|
|
613
614
|
|
|
614
615
|
public ReadObjectField(): string {
|
|
615
|
-
const record = recordValue(this.value)
|
|
616
|
-
if (record == null) {
|
|
617
|
-
this.SetErrorf('expected JSON object')
|
|
618
|
-
return ''
|
|
619
|
-
}
|
|
620
616
|
if (this.objectEntries == null) {
|
|
617
|
+
const record = recordValue(this.value)
|
|
618
|
+
if (record == null) {
|
|
619
|
+
this.SetErrorf('expected JSON object')
|
|
620
|
+
return ''
|
|
621
|
+
}
|
|
621
622
|
this.objectEntries = Object.entries(record)
|
|
622
623
|
this.objectIndex = 0
|
|
624
|
+
this.objectValue = this.value
|
|
623
625
|
}
|
|
624
626
|
if (this.objectIndex >= this.objectEntries.length) {
|
|
627
|
+
this.value = this.objectValue
|
|
628
|
+
this.objectEntries = null
|
|
629
|
+
this.objectIndex = 0
|
|
630
|
+
this.objectValue = undefined
|
|
625
631
|
return ''
|
|
626
632
|
}
|
|
627
633
|
const [key, value] = this.objectEntries[this.objectIndex]
|
|
@@ -747,22 +753,22 @@ export class UnmarshalState {
|
|
|
747
753
|
|
|
748
754
|
public WhatIsNext(): number {
|
|
749
755
|
if (this.value === null) {
|
|
750
|
-
return
|
|
756
|
+
return 3
|
|
751
757
|
}
|
|
752
758
|
if (Array.isArray(this.value)) {
|
|
753
|
-
return
|
|
759
|
+
return 5
|
|
754
760
|
}
|
|
755
761
|
if (typeof this.value === 'object') {
|
|
756
|
-
return
|
|
762
|
+
return 6
|
|
757
763
|
}
|
|
758
764
|
if (typeof this.value === 'string') {
|
|
759
|
-
return
|
|
765
|
+
return 1
|
|
760
766
|
}
|
|
761
767
|
if (typeof this.value === 'number') {
|
|
762
|
-
return
|
|
768
|
+
return 2
|
|
763
769
|
}
|
|
764
770
|
if (typeof this.value === 'boolean') {
|
|
765
|
-
return
|
|
771
|
+
return 4
|
|
766
772
|
}
|
|
767
773
|
return 0
|
|
768
774
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { New, NewX, NonceSize, NonceSizeX, Overhead } from './index.js'
|
|
4
|
+
|
|
5
|
+
describe('chacha20poly1305 override', () => {
|
|
6
|
+
test('matches the RFC 8439 ChaCha20-Poly1305 AEAD vector', () => {
|
|
7
|
+
const [aead, err] = New(
|
|
8
|
+
hex('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'),
|
|
9
|
+
)
|
|
10
|
+
expect(err).toBeNull()
|
|
11
|
+
expect(aead?.NonceSize()).toBe(NonceSize)
|
|
12
|
+
expect(aead?.Overhead()).toBe(Overhead)
|
|
13
|
+
|
|
14
|
+
const nonce = hex('070000004041424344454647')
|
|
15
|
+
const aad = hex('50515253c0c1c2c3c4c5c6c7')
|
|
16
|
+
const plaintext = hex(
|
|
17
|
+
'4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e',
|
|
18
|
+
)
|
|
19
|
+
const sealed = aead!.Seal(null, nonce, plaintext, aad)
|
|
20
|
+
expect(toHex(sealed)).toBe(
|
|
21
|
+
'd31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691',
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const [opened, openErr] = aead!.Open(null, nonce, sealed, aad)
|
|
25
|
+
expect(openErr).toBeNull()
|
|
26
|
+
expect(toHex(opened)).toBe(toHex(plaintext))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('rejects tampered ciphertexts', () => {
|
|
30
|
+
const [aead, err] = New(
|
|
31
|
+
hex('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'),
|
|
32
|
+
)
|
|
33
|
+
expect(err).toBeNull()
|
|
34
|
+
const sealed = aead!.Seal(
|
|
35
|
+
null,
|
|
36
|
+
hex('000000000000000000000002'),
|
|
37
|
+
hex('68656c6c6f'),
|
|
38
|
+
null,
|
|
39
|
+
)
|
|
40
|
+
sealed![0] ^= 1
|
|
41
|
+
|
|
42
|
+
const [opened, openErr] = aead!.Open(
|
|
43
|
+
null,
|
|
44
|
+
hex('000000000000000000000002'),
|
|
45
|
+
sealed,
|
|
46
|
+
null,
|
|
47
|
+
)
|
|
48
|
+
expect(opened).toBeNull()
|
|
49
|
+
expect(openErr?.Error()).toBe(
|
|
50
|
+
'chacha20poly1305: message authentication failed',
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('round trips XChaCha20-Poly1305', () => {
|
|
55
|
+
const [aead, err] = NewX(
|
|
56
|
+
hex('1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0'),
|
|
57
|
+
)
|
|
58
|
+
expect(err).toBeNull()
|
|
59
|
+
expect(aead?.NonceSize()).toBe(NonceSizeX)
|
|
60
|
+
expect(aead?.Overhead()).toBe(Overhead)
|
|
61
|
+
|
|
62
|
+
const nonce = hex('000000000102030405060708090a0b0c0d0e0f1011121314')
|
|
63
|
+
const aad = hex('f33388860000000000004e91')
|
|
64
|
+
const plaintext = hex('496e7465726f70657261626c6520584368614368613230')
|
|
65
|
+
const sealed = aead!.Seal(null, nonce, plaintext, aad)
|
|
66
|
+
|
|
67
|
+
const [opened, openErr] = aead!.Open(null, nonce, sealed, aad)
|
|
68
|
+
expect(openErr).toBeNull()
|
|
69
|
+
expect(toHex(opened)).toBe(toHex(plaintext))
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('rejects invalid key length', () => {
|
|
73
|
+
const [aead, err] = New(new Uint8Array(31))
|
|
74
|
+
expect(aead).toBeNull()
|
|
75
|
+
expect(err?.Error()).toBe('chacha20poly1305: bad key length')
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
function hex(input: string): Uint8Array {
|
|
80
|
+
const out = new Uint8Array(input.length / 2)
|
|
81
|
+
for (let idx = 0; idx < out.length; idx++) {
|
|
82
|
+
out[idx] = Number.parseInt(input.slice(idx * 2, idx * 2 + 2), 16)
|
|
83
|
+
}
|
|
84
|
+
return out
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function toHex(input: Uint8Array | number[] | null): string {
|
|
88
|
+
return Array.from(input ?? [])
|
|
89
|
+
.map((value) => value.toString(16).padStart(2, '0'))
|
|
90
|
+
.join('')
|
|
91
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
+
import * as errors from '@goscript/errors/index.js'
|
|
3
|
+
import {
|
|
4
|
+
chacha20poly1305 as nobleChaCha20Poly1305,
|
|
5
|
+
xchacha20poly1305 as nobleXChaCha20Poly1305,
|
|
6
|
+
} from '@noble/ciphers/chacha.js'
|
|
7
|
+
|
|
8
|
+
export const KeySize = 32
|
|
9
|
+
export const NonceSize = 12
|
|
10
|
+
export const NonceSizeX = 24
|
|
11
|
+
export const Overhead = 16
|
|
12
|
+
|
|
13
|
+
const maxIETFPlaintext = 2 ** 38 - 64
|
|
14
|
+
const maxIETFCiphertext = 2 ** 38 - 48
|
|
15
|
+
|
|
16
|
+
type NobleAEAD = typeof nobleChaCha20Poly1305 | typeof nobleXChaCha20Poly1305
|
|
17
|
+
|
|
18
|
+
export let errOpen: $.GoError = errors.New(
|
|
19
|
+
'chacha20poly1305: message authentication failed',
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export function __goscript_set_errOpen(value: $.GoError): void {
|
|
23
|
+
errOpen = value
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AEAD {
|
|
27
|
+
NonceSize(): number
|
|
28
|
+
Overhead(): number
|
|
29
|
+
Seal(
|
|
30
|
+
dst: $.Slice<number>,
|
|
31
|
+
nonce: $.Slice<number>,
|
|
32
|
+
plaintext: $.Slice<number>,
|
|
33
|
+
additionalData: $.Slice<number>,
|
|
34
|
+
): $.Slice<number>
|
|
35
|
+
Open(
|
|
36
|
+
dst: $.Slice<number>,
|
|
37
|
+
nonce: $.Slice<number>,
|
|
38
|
+
ciphertext: $.Slice<number>,
|
|
39
|
+
additionalData: $.Slice<number>,
|
|
40
|
+
): [$.Slice<number>, $.GoError]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class chacha20poly1305 implements AEAD {
|
|
44
|
+
readonly key: Uint8Array
|
|
45
|
+
|
|
46
|
+
constructor(key: Uint8Array) {
|
|
47
|
+
this.key = key.slice()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
NonceSize(): number {
|
|
51
|
+
return NonceSize
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Overhead(): number {
|
|
55
|
+
return Overhead
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Seal(
|
|
59
|
+
dst: $.Slice<number>,
|
|
60
|
+
nonce: $.Slice<number>,
|
|
61
|
+
plaintext: $.Slice<number>,
|
|
62
|
+
additionalData: $.Slice<number>,
|
|
63
|
+
): $.Slice<number> {
|
|
64
|
+
return seal(
|
|
65
|
+
dst,
|
|
66
|
+
nobleChaCha20Poly1305,
|
|
67
|
+
this.key,
|
|
68
|
+
nonce,
|
|
69
|
+
NonceSize,
|
|
70
|
+
plaintext,
|
|
71
|
+
additionalData,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Open(
|
|
76
|
+
dst: $.Slice<number>,
|
|
77
|
+
nonce: $.Slice<number>,
|
|
78
|
+
ciphertext: $.Slice<number>,
|
|
79
|
+
additionalData: $.Slice<number>,
|
|
80
|
+
): [$.Slice<number>, $.GoError] {
|
|
81
|
+
return open(
|
|
82
|
+
dst,
|
|
83
|
+
nobleChaCha20Poly1305,
|
|
84
|
+
this.key,
|
|
85
|
+
nonce,
|
|
86
|
+
NonceSize,
|
|
87
|
+
ciphertext,
|
|
88
|
+
additionalData,
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class xchacha20poly1305 implements AEAD {
|
|
94
|
+
readonly key: Uint8Array
|
|
95
|
+
|
|
96
|
+
constructor(key: Uint8Array) {
|
|
97
|
+
this.key = key.slice()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
NonceSize(): number {
|
|
101
|
+
return NonceSizeX
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Overhead(): number {
|
|
105
|
+
return Overhead
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Seal(
|
|
109
|
+
dst: $.Slice<number>,
|
|
110
|
+
nonce: $.Slice<number>,
|
|
111
|
+
plaintext: $.Slice<number>,
|
|
112
|
+
additionalData: $.Slice<number>,
|
|
113
|
+
): $.Slice<number> {
|
|
114
|
+
return seal(
|
|
115
|
+
dst,
|
|
116
|
+
nobleXChaCha20Poly1305,
|
|
117
|
+
this.key,
|
|
118
|
+
nonce,
|
|
119
|
+
NonceSizeX,
|
|
120
|
+
plaintext,
|
|
121
|
+
additionalData,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Open(
|
|
126
|
+
dst: $.Slice<number>,
|
|
127
|
+
nonce: $.Slice<number>,
|
|
128
|
+
ciphertext: $.Slice<number>,
|
|
129
|
+
additionalData: $.Slice<number>,
|
|
130
|
+
): [$.Slice<number>, $.GoError] {
|
|
131
|
+
return open(
|
|
132
|
+
dst,
|
|
133
|
+
nobleXChaCha20Poly1305,
|
|
134
|
+
this.key,
|
|
135
|
+
nonce,
|
|
136
|
+
NonceSizeX,
|
|
137
|
+
ciphertext,
|
|
138
|
+
additionalData,
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function New(key: $.Slice<number>): [AEAD | null, $.GoError] {
|
|
144
|
+
const keyBytes = bytes(key)
|
|
145
|
+
if (keyBytes.length !== KeySize) {
|
|
146
|
+
return [null, errors.New('chacha20poly1305: bad key length')]
|
|
147
|
+
}
|
|
148
|
+
return [
|
|
149
|
+
$.interfaceValue<AEAD | null>(
|
|
150
|
+
new chacha20poly1305(keyBytes),
|
|
151
|
+
'*chacha20poly1305.chacha20poly1305',
|
|
152
|
+
),
|
|
153
|
+
null,
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function NewX(key: $.Slice<number>): [AEAD | null, $.GoError] {
|
|
158
|
+
const keyBytes = bytes(key)
|
|
159
|
+
if (keyBytes.length !== KeySize) {
|
|
160
|
+
return [null, errors.New('chacha20poly1305: bad key length')]
|
|
161
|
+
}
|
|
162
|
+
return [
|
|
163
|
+
$.interfaceValue<AEAD | null>(
|
|
164
|
+
new xchacha20poly1305(keyBytes),
|
|
165
|
+
'*chacha20poly1305.xchacha20poly1305',
|
|
166
|
+
),
|
|
167
|
+
null,
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function seal(
|
|
172
|
+
dst: $.Slice<number>,
|
|
173
|
+
cipher: NobleAEAD,
|
|
174
|
+
key: Uint8Array,
|
|
175
|
+
nonce: $.Slice<number>,
|
|
176
|
+
expectedNonceSize: number,
|
|
177
|
+
plaintext: $.Slice<number>,
|
|
178
|
+
additionalData: $.Slice<number>,
|
|
179
|
+
): $.Slice<number> {
|
|
180
|
+
const nonceBytes = bytes(nonce)
|
|
181
|
+
if (nonceBytes.length !== expectedNonceSize) {
|
|
182
|
+
$.panic('chacha20poly1305: bad nonce length passed to Seal')
|
|
183
|
+
}
|
|
184
|
+
const plaintextBytes = bytes(plaintext)
|
|
185
|
+
if (plaintextBytes.length > maxIETFPlaintext) {
|
|
186
|
+
$.panic('chacha20poly1305: plaintext too large')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const sealed = cipher(key, nonceBytes, bytes(additionalData)).encrypt(
|
|
190
|
+
plaintextBytes,
|
|
191
|
+
)
|
|
192
|
+
return appendBytes(dst, sealed)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function open(
|
|
196
|
+
dst: $.Slice<number>,
|
|
197
|
+
cipher: NobleAEAD,
|
|
198
|
+
key: Uint8Array,
|
|
199
|
+
nonce: $.Slice<number>,
|
|
200
|
+
expectedNonceSize: number,
|
|
201
|
+
ciphertext: $.Slice<number>,
|
|
202
|
+
additionalData: $.Slice<number>,
|
|
203
|
+
): [$.Slice<number>, $.GoError] {
|
|
204
|
+
const nonceBytes = bytes(nonce)
|
|
205
|
+
if (nonceBytes.length !== expectedNonceSize) {
|
|
206
|
+
$.panic('chacha20poly1305: bad nonce length passed to Open')
|
|
207
|
+
}
|
|
208
|
+
const ciphertextBytes = bytes(ciphertext)
|
|
209
|
+
if (ciphertextBytes.length < Overhead) {
|
|
210
|
+
return [null, errOpen]
|
|
211
|
+
}
|
|
212
|
+
if (ciphertextBytes.length > maxIETFCiphertext) {
|
|
213
|
+
$.panic('chacha20poly1305: ciphertext too large')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
return [
|
|
218
|
+
appendBytes(
|
|
219
|
+
dst,
|
|
220
|
+
cipher(key, nonceBytes, bytes(additionalData)).decrypt(ciphertextBytes),
|
|
221
|
+
),
|
|
222
|
+
null,
|
|
223
|
+
]
|
|
224
|
+
} catch {
|
|
225
|
+
return [null, errOpen]
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function bytes(value: $.Slice<number>): Uint8Array {
|
|
230
|
+
return $.bytesToUint8Array(value).slice()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function appendBytes(
|
|
234
|
+
dst: $.Slice<number>,
|
|
235
|
+
suffix: Uint8Array,
|
|
236
|
+
): $.Slice<number> {
|
|
237
|
+
const prefix = $.bytesToUint8Array(dst)
|
|
238
|
+
if (prefix.length === 0) {
|
|
239
|
+
return suffix.slice()
|
|
240
|
+
}
|
|
241
|
+
const out = new Uint8Array(prefix.length + suffix.length)
|
|
242
|
+
out.set(prefix)
|
|
243
|
+
out.set(suffix, prefix.length)
|
|
244
|
+
return out
|
|
245
|
+
}
|
|
@@ -16,7 +16,7 @@ describe('internal/byteorder uint64', () => {
|
|
|
16
16
|
BEPutUint64(bytes, 0x0102030405060708n)
|
|
17
17
|
|
|
18
18
|
expect(Array.from(bytes)).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
|
|
19
|
-
expect(BEUint64(bytes)).toBe(
|
|
19
|
+
expect(BEUint64(bytes)).toBe(0x0102030405060708n)
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
test('reads and writes little-endian bigint values', () => {
|
|
@@ -25,7 +25,7 @@ describe('internal/byteorder uint64', () => {
|
|
|
25
25
|
LEPutUint64(bytes, 0x0102030405060708n)
|
|
26
26
|
|
|
27
27
|
expect(Array.from(bytes)).toEqual([8, 7, 6, 5, 4, 3, 2, 1])
|
|
28
|
-
expect(LEUint64(bytes)).toBe(
|
|
28
|
+
expect(LEUint64(bytes)).toBe(0x0102030405060708n)
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
test('appends uint64 values', () => {
|
|
@@ -13,7 +13,7 @@ export function BEUint32(b: $.Bytes): number {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function BEUint64(b: $.Bytes): number {
|
|
16
|
-
return
|
|
16
|
+
return $.uint(
|
|
17
17
|
(BigInt(b![0]) << 56n) |
|
|
18
18
|
(BigInt(b![1]) << 48n) |
|
|
19
19
|
(BigInt(b![2]) << 40n) |
|
|
@@ -34,7 +34,7 @@ export function LEUint32(b: $.Bytes): number {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export function LEUint64(b: $.Bytes): number {
|
|
37
|
-
return
|
|
37
|
+
return $.uint(
|
|
38
38
|
BigInt(b![0]) |
|
|
39
39
|
(BigInt(b![1]) << 8n) |
|
|
40
40
|
(BigInt(b![2]) << 16n) |
|
package/gs/io/io.test.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
LimitedReader,
|
|
4
|
+
MultiWriter,
|
|
5
|
+
NewSectionReader,
|
|
6
|
+
NopCloser,
|
|
7
|
+
Pipe,
|
|
8
|
+
TeeReader,
|
|
9
|
+
} from './index.js'
|
|
3
10
|
import { describe, expect, test } from 'vitest'
|
|
4
11
|
|
|
5
12
|
class sliceReader {
|
|
@@ -22,6 +29,15 @@ class captureWriter {
|
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
class syncReaderAt {
|
|
33
|
+
constructor(private data: Uint8Array) {}
|
|
34
|
+
|
|
35
|
+
ReadAt(p: $.Bytes, off: number): [number, $.GoError] {
|
|
36
|
+
const n = $.copy(p, this.data.subarray(off))
|
|
37
|
+
return [n, n < $.len(p) ? (new Error('EOF') as $.GoError) : null]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
describe('io override', () => {
|
|
26
42
|
test('LimitedReader accepts generated struct-literal construction', async () => {
|
|
27
43
|
const reader = new LimitedReader({
|
|
@@ -118,6 +134,45 @@ describe('io override', () => {
|
|
|
118
134
|
expect(Buffer.from(chunks).toString('utf8')).toBe('abc')
|
|
119
135
|
})
|
|
120
136
|
|
|
137
|
+
test('SectionReader preserves sync reads for sync ReaderAt', () => {
|
|
138
|
+
const reader = NewSectionReader(
|
|
139
|
+
new syncReaderAt($.stringToBytes('abcdef')),
|
|
140
|
+
1,
|
|
141
|
+
3,
|
|
142
|
+
)
|
|
143
|
+
const buf = new Uint8Array(4)
|
|
144
|
+
|
|
145
|
+
const result = reader.Read(buf)
|
|
146
|
+
expect(result).not.toBeInstanceOf(Promise)
|
|
147
|
+
const [n, err] = result
|
|
148
|
+
|
|
149
|
+
expect(err).toBeNull()
|
|
150
|
+
expect(n).toBe(3)
|
|
151
|
+
expect(Buffer.from(buf.subarray(0, n)).toString('utf8')).toBe('bcd')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('SectionReader awaits async ReaderAt', async () => {
|
|
155
|
+
const reader = NewSectionReader(
|
|
156
|
+
{
|
|
157
|
+
async ReadAt(p: $.Bytes, off: number): Promise<[number, $.GoError]> {
|
|
158
|
+
await Promise.resolve()
|
|
159
|
+
const data = $.stringToBytes('abcdef')
|
|
160
|
+
const n = $.copy(p, data.subarray(off))
|
|
161
|
+
return [n, n < $.len(p) ? (new Error('EOF') as $.GoError) : null]
|
|
162
|
+
},
|
|
163
|
+
} as any,
|
|
164
|
+
1,
|
|
165
|
+
3,
|
|
166
|
+
)
|
|
167
|
+
const buf = new Uint8Array(4)
|
|
168
|
+
|
|
169
|
+
const [n, err] = await reader.Read(buf)
|
|
170
|
+
|
|
171
|
+
expect(err).toBeNull()
|
|
172
|
+
expect(n).toBe(3)
|
|
173
|
+
expect(Buffer.from(buf.subarray(0, n)).toString('utf8')).toBe('bcd')
|
|
174
|
+
})
|
|
175
|
+
|
|
121
176
|
test('PipeReader waits for a later write', async () => {
|
|
122
177
|
const [reader, writer] = Pipe()
|
|
123
178
|
const buf = new Uint8Array(5)
|
package/gs/io/io.ts
CHANGED
|
@@ -363,7 +363,15 @@ export class SectionReader implements Reader, Seeker, ReaderAt {
|
|
|
363
363
|
p = $.goSlice(p, 0, max)
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
const
|
|
366
|
+
const res = this.r.ReadAt(p, this.off) as any
|
|
367
|
+
if (res instanceof Promise) {
|
|
368
|
+
return res.then(([n, err]: [number, $.GoError]) => {
|
|
369
|
+
this.off += n
|
|
370
|
+
return [n, err]
|
|
371
|
+
}) as any
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const [n, err] = res
|
|
367
375
|
this.off += n
|
|
368
376
|
return [n, err]
|
|
369
377
|
}
|
|
@@ -400,7 +408,16 @@ export class SectionReader implements Reader, Seeker, ReaderAt {
|
|
|
400
408
|
off += this.base
|
|
401
409
|
if (off + $.len(p) > this.limit) {
|
|
402
410
|
p = $.goSlice(p, 0, this.limit - off)
|
|
403
|
-
const
|
|
411
|
+
const res = this.r.ReadAt(p, off) as any
|
|
412
|
+
if (res instanceof Promise) {
|
|
413
|
+
return res.then(([n, err]: [number, $.GoError]) => {
|
|
414
|
+
if (err === null) {
|
|
415
|
+
return [n, EOF]
|
|
416
|
+
}
|
|
417
|
+
return [n, err]
|
|
418
|
+
}) as any
|
|
419
|
+
}
|
|
420
|
+
const [n, err] = res
|
|
404
421
|
if (err === null) {
|
|
405
422
|
return [n, EOF]
|
|
406
423
|
}
|