@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.
- package/INSTALL.md +16 -8
- package/bin/nish +0 -0
- package/docs/AI.md +118 -26
- package/package.json +1 -1
- package/runtime/core/host.d.ts +2 -1
- package/runtime/core/host.mjs +4 -0
- package/runtime/core.d.ts +47 -0
- package/runtime/core.mjs +67 -0
- package/runtime/nish.d.ts +176 -0
- package/runtime/nish.h +120 -6
- package/runtime/nish.mjs +24 -4
- package/runtime/runtime-checksum.c +270 -0
- package/runtime/runtime-net.c +386 -5
- package/runtime/runtime-os.c +59 -8
- package/runtime/runtime-parallel.c +81 -5
- package/runtime/runtime-sandbox.c +479 -0
- package/runtime/runtime-sha256.c +239 -0
- package/runtime/runtime-shm.c +541 -0
- package/runtime/runtime.c +827 -791
- package/scripts/build.sh +79 -28
- package/std/README.md +25 -13
- package/std/collections.ts +5 -5
- package/std/compress/checksum.ts +88 -75
- package/std/compress/deflate.ts +224 -0
- package/std/compress/gzip.ts +52 -0
- package/std/crypto/bcrypt.ts +816 -280
- package/std/crypto/hmac.ts +305 -2
- package/std/crypto/jwt.ts +1661 -0
- package/std/json-equal.ts +335 -0
- package/std/json-format.ts +1070 -0
- package/std/json-number.ts +428 -0
- package/std/json-pointer.ts +245 -0
- package/std/json-typed.ts +34 -0
- package/std/json-value.ts +510 -17
- package/std/json.ts +35 -6
- package/std/net/handler.ts +1348 -0
- package/std/net/http-client.ts +97 -16
- package/std/net/http-compress.ts +301 -16
- package/std/net/http1-client.ts +104 -19
- package/std/net/http1-server.ts +127 -24
- package/std/net/http1.ts +24 -4
- package/std/net/outbound.ts +409 -0
- package/std/net/postgres.ts +405 -0
- package/std/net/prefork.ts +344 -20
- package/std/net/websocket.ts +295 -14
- package/std/regex.ts +613 -52
- package/std/secret.ts +1 -1
- package/std/sync/ring.ts +587 -0
- package/std/sync/shm.ts +284 -0
- package/std/term-io.ts +35 -8
- package/std/term-rate.ts +13 -1
- package/std/threads.ts +35 -3
- package/std/yaml-json.ts +841 -0
- /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
package/std/crypto/hmac.ts
CHANGED
|
@@ -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
|
+
}
|