@amritk/nish-x86_64-linux 0.27.2 → 0.29.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 (54) hide show
  1. package/INSTALL.md +16 -8
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +118 -26
  4. package/package.json +1 -1
  5. package/runtime/core/host.d.ts +2 -1
  6. package/runtime/core/host.mjs +4 -0
  7. package/runtime/core.d.ts +47 -0
  8. package/runtime/core.mjs +67 -0
  9. package/runtime/nish.d.ts +176 -0
  10. package/runtime/nish.h +120 -6
  11. package/runtime/nish.mjs +24 -4
  12. package/runtime/runtime-checksum.c +270 -0
  13. package/runtime/runtime-net.c +386 -5
  14. package/runtime/runtime-os.c +59 -8
  15. package/runtime/runtime-parallel.c +81 -5
  16. package/runtime/runtime-sandbox.c +479 -0
  17. package/runtime/runtime-sha256.c +239 -0
  18. package/runtime/runtime-shm.c +541 -0
  19. package/runtime/runtime.c +827 -791
  20. package/scripts/build.sh +79 -28
  21. package/std/README.md +25 -13
  22. package/std/collections.ts +5 -5
  23. package/std/compress/checksum.ts +88 -75
  24. package/std/compress/deflate.ts +224 -0
  25. package/std/compress/gzip.ts +52 -0
  26. package/std/crypto/bcrypt.ts +816 -280
  27. package/std/crypto/hmac.ts +305 -2
  28. package/std/crypto/jwt.ts +1661 -0
  29. package/std/json-equal.ts +335 -0
  30. package/std/json-format.ts +1070 -0
  31. package/std/json-number.ts +428 -0
  32. package/std/json-pointer.ts +245 -0
  33. package/std/json-typed.ts +34 -0
  34. package/std/json-value.ts +510 -17
  35. package/std/json.ts +35 -6
  36. package/std/net/handler.ts +1348 -0
  37. package/std/net/http-client.ts +97 -16
  38. package/std/net/http-compress.ts +301 -16
  39. package/std/net/http1-client.ts +104 -19
  40. package/std/net/http1-server.ts +127 -24
  41. package/std/net/http1.ts +24 -4
  42. package/std/net/outbound.ts +409 -0
  43. package/std/net/postgres.ts +405 -0
  44. package/std/net/prefork.ts +344 -20
  45. package/std/net/websocket.ts +295 -14
  46. package/std/regex.ts +613 -52
  47. package/std/secret.ts +1 -1
  48. package/std/sync/ring.ts +587 -0
  49. package/std/sync/shm.ts +284 -0
  50. package/std/term-io.ts +35 -8
  51. package/std/term-rate.ts +13 -1
  52. package/std/threads.ts +35 -3
  53. package/std/yaml-json.ts +841 -0
  54. /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
@@ -46,6 +46,13 @@
46
46
  * stores nothing of its own and an arena scope around it releases all it
47
47
  * allocated (see "Caller-owned scratch" below).
48
48
  *
49
+ * `HmacKey` keys a `Secret<u8[]>` once, for a key MACed with many times (a
50
+ * JWT verifier's HS key): it keeps the inner and outer midstates (RFC 2104
51
+ * §4) as a `Secret<u64[]>`, and `hmacKeyedVerify` / `hmacKeyedInto` copy them
52
+ * into the key's own hashers per message instead of absorbing the key again.
53
+ * Each call wipes the copy, the hashers and the inner digest before it
54
+ * answers; `HmacKey.wipe()` zeroes the pads (see "A key held keyed" below).
55
+ *
49
56
  * `pbkdf2HmacSha256` is PBKDF2 with HMAC-SHA-256 (RFC 8018 §5.2), the key
50
57
  * derivation SCRAM-SHA-256 runs over a password: the password goes in as a
51
58
  * `Secret<u8[]>` and the derived key comes out as one, and every U_j and T_i
@@ -55,8 +62,8 @@
55
62
  */
56
63
  import { SHA256_BLOCK, SHA256_SIZE, Sha256 } from "nish/crypto/sha256"
57
64
  import { SHA384_SIZE, SHA512_BLOCK, SHA512_SIZE, Sha384, Sha512 } from "nish/crypto/sha512"
58
- import { timingSafeEqual } from "nish/crypto/ct"
59
- import { Secret, exposeWith, secret, wipe } from "nish:secret"
65
+ import { timingSafeEqual, timingSafeEqualAt } from "nish/crypto/ct"
66
+ import { Secret, expose, exposeWith, secret, wipe } from "nish:secret"
60
67
 
61
68
  /** RFC 2104 §2's `ipad`, the byte XORed into the key for the inner hash. */
62
69
  const HMAC_IPAD: i32 = 0x36
@@ -797,3 +804,299 @@ export const pbkdf2HmacSha256 = (
797
804
  }
798
805
  return secret(dk)
799
806
  }
807
+
808
+ // ---- A key held keyed: the pads absorbed once, as a Secret --------------------
809
+ //
810
+ // A verifier that checks many tags under one key — a JWT's HS256 signature on
811
+ // every request — pays for the key's two pad blocks once, not once per tag.
812
+ // RFC 2104 §4 notes the two keyed chaining values can be kept and the
813
+ // computation started from them; `HmacKey` keeps exactly those, and keeps them
814
+ // as a `Secret`, because they are key material: either one, with the hash's
815
+ // length padding, MACs any message under the key without the key itself.
816
+ //
817
+ // A function `exposeWith` runs may not write through the argument it is
818
+ // handed (NL2450), and a hasher made per tag stores its arrays in its fields,
819
+ // which no arena block may release around (NL2424). So each tag copies the
820
+ // midstates out with `expose`, resumes the two hashers the key owns from the
821
+ // copy, wipes the copy, feeds and finishes them and wipes them; what it
822
+ // allocates — the copy and the two digests — dies inside the arena block of
823
+ // `hmacKeyedInto` and `hmacKeyedVerify`. That is the shape
824
+ // `p256SignSha256Into` has (`std/crypto/p256.ts`): a plain copy of a secret
825
+ // step's answer, used in caller-owned scratch and wiped before return.
826
+
827
+ /** `HmacKey`'s hash: HMAC-SHA-256. */
828
+ export const HMAC_SHA256: i32 = 1
829
+ /** `HmacKey`'s hash: HMAC-SHA-384. */
830
+ export const HMAC_SHA384: i32 = 2
831
+ /** `HmacKey`'s hash: HMAC-SHA-512. */
832
+ export const HMAC_SHA512: i32 = 3
833
+
834
+ /** Words of one chaining value: eight, for SHA-256's `u32` and SHA-512's `u64` alike. */
835
+ const HMAC_WORDS: i32 = 8
836
+
837
+ /** The tag length of `hash` in bytes, 0 for no hash `HmacKey` knows. */
838
+ const hmacTagSize = (hash: i32): i32 => {
839
+ if (hash === HMAC_SHA256) {
840
+ return SHA256_SIZE
841
+ }
842
+ if (hash === HMAC_SHA384) {
843
+ return SHA384_SIZE
844
+ }
845
+ if (hash === HMAC_SHA512) {
846
+ return SHA512_SIZE
847
+ }
848
+ return 0
849
+ }
850
+
851
+ /**
852
+ * The inner and outer chaining values after the key's pad blocks, for
853
+ * `exposeWith` to run on the plain key: words 0 to 7 the inner, 8 to 15 the
854
+ * outer, SHA-256's `u32` words widened to `u64`. The two keying hashers are
855
+ * wiped before it answers.
856
+ */
857
+ const hmacMidstates = (key: u8[], hash: i32): u64[] => {
858
+ const out: u64[] = new Array<u64>(2 * HMAC_WORDS)
859
+ if (hash === HMAC_SHA256) {
860
+ const inner = new Sha256()
861
+ const outer = new Sha256()
862
+ hmacKeySha256(inner, outer, key)
863
+ for (
864
+ let i: i32 = 0;
865
+ i < HMAC_WORDS && i < toI32(inner.state.length) && i < toI32(outer.state.length);
866
+ i += 1
867
+ ) {
868
+ const j: i32 = i + HMAC_WORDS
869
+ if (i < toI32(out.length) && j < toI32(out.length)) {
870
+ out[i] = toU64(inner.state[i])
871
+ out[j] = toU64(outer.state[i])
872
+ }
873
+ }
874
+ hmacWipeSha256(inner)
875
+ hmacWipeSha256(outer)
876
+ } else if (hash === HMAC_SHA384) {
877
+ const inner = new Sha384()
878
+ const outer = new Sha384()
879
+ hmacKeySha384(inner, outer, key)
880
+ hmacMidstates512(out, inner.engine.state, outer.engine.state)
881
+ hmacWipeSha384(inner)
882
+ hmacWipeSha384(outer)
883
+ } else {
884
+ const inner = new Sha512()
885
+ const outer = new Sha512()
886
+ hmacKeySha512(inner, outer, key)
887
+ hmacMidstates512(out, inner.engine.state, outer.engine.state)
888
+ hmacWipeSha512(inner)
889
+ hmacWipeSha512(outer)
890
+ }
891
+ return out
892
+ }
893
+
894
+ /** `out[0 .. 8) = inner`, `out[8 .. 16) = outer`, for the SHA-512 family. */
895
+ const hmacMidstates512 = (out: u64[], inner: u64[], outer: u64[]): void => {
896
+ for (let i: i32 = 0; i < HMAC_WORDS && i < toI32(inner.length) && i < toI32(outer.length); i += 1) {
897
+ const j: i32 = i + HMAC_WORDS
898
+ if (i < toI32(out.length) && j < toI32(out.length)) {
899
+ out[i] = inner[i]
900
+ out[j] = outer[i]
901
+ }
902
+ }
903
+ }
904
+
905
+ /**
906
+ * An HMAC key absorbed once: the inner and outer chaining values after the
907
+ * key's pad blocks (RFC 2104 §4), held as a `Secret<u64[]>`. Every tag it
908
+ * makes or checks starts from them, so a tag costs the message's blocks and
909
+ * two finishing blocks, and never the key's two again.
910
+ *
911
+ * const key = new HmacKey(HMAC_SHA256, secretKey); // once
912
+ * hmacKeyedInto(key, data, 0, data.length, tag, 0); // per message
913
+ * const ok: boolean = hmacKeyedVerify(key, data, 0, data.length, tag, 0);
914
+ * key.wipe(); // when done
915
+ *
916
+ * The tags are `hmacSha256`'s, `hmacSha384`'s and `hmacSha512`'s for the same
917
+ * key. The key itself is read only inside `exposeWith`, and the hashers that
918
+ * keyed it are wiped; the caller still owns and wipes the `Secret` it passed.
919
+ * An unknown `hash` panics.
920
+ *
921
+ * The key also owns two hashers of its family, made once, that each tag is
922
+ * computed in and that are wiped before the tag call returns. One key is
923
+ * therefore used by one computation at a time, as a `Sha256` is.
924
+ */
925
+ export class HmacKey {
926
+ /** Words 0 to 7: the keyed inner chaining value; 8 to 15: the keyed outer one. */
927
+ readonly pads: Secret<u64[]>
928
+ /** SHA-256's work hashers; idle and wiped between tags. */
929
+ readonly inner256: Sha256
930
+ readonly outer256: Sha256
931
+ /**
932
+ * SHA-384's and SHA-512's work hashers: SHA-384 is SHA-512's compression
933
+ * from another start, cut to 48 bytes, so resumed from a chaining value the
934
+ * two are one hasher and differ only in the digest length taken.
935
+ */
936
+ readonly inner512: Sha512
937
+ readonly outer512: Sha512
938
+ /** `HMAC_SHA256`, `HMAC_SHA384` or `HMAC_SHA512`. */
939
+ readonly hash: i32
940
+ /**
941
+ * Whether the key still MACs: `wipe()` clears it. A wiped key's chaining
942
+ * values are zeros anyone can resume from, so a tag under them proves
943
+ * nothing and none is computed.
944
+ */
945
+ live: boolean
946
+
947
+ constructor(hash: i32, key: Secret<u8[]>) {
948
+ if (hmacTagSize(hash) === 0) {
949
+ panic("HmacKey: an unknown hash")
950
+ }
951
+ this.hash = hash
952
+ this.pads = secret(exposeWith(key, hash, hmacMidstates))
953
+ this.inner256 = new Sha256()
954
+ this.outer256 = new Sha256()
955
+ this.inner512 = new Sha512()
956
+ this.outer512 = new Sha512()
957
+ hmacWipeSha256(this.inner256)
958
+ hmacWipeSha256(this.outer256)
959
+ hmacWipeSha512(this.inner512)
960
+ hmacWipeSha512(this.outer512)
961
+ this.live = true
962
+ }
963
+
964
+ /** The tag length in bytes: 32, 48 or 64. */
965
+ size(): i32 {
966
+ return hmacTagSize(this.hash)
967
+ }
968
+
969
+ /** Zero both keyed chaining values and the work hashers; the key MACs nothing after. */
970
+ wipe(): void {
971
+ this.live = false
972
+ wipe(this.pads)
973
+ hmacWipeSha256(this.inner256)
974
+ hmacWipeSha256(this.outer256)
975
+ hmacWipeSha512(this.inner512)
976
+ hmacWipeSha512(this.outer512)
977
+ }
978
+ }
979
+
980
+ /** A plain copy of the 16 midstate words, for `expose` to run; the caller wipes it. */
981
+ const hmacCopyWords = (pads: u64[]): u64[] => {
982
+ const out: u64[] = new Array<u64>(2 * HMAC_WORDS)
983
+ for (let i: i32 = 0; i < toI32(out.length) && i < toI32(pads.length); i += 1) {
984
+ out[i] = pads[i]
985
+ }
986
+ return out
987
+ }
988
+
989
+ /** `h` started again at the point after one keyed block, chaining value `mid[at .. at + 8)`. */
990
+ const hmacResume256 = (h: Sha256, mid: u64[], at: i32): void => {
991
+ hmacWipeSha256(h)
992
+ const state: u32[] = h.state
993
+ for (let i: i32 = 0; i < HMAC_WORDS && i < toI32(state.length) && at + i < toI32(mid.length); i += 1) {
994
+ state[i] = toU32(mid[at + i])
995
+ }
996
+ h.total = toI64(SHA256_BLOCK)
997
+ h.finished = false
998
+ }
999
+
1000
+ /** `hmacResume256` for the SHA-512 family's hasher. */
1001
+ const hmacResume512 = (h: Sha512, mid: u64[], at: i32): void => {
1002
+ hmacWipeSha512(h)
1003
+ const state: u64[] = h.engine.state
1004
+ for (let i: i32 = 0; i < HMAC_WORDS && i < toI32(state.length) && at + i < toI32(mid.length); i += 1) {
1005
+ state[i] = mid[at + i]
1006
+ }
1007
+ h.engine.count = toU64(SHA512_BLOCK)
1008
+ h.engine.finished = false
1009
+ }
1010
+
1011
+ /**
1012
+ * The tag of `data[off .. off + len)` under `key`, as a fresh array the
1013
+ * caller wipes: the midstates copied out of their `Secret`, the key's two
1014
+ * hashers resumed from them, fed and finished, and the copy, the inner digest
1015
+ * and both hashers wiped before it answers.
1016
+ */
1017
+ const hmacKeyedTag = (key: HmacKey, data: u8[], off: i32, len: i32): u8[] => {
1018
+ const mid: u64[] = expose(key.pads, hmacCopyWords)
1019
+ if (key.hash === HMAC_SHA256) {
1020
+ const inner: Sha256 = key.inner256
1021
+ const outer: Sha256 = key.outer256
1022
+ hmacResume256(inner, mid, 0)
1023
+ hmacResume256(outer, mid, HMAC_WORDS)
1024
+ wipe(mid)
1025
+ inner.update(data, off, len)
1026
+ const digest: u8[] = inner.digest()
1027
+ outer.update(digest, HMAC_FROM, SHA256_SIZE)
1028
+ const tag: u8[] = outer.digest()
1029
+ wipe(digest)
1030
+ hmacWipeSha256(inner)
1031
+ hmacWipeSha256(outer)
1032
+ return tag
1033
+ }
1034
+ const size: i32 = key.hash === HMAC_SHA384 ? SHA384_SIZE : SHA512_SIZE
1035
+ const inner: Sha512 = key.inner512
1036
+ const outer: Sha512 = key.outer512
1037
+ hmacResume512(inner, mid, 0)
1038
+ hmacResume512(outer, mid, HMAC_WORDS)
1039
+ wipe(mid)
1040
+ inner.update(data, off, len)
1041
+ const digest: u8[] = inner.digest()
1042
+ outer.update(digest, HMAC_FROM, size)
1043
+ const tag: u8[] = outer.digest()
1044
+ wipe(digest)
1045
+ hmacWipeSha512(inner)
1046
+ hmacWipeSha512(outer)
1047
+ return tag
1048
+ }
1049
+
1050
+ /**
1051
+ * The tag of `data[off .. off + len)` under `key`, written at
1052
+ * `out[at .. at + key.size())`. A window outside its array panics before
1053
+ * anything is read. It runs in an arena block of its own and stores nothing,
1054
+ * so a tag leaves the arena where it was.
1055
+ */
1056
+ export const hmacKeyedInto = (key: HmacKey, data: u8[], off: i32, len: i32, out: u8[], at: i32): void => {
1057
+ const size: i32 = key.size()
1058
+ if (!key.live) {
1059
+ panic("hmacKeyedInto: the key is wiped")
1060
+ }
1061
+ if (!hmacWindowFits(toI32(data.length), off, len)) {
1062
+ panic("hmacKeyedInto: the message window is outside its array")
1063
+ }
1064
+ if (!hmacWindowFits(toI32(out.length), at, size)) {
1065
+ panic("hmacKeyedInto: the tag window is outside its array")
1066
+ }
1067
+ using _scope = arena()
1068
+ const tag: u8[] = hmacKeyedTag(key, data, off, len)
1069
+ for (let i: i32 = 0; i < size && i < toI32(tag.length) && at + i < toI32(out.length); i += 1) {
1070
+ out[at + i] = tag[i]
1071
+ }
1072
+ wipe(tag)
1073
+ }
1074
+
1075
+ /**
1076
+ * Whether `tag[tagAt .. tagAt + key.size())` is the tag of
1077
+ * `data[off .. off + len)` under `key`, compared in constant time and the
1078
+ * computed tag wiped, since it is the one tag a forger needs. A tag window
1079
+ * that does not fit answers `false`; a message window outside its array
1080
+ * panics. It runs in an arena block of its own and stores nothing.
1081
+ */
1082
+ export const hmacKeyedVerify = (
1083
+ key: HmacKey,
1084
+ data: u8[],
1085
+ off: i32,
1086
+ len: i32,
1087
+ tag: u8[],
1088
+ tagAt: i32
1089
+ ): boolean => {
1090
+ const size: i32 = key.size()
1091
+ if (!hmacWindowFits(toI32(data.length), off, len)) {
1092
+ panic("hmacKeyedVerify: the message window is outside its array")
1093
+ }
1094
+ if (!key.live || !hmacWindowFits(toI32(tag.length), tagAt, size)) {
1095
+ return false
1096
+ }
1097
+ using _scope = arena()
1098
+ const mine: u8[] = hmacKeyedTag(key, data, off, len)
1099
+ const ok: boolean = timingSafeEqualAt(mine, HMAC_FROM, tag, tagAt, size)
1100
+ wipe(mine)
1101
+ return ok
1102
+ }