From 6d55325a41fee7c95debfc721e4e9c792d02f897 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:02:05 +0000 Subject: [PATCH] Bump github.com/nats-io/nats-server/v2 from 2.9.19 to 2.9.21 Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.19 to 2.9.21. - [Release notes](https://github.com/nats-io/nats-server/releases) - [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml) - [Commits](https://github.com/nats-io/nats-server/compare/v2.9.19...v2.9.21) --- updated-dependencies: - dependency-name: github.com/nats-io/nats-server/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 6 +- go.sum | 12 +- .../klauspost/compress/flate/deflate.go | 5 +- .../compress/flate/huffman_bit_writer.go | 5 - .../compress/flate/huffman_sortByFreq.go | 19 - .../klauspost/compress/s2/encode_all.go | 1 - .../klauspost/compress/s2/encode_better.go | 44 +- .../klauspost/compress/s2/encodeblock_amd64.s | 655 +++++++------- .../klauspost/compress/s2/reader.go | 7 + .../klauspost/compress/s2/writer.go | 2 +- .../nats-io/nats-server/v2/conf/parse.go | 4 +- .../nats-io/nats-server/v2/logger/log.go | 35 +- .../nats-server/v2/server/certidp/certidp.go | 297 +++++++ .../nats-server/v2/server/certidp/messages.go | 106 +++ .../v2/server/certidp/ocsp_responder.go | 83 ++ .../v2/server/certstore/certstore.go | 102 +++ .../v2/server/certstore/certstore_other.go | 46 + .../v2/server/certstore/certstore_windows.go | 827 ++++++++++++++++++ .../nats-server/v2/server/certstore/errors.go | 73 ++ .../nats-io/nats-server/v2/server/client.go | 84 +- .../nats-io/nats-server/v2/server/const.go | 2 +- .../nats-io/nats-server/v2/server/consumer.go | 2 +- .../nats-io/nats-server/v2/server/events.go | 137 ++- .../nats-server/v2/server/filestore.go | 177 +++- .../nats-io/nats-server/v2/server/gateway.go | 2 +- .../nats-server/v2/server/jetstream.go | 2 +- .../nats-server/v2/server/jetstream_api.go | 16 +- .../v2/server/jetstream_cluster.go | 37 +- .../nats-io/nats-server/v2/server/leafnode.go | 7 +- .../nats-io/nats-server/v2/server/log.go | 11 +- .../nats-io/nats-server/v2/server/memstore.go | 3 +- .../nats-io/nats-server/v2/server/monitor.go | 178 ++-- .../nats-io/nats-server/v2/server/mqtt.go | 6 +- .../nats-io/nats-server/v2/server/ocsp.go | 271 ++++-- .../nats-server/v2/server/ocsp_peer.go | 405 +++++++++ .../v2/server/ocsp_responsecache.go | 636 ++++++++++++++ .../nats-io/nats-server/v2/server/opts.go | 151 +++- .../nats-io/nats-server/v2/server/reload.go | 40 +- .../nats-io/nats-server/v2/server/server.go | 83 +- .../nats-io/nats-server/v2/server/stream.go | 18 +- .../nats-io/nats-server/v2/server/sublist.go | 2 +- vendor/github.com/nats-io/nats.go/.travis.yml | 14 + vendor/github.com/nats-io/nats.go/README.md | 100 +-- vendor/github.com/nats-io/nats.go/context.go | 2 +- vendor/github.com/nats-io/nats.go/enc.go | 14 +- .../nats.go/encoders/builtin/default_enc.go | 4 +- .../nats.go/encoders/builtin/gob_enc.go | 4 +- .../nats.go/encoders/builtin/json_enc.go | 4 +- vendor/github.com/nats-io/nats.go/go_test.mod | 6 +- vendor/github.com/nats-io/nats.go/go_test.sum | 12 +- vendor/github.com/nats-io/nats.go/js.go | 46 +- vendor/github.com/nats-io/nats.go/jsm.go | 23 +- .../nats-io/nats.go/legacy_jetstream.md | 83 ++ vendor/github.com/nats-io/nats.go/nats.go | 4 +- vendor/github.com/nats-io/nats.go/netchan.go | 8 +- vendor/github.com/nats-io/nats.go/object.go | 1 + vendor/golang.org/x/crypto/cryptobyte/asn1.go | 824 +++++++++++++++++ .../x/crypto/cryptobyte/asn1/asn1.go | 46 + .../golang.org/x/crypto/cryptobyte/builder.go | 345 ++++++++ .../golang.org/x/crypto/cryptobyte/string.go | 172 ++++ vendor/modules.txt | 10 +- 61 files changed, 5460 insertions(+), 861 deletions(-) create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certidp/certidp.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certidp/messages.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certidp/ocsp_responder.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_other.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_windows.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/certstore/errors.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/ocsp_peer.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/ocsp_responsecache.go create mode 100644 vendor/github.com/nats-io/nats.go/legacy_jetstream.md create mode 100644 vendor/golang.org/x/crypto/cryptobyte/asn1.go create mode 100644 vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go create mode 100644 vendor/golang.org/x/crypto/cryptobyte/builder.go create mode 100644 vendor/golang.org/x/crypto/cryptobyte/string.go diff --git a/go.mod b/go.mod index 5b5eced77..5dd1d15e9 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/libregraph/lico v0.60.1-0.20230811070109-1d4140be554d github.com/mitchellh/mapstructure v1.5.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/nats-io/nats-server/v2 v2.9.19 + github.com/nats-io/nats-server/v2 v2.9.21 github.com/oklog/run v1.1.0 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo v1.16.5 @@ -238,7 +238,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/juliangruber/go-intersect v1.1.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect github.com/libregraph/oidc-go v1.0.0 // indirect github.com/longsleep/go-metrics v1.0.0 // indirect @@ -266,7 +266,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/nats-io/jwt/v2 v2.4.1 // indirect - github.com/nats-io/nats.go v1.27.0 // indirect + github.com/nats-io/nats.go v1.28.0 // indirect github.com/nats-io/nkeys v0.4.4 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect diff --git a/go.sum b/go.sum index 630f57e2b..90da53b42 100644 --- a/go.sum +++ b/go.sum @@ -1402,8 +1402,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1553,10 +1553,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4= github.com/nats-io/jwt/v2 v2.4.1/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI= -github.com/nats-io/nats-server/v2 v2.9.19 h1:OF9jSKZGo425C/FcVVIvNgpd36CUe7aVTTXEZRJk6kA= -github.com/nats-io/nats-server/v2 v2.9.19/go.mod h1:aTb/xtLCGKhfTFLxP591CMWfkdgBmcUUSkiSOe5A3gw= -github.com/nats-io/nats.go v1.27.0 h1:3o9fsPhmoKm+yK7rekH2GtWoE+D9jFbw8N3/ayI1C00= -github.com/nats-io/nats.go v1.27.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= +github.com/nats-io/nats-server/v2 v2.9.21 h1:2TBTh0UDE74eNXQmV4HofsmRSCiVN0TH2Wgrp6BD6fk= +github.com/nats-io/nats-server/v2 v2.9.21/go.mod h1:ozqMZc2vTHcNcblOiXMWIXkf8+0lDGAi5wQcG+O1mHU= +github.com/nats-io/nats.go v1.28.0 h1:Th4G6zdsz2d0OqXdfzKLClo6bOfoI/b1kInhRtFIy5c= +github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= diff --git a/vendor/github.com/klauspost/compress/flate/deflate.go b/vendor/github.com/klauspost/compress/flate/deflate.go index 82882961a..5faea0b2b 100644 --- a/vendor/github.com/klauspost/compress/flate/deflate.go +++ b/vendor/github.com/klauspost/compress/flate/deflate.go @@ -90,9 +90,8 @@ type advancedState struct { ii uint16 // position of last match, intended to overflow to reset. // input window: unprocessed data is window[index:windowEnd] - index int - estBitsPerByte int - hashMatch [maxMatchLength + minMatchLength]uint32 + index int + hashMatch [maxMatchLength + minMatchLength]uint32 // Input hash chains // hashHead[hashValue] contains the largest inputIndex with the specified hash value diff --git a/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go b/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go index 89a5dd89f..f70594c34 100644 --- a/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go +++ b/vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go @@ -34,11 +34,6 @@ const ( // Should preferably be a multiple of 6, since // we accumulate 6 bytes between writes to the buffer. bufferFlushSize = 246 - - // bufferSize is the actual output byte buffer size. - // It must have additional headroom for a flush - // which can contain up to 8 bytes. - bufferSize = bufferFlushSize + 8 ) // Minimum length code that emits bits. diff --git a/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go b/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go index 207780299..6c05ba8c1 100644 --- a/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go +++ b/vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go @@ -42,25 +42,6 @@ func quickSortByFreq(data []literalNode, a, b, maxDepth int) { } } -// siftDownByFreq implements the heap property on data[lo, hi). -// first is an offset into the array where the root of the heap lies. -func siftDownByFreq(data []literalNode, lo, hi, first int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && (data[first+child].freq == data[first+child+1].freq && data[first+child].literal < data[first+child+1].literal || data[first+child].freq < data[first+child+1].freq) { - child++ - } - if data[first+root].freq == data[first+child].freq && data[first+root].literal > data[first+child].literal || data[first+root].freq > data[first+child].freq { - return - } - data[first+root], data[first+child] = data[first+child], data[first+root] - root = child - } -} func doPivotByFreq(data []literalNode, lo, hi int) (midlo, midhi int) { m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. if hi-lo > 40 { diff --git a/vendor/github.com/klauspost/compress/s2/encode_all.go b/vendor/github.com/klauspost/compress/s2/encode_all.go index 11657f094..5e57995d4 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_all.go +++ b/vendor/github.com/klauspost/compress/s2/encode_all.go @@ -742,7 +742,6 @@ searchDict: x := load64(src, s-2) m2Hash := hash6(x, tableBits) currHash := hash6(x>>8, tableBits) - candidate = int(table[currHash]) table[m2Hash] = uint32(s - 2) table[currHash] = uint32(s - 1) cv = load64(src, s) diff --git a/vendor/github.com/klauspost/compress/s2/encode_better.go b/vendor/github.com/klauspost/compress/s2/encode_better.go index f46adb411..544cb1e17 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_better.go +++ b/vendor/github.com/klauspost/compress/s2/encode_better.go @@ -157,7 +157,6 @@ func encodeBlockBetterGo(dst, src []byte) (d int) { index0 := base + 1 index1 := s - 2 - cv = load64(src, s) for index0 < index1 { cv0 := load64(src, index0) cv1 := load64(src, index1) @@ -269,18 +268,21 @@ func encodeBlockBetterGo(dst, src []byte) (d int) { lTable[hash7(cv0, lTableBits)] = uint32(index0) sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) + // lTable could be postponed, but very minor difference. lTable[hash7(cv1, lTableBits)] = uint32(index1) sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) index0 += 1 index1 -= 1 cv = load64(src, s) - // index every second long in between. - for index0 < index1 { + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) - lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) index0 += 2 - index1 -= 2 + index2 += 2 } } @@ -459,12 +461,14 @@ func encodeBlockBetterSnappyGo(dst, src []byte) (d int) { index1 -= 1 cv = load64(src, s) - // index every second long in between. - for index0 < index1 { + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) - lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) index0 += 2 - index1 -= 2 + index2 += 2 } } @@ -599,7 +603,6 @@ searchDict: if s >= sLimit { break searchDict } - cv = load64(src, s) // Index in-between index0 := base + 1 index1 := s - 2 @@ -865,12 +868,14 @@ searchDict: index1 -= 1 cv = load64(src, s) - // index every second long in between. - for index0 < index1 { + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) - lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) index0 += 2 - index1 -= 2 + index2 += 2 } } @@ -961,7 +966,6 @@ searchDict: index0 := base + 1 index1 := s - 2 - cv = load64(src, s) for index0 < index1 { cv0 := load64(src, index0) cv1 := load64(src, index1) @@ -1079,12 +1083,14 @@ searchDict: index1 -= 1 cv = load64(src, s) - // index every second long in between. - for index0 < index1 { + // Index large values sparsely in between. + // We do two starting from different offsets for speed. + index2 := (index0 + index1 + 1) >> 1 + for index2 < index1 { lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) - lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) + lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2) index0 += 2 - index1 -= 2 + index2 += 2 } } diff --git a/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s index 9222d179c..54031aa31 100644 --- a/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s +++ b/vendor/github.com/klauspost/compress/s2/encodeblock_amd64.s @@ -274,7 +274,6 @@ matchlen_loop_repeat_extend_encodeBlockAsm: LEAL 8(R11), R11 CMPL R8, $0x08 JAE matchlen_loopback_repeat_extend_encodeBlockAsm - JZ repeat_extend_forward_end_encodeBlockAsm matchlen_match4_repeat_extend_encodeBlockAsm: CMPL R8, $0x04 @@ -282,21 +281,21 @@ matchlen_match4_repeat_extend_encodeBlockAsm: MOVL (R9)(R11*1), R10 CMPL (BX)(R11*1), R10 JNE matchlen_match2_repeat_extend_encodeBlockAsm - SUBL $0x04, R8 + LEAL -4(R8), R8 LEAL 4(R11), R11 matchlen_match2_repeat_extend_encodeBlockAsm: - CMPL R8, $0x02 - JB matchlen_match1_repeat_extend_encodeBlockAsm + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm + JB repeat_extend_forward_end_encodeBlockAsm MOVW (R9)(R11*1), R10 CMPW (BX)(R11*1), R10 JNE matchlen_match1_repeat_extend_encodeBlockAsm - SUBL $0x02, R8 LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeBlockAsm matchlen_match1_repeat_extend_encodeBlockAsm: - CMPL R8, $0x01 - JB repeat_extend_forward_end_encodeBlockAsm MOVB (R9)(R11*1), R10 CMPB (BX)(R11*1), R10 JNE repeat_extend_forward_end_encodeBlockAsm @@ -877,7 +876,6 @@ matchlen_loop_match_nolit_encodeBlockAsm: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeBlockAsm - JZ match_nolit_end_encodeBlockAsm matchlen_match4_match_nolit_encodeBlockAsm: CMPL SI, $0x04 @@ -885,21 +883,21 @@ matchlen_match4_match_nolit_encodeBlockAsm: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeBlockAsm - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeBlockAsm: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeBlockAsm + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm + JB match_nolit_end_encodeBlockAsm MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeBlockAsm - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeBlockAsm matchlen_match1_match_nolit_encodeBlockAsm: - CMPL SI, $0x01 - JB match_nolit_end_encodeBlockAsm MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeBlockAsm @@ -1637,7 +1635,6 @@ matchlen_loop_repeat_extend_encodeBlockAsm4MB: LEAL 8(R11), R11 CMPL R8, $0x08 JAE matchlen_loopback_repeat_extend_encodeBlockAsm4MB - JZ repeat_extend_forward_end_encodeBlockAsm4MB matchlen_match4_repeat_extend_encodeBlockAsm4MB: CMPL R8, $0x04 @@ -1645,21 +1642,21 @@ matchlen_match4_repeat_extend_encodeBlockAsm4MB: MOVL (R9)(R11*1), R10 CMPL (BX)(R11*1), R10 JNE matchlen_match2_repeat_extend_encodeBlockAsm4MB - SUBL $0x04, R8 + LEAL -4(R8), R8 LEAL 4(R11), R11 matchlen_match2_repeat_extend_encodeBlockAsm4MB: - CMPL R8, $0x02 - JB matchlen_match1_repeat_extend_encodeBlockAsm4MB + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm4MB + JB repeat_extend_forward_end_encodeBlockAsm4MB MOVW (R9)(R11*1), R10 CMPW (BX)(R11*1), R10 JNE matchlen_match1_repeat_extend_encodeBlockAsm4MB - SUBL $0x02, R8 LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeBlockAsm4MB matchlen_match1_repeat_extend_encodeBlockAsm4MB: - CMPL R8, $0x01 - JB repeat_extend_forward_end_encodeBlockAsm4MB MOVB (R9)(R11*1), R10 CMPB (BX)(R11*1), R10 JNE repeat_extend_forward_end_encodeBlockAsm4MB @@ -2190,7 +2187,6 @@ matchlen_loop_match_nolit_encodeBlockAsm4MB: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeBlockAsm4MB - JZ match_nolit_end_encodeBlockAsm4MB matchlen_match4_match_nolit_encodeBlockAsm4MB: CMPL SI, $0x04 @@ -2198,21 +2194,21 @@ matchlen_match4_match_nolit_encodeBlockAsm4MB: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeBlockAsm4MB - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeBlockAsm4MB: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeBlockAsm4MB + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm4MB + JB match_nolit_end_encodeBlockAsm4MB MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeBlockAsm4MB - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeBlockAsm4MB matchlen_match1_match_nolit_encodeBlockAsm4MB: - CMPL SI, $0x01 - JB match_nolit_end_encodeBlockAsm4MB MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeBlockAsm4MB @@ -2902,7 +2898,6 @@ matchlen_loop_repeat_extend_encodeBlockAsm12B: LEAL 8(R11), R11 CMPL R8, $0x08 JAE matchlen_loopback_repeat_extend_encodeBlockAsm12B - JZ repeat_extend_forward_end_encodeBlockAsm12B matchlen_match4_repeat_extend_encodeBlockAsm12B: CMPL R8, $0x04 @@ -2910,21 +2905,21 @@ matchlen_match4_repeat_extend_encodeBlockAsm12B: MOVL (R9)(R11*1), R10 CMPL (BX)(R11*1), R10 JNE matchlen_match2_repeat_extend_encodeBlockAsm12B - SUBL $0x04, R8 + LEAL -4(R8), R8 LEAL 4(R11), R11 matchlen_match2_repeat_extend_encodeBlockAsm12B: - CMPL R8, $0x02 - JB matchlen_match1_repeat_extend_encodeBlockAsm12B + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm12B + JB repeat_extend_forward_end_encodeBlockAsm12B MOVW (R9)(R11*1), R10 CMPW (BX)(R11*1), R10 JNE matchlen_match1_repeat_extend_encodeBlockAsm12B - SUBL $0x02, R8 LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeBlockAsm12B matchlen_match1_repeat_extend_encodeBlockAsm12B: - CMPL R8, $0x01 - JB repeat_extend_forward_end_encodeBlockAsm12B MOVB (R9)(R11*1), R10 CMPB (BX)(R11*1), R10 JNE repeat_extend_forward_end_encodeBlockAsm12B @@ -3333,7 +3328,6 @@ matchlen_loop_match_nolit_encodeBlockAsm12B: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeBlockAsm12B - JZ match_nolit_end_encodeBlockAsm12B matchlen_match4_match_nolit_encodeBlockAsm12B: CMPL SI, $0x04 @@ -3341,21 +3335,21 @@ matchlen_match4_match_nolit_encodeBlockAsm12B: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeBlockAsm12B - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeBlockAsm12B: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeBlockAsm12B + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm12B + JB match_nolit_end_encodeBlockAsm12B MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeBlockAsm12B - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeBlockAsm12B matchlen_match1_match_nolit_encodeBlockAsm12B: - CMPL SI, $0x01 - JB match_nolit_end_encodeBlockAsm12B MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeBlockAsm12B @@ -3935,7 +3929,6 @@ matchlen_loop_repeat_extend_encodeBlockAsm10B: LEAL 8(R11), R11 CMPL R8, $0x08 JAE matchlen_loopback_repeat_extend_encodeBlockAsm10B - JZ repeat_extend_forward_end_encodeBlockAsm10B matchlen_match4_repeat_extend_encodeBlockAsm10B: CMPL R8, $0x04 @@ -3943,21 +3936,21 @@ matchlen_match4_repeat_extend_encodeBlockAsm10B: MOVL (R9)(R11*1), R10 CMPL (BX)(R11*1), R10 JNE matchlen_match2_repeat_extend_encodeBlockAsm10B - SUBL $0x04, R8 + LEAL -4(R8), R8 LEAL 4(R11), R11 matchlen_match2_repeat_extend_encodeBlockAsm10B: - CMPL R8, $0x02 - JB matchlen_match1_repeat_extend_encodeBlockAsm10B + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm10B + JB repeat_extend_forward_end_encodeBlockAsm10B MOVW (R9)(R11*1), R10 CMPW (BX)(R11*1), R10 JNE matchlen_match1_repeat_extend_encodeBlockAsm10B - SUBL $0x02, R8 LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeBlockAsm10B matchlen_match1_repeat_extend_encodeBlockAsm10B: - CMPL R8, $0x01 - JB repeat_extend_forward_end_encodeBlockAsm10B MOVB (R9)(R11*1), R10 CMPB (BX)(R11*1), R10 JNE repeat_extend_forward_end_encodeBlockAsm10B @@ -4366,7 +4359,6 @@ matchlen_loop_match_nolit_encodeBlockAsm10B: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeBlockAsm10B - JZ match_nolit_end_encodeBlockAsm10B matchlen_match4_match_nolit_encodeBlockAsm10B: CMPL SI, $0x04 @@ -4374,21 +4366,21 @@ matchlen_match4_match_nolit_encodeBlockAsm10B: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeBlockAsm10B - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeBlockAsm10B: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeBlockAsm10B + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm10B + JB match_nolit_end_encodeBlockAsm10B MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeBlockAsm10B - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeBlockAsm10B matchlen_match1_match_nolit_encodeBlockAsm10B: - CMPL SI, $0x01 - JB match_nolit_end_encodeBlockAsm10B MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeBlockAsm10B @@ -4968,7 +4960,6 @@ matchlen_loop_repeat_extend_encodeBlockAsm8B: LEAL 8(R11), R11 CMPL R8, $0x08 JAE matchlen_loopback_repeat_extend_encodeBlockAsm8B - JZ repeat_extend_forward_end_encodeBlockAsm8B matchlen_match4_repeat_extend_encodeBlockAsm8B: CMPL R8, $0x04 @@ -4976,21 +4967,21 @@ matchlen_match4_repeat_extend_encodeBlockAsm8B: MOVL (R9)(R11*1), R10 CMPL (BX)(R11*1), R10 JNE matchlen_match2_repeat_extend_encodeBlockAsm8B - SUBL $0x04, R8 + LEAL -4(R8), R8 LEAL 4(R11), R11 matchlen_match2_repeat_extend_encodeBlockAsm8B: - CMPL R8, $0x02 - JB matchlen_match1_repeat_extend_encodeBlockAsm8B + CMPL R8, $0x01 + JE matchlen_match1_repeat_extend_encodeBlockAsm8B + JB repeat_extend_forward_end_encodeBlockAsm8B MOVW (R9)(R11*1), R10 CMPW (BX)(R11*1), R10 JNE matchlen_match1_repeat_extend_encodeBlockAsm8B - SUBL $0x02, R8 LEAL 2(R11), R11 + SUBL $0x02, R8 + JZ repeat_extend_forward_end_encodeBlockAsm8B matchlen_match1_repeat_extend_encodeBlockAsm8B: - CMPL R8, $0x01 - JB repeat_extend_forward_end_encodeBlockAsm8B MOVB (R9)(R11*1), R10 CMPB (BX)(R11*1), R10 JNE repeat_extend_forward_end_encodeBlockAsm8B @@ -5385,7 +5376,6 @@ matchlen_loop_match_nolit_encodeBlockAsm8B: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeBlockAsm8B - JZ match_nolit_end_encodeBlockAsm8B matchlen_match4_match_nolit_encodeBlockAsm8B: CMPL SI, $0x04 @@ -5393,21 +5383,21 @@ matchlen_match4_match_nolit_encodeBlockAsm8B: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeBlockAsm8B - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeBlockAsm8B: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeBlockAsm8B + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeBlockAsm8B + JB match_nolit_end_encodeBlockAsm8B MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeBlockAsm8B - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeBlockAsm8B matchlen_match1_match_nolit_encodeBlockAsm8B: - CMPL SI, $0x01 - JB match_nolit_end_encodeBlockAsm8B MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeBlockAsm8B @@ -5889,7 +5879,6 @@ matchlen_loop_match_nolit_encodeBetterBlockAsm: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm - JZ match_nolit_end_encodeBetterBlockAsm matchlen_match4_match_nolit_encodeBetterBlockAsm: CMPL DI, $0x04 @@ -5897,21 +5886,21 @@ matchlen_match4_match_nolit_encodeBetterBlockAsm: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeBetterBlockAsm - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeBetterBlockAsm: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeBetterBlockAsm + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm + JB match_nolit_end_encodeBetterBlockAsm MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeBetterBlockAsm - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeBetterBlockAsm matchlen_match1_match_nolit_encodeBetterBlockAsm: - CMPL DI, $0x01 - JB match_nolit_end_encodeBetterBlockAsm MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeBetterBlockAsm @@ -6607,24 +6596,26 @@ match_nolit_dst_ok_encodeBetterBlockAsm: MOVL R8, 24(SP)(R11*4) MOVL DI, 524312(SP)(R10*4) MOVL R13, 524312(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeBetterBlockAsm: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeBetterBlockAsm - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x08, DI - IMULQ BX, DI - SHRQ $0x2f, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x08, R9 IMULQ BX, R9 SHRQ $0x2f, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x08, R10 + IMULQ BX, R10 + SHRQ $0x2f, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeBetterBlockAsm emit_remainder_encodeBetterBlockAsm: @@ -6960,7 +6951,6 @@ matchlen_loop_match_nolit_encodeBetterBlockAsm4MB: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm4MB - JZ match_nolit_end_encodeBetterBlockAsm4MB matchlen_match4_match_nolit_encodeBetterBlockAsm4MB: CMPL DI, $0x04 @@ -6968,21 +6958,21 @@ matchlen_match4_match_nolit_encodeBetterBlockAsm4MB: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeBetterBlockAsm4MB - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeBetterBlockAsm4MB: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeBetterBlockAsm4MB + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm4MB + JB match_nolit_end_encodeBetterBlockAsm4MB MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeBetterBlockAsm4MB - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeBetterBlockAsm4MB matchlen_match1_match_nolit_encodeBetterBlockAsm4MB: - CMPL DI, $0x01 - JB match_nolit_end_encodeBetterBlockAsm4MB MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeBetterBlockAsm4MB @@ -7620,24 +7610,26 @@ match_nolit_dst_ok_encodeBetterBlockAsm4MB: MOVL R8, 24(SP)(R11*4) MOVL DI, 524312(SP)(R10*4) MOVL R13, 524312(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeBetterBlockAsm4MB: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeBetterBlockAsm4MB - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x08, DI - IMULQ BX, DI - SHRQ $0x2f, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x08, R9 IMULQ BX, R9 SHRQ $0x2f, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x08, R10 + IMULQ BX, R10 + SHRQ $0x2f, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeBetterBlockAsm4MB emit_remainder_encodeBetterBlockAsm4MB: @@ -7957,7 +7949,6 @@ matchlen_loop_match_nolit_encodeBetterBlockAsm12B: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm12B - JZ match_nolit_end_encodeBetterBlockAsm12B matchlen_match4_match_nolit_encodeBetterBlockAsm12B: CMPL DI, $0x04 @@ -7965,21 +7956,21 @@ matchlen_match4_match_nolit_encodeBetterBlockAsm12B: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeBetterBlockAsm12B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeBetterBlockAsm12B: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeBetterBlockAsm12B + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm12B + JB match_nolit_end_encodeBetterBlockAsm12B MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeBetterBlockAsm12B - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeBetterBlockAsm12B matchlen_match1_match_nolit_encodeBetterBlockAsm12B: - CMPL DI, $0x01 - JB match_nolit_end_encodeBetterBlockAsm12B MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeBetterBlockAsm12B @@ -8478,24 +8469,26 @@ match_nolit_dst_ok_encodeBetterBlockAsm12B: MOVL R8, 24(SP)(R11*4) MOVL DI, 65560(SP)(R10*4) MOVL R13, 65560(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeBetterBlockAsm12B: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeBetterBlockAsm12B - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x10, DI - IMULQ BX, DI - SHRQ $0x32, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x10, R9 IMULQ BX, R9 SHRQ $0x32, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x10, R10 + IMULQ BX, R10 + SHRQ $0x32, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeBetterBlockAsm12B emit_remainder_encodeBetterBlockAsm12B: @@ -8807,7 +8800,6 @@ matchlen_loop_match_nolit_encodeBetterBlockAsm10B: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm10B - JZ match_nolit_end_encodeBetterBlockAsm10B matchlen_match4_match_nolit_encodeBetterBlockAsm10B: CMPL DI, $0x04 @@ -8815,21 +8807,21 @@ matchlen_match4_match_nolit_encodeBetterBlockAsm10B: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeBetterBlockAsm10B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeBetterBlockAsm10B: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeBetterBlockAsm10B + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm10B + JB match_nolit_end_encodeBetterBlockAsm10B MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeBetterBlockAsm10B - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeBetterBlockAsm10B matchlen_match1_match_nolit_encodeBetterBlockAsm10B: - CMPL DI, $0x01 - JB match_nolit_end_encodeBetterBlockAsm10B MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeBetterBlockAsm10B @@ -9328,24 +9320,26 @@ match_nolit_dst_ok_encodeBetterBlockAsm10B: MOVL R8, 24(SP)(R11*4) MOVL DI, 16408(SP)(R10*4) MOVL R13, 16408(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeBetterBlockAsm10B: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeBetterBlockAsm10B - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x10, DI - IMULQ BX, DI - SHRQ $0x34, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x10, R9 IMULQ BX, R9 SHRQ $0x34, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x10, R10 + IMULQ BX, R10 + SHRQ $0x34, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeBetterBlockAsm10B emit_remainder_encodeBetterBlockAsm10B: @@ -9657,7 +9651,6 @@ matchlen_loop_match_nolit_encodeBetterBlockAsm8B: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeBetterBlockAsm8B - JZ match_nolit_end_encodeBetterBlockAsm8B matchlen_match4_match_nolit_encodeBetterBlockAsm8B: CMPL DI, $0x04 @@ -9665,21 +9658,21 @@ matchlen_match4_match_nolit_encodeBetterBlockAsm8B: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeBetterBlockAsm8B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeBetterBlockAsm8B: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeBetterBlockAsm8B + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeBetterBlockAsm8B + JB match_nolit_end_encodeBetterBlockAsm8B MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeBetterBlockAsm8B - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeBetterBlockAsm8B matchlen_match1_match_nolit_encodeBetterBlockAsm8B: - CMPL DI, $0x01 - JB match_nolit_end_encodeBetterBlockAsm8B MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeBetterBlockAsm8B @@ -10164,24 +10157,26 @@ match_nolit_dst_ok_encodeBetterBlockAsm8B: MOVL R8, 24(SP)(R11*4) MOVL DI, 4120(SP)(R10*4) MOVL R13, 4120(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeBetterBlockAsm8B: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeBetterBlockAsm8B - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x10, DI - IMULQ BX, DI - SHRQ $0x36, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x10, R9 IMULQ BX, R9 SHRQ $0x36, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x10, R10 + IMULQ BX, R10 + SHRQ $0x36, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeBetterBlockAsm8B emit_remainder_encodeBetterBlockAsm8B: @@ -10605,7 +10600,6 @@ matchlen_loop_repeat_extend_encodeSnappyBlockAsm: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm - JZ repeat_extend_forward_end_encodeSnappyBlockAsm matchlen_match4_repeat_extend_encodeSnappyBlockAsm: CMPL DI, $0x04 @@ -10613,21 +10607,21 @@ matchlen_match4_repeat_extend_encodeSnappyBlockAsm: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_encodeSnappyBlockAsm: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_encodeSnappyBlockAsm + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm + JB repeat_extend_forward_end_encodeSnappyBlockAsm MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_encodeSnappyBlockAsm matchlen_match1_repeat_extend_encodeSnappyBlockAsm: - CMPL DI, $0x01 - JB repeat_extend_forward_end_encodeSnappyBlockAsm MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_encodeSnappyBlockAsm @@ -10928,7 +10922,6 @@ matchlen_loop_match_nolit_encodeSnappyBlockAsm: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm - JZ match_nolit_end_encodeSnappyBlockAsm matchlen_match4_match_nolit_encodeSnappyBlockAsm: CMPL SI, $0x04 @@ -10936,21 +10929,21 @@ matchlen_match4_match_nolit_encodeSnappyBlockAsm: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeSnappyBlockAsm: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBlockAsm + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm + JB match_nolit_end_encodeSnappyBlockAsm MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeSnappyBlockAsm matchlen_match1_match_nolit_encodeSnappyBlockAsm: - CMPL SI, $0x01 - JB match_nolit_end_encodeSnappyBlockAsm MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeSnappyBlockAsm @@ -11469,7 +11462,6 @@ matchlen_loop_repeat_extend_encodeSnappyBlockAsm64K: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm64K - JZ repeat_extend_forward_end_encodeSnappyBlockAsm64K matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K: CMPL DI, $0x04 @@ -11477,21 +11469,21 @@ matchlen_match4_repeat_extend_encodeSnappyBlockAsm64K: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm64K - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_encodeSnappyBlockAsm64K: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K + JB repeat_extend_forward_end_encodeSnappyBlockAsm64K MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_encodeSnappyBlockAsm64K matchlen_match1_repeat_extend_encodeSnappyBlockAsm64K: - CMPL DI, $0x01 - JB repeat_extend_forward_end_encodeSnappyBlockAsm64K MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_encodeSnappyBlockAsm64K @@ -11752,7 +11744,6 @@ matchlen_loop_match_nolit_encodeSnappyBlockAsm64K: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm64K - JZ match_nolit_end_encodeSnappyBlockAsm64K matchlen_match4_match_nolit_encodeSnappyBlockAsm64K: CMPL SI, $0x04 @@ -11760,21 +11751,21 @@ matchlen_match4_match_nolit_encodeSnappyBlockAsm64K: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm64K - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeSnappyBlockAsm64K: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBlockAsm64K + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm64K + JB match_nolit_end_encodeSnappyBlockAsm64K MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm64K - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeSnappyBlockAsm64K matchlen_match1_match_nolit_encodeSnappyBlockAsm64K: - CMPL SI, $0x01 - JB match_nolit_end_encodeSnappyBlockAsm64K MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeSnappyBlockAsm64K @@ -12253,7 +12244,6 @@ matchlen_loop_repeat_extend_encodeSnappyBlockAsm12B: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm12B - JZ repeat_extend_forward_end_encodeSnappyBlockAsm12B matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B: CMPL DI, $0x04 @@ -12261,21 +12251,21 @@ matchlen_match4_repeat_extend_encodeSnappyBlockAsm12B: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm12B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_encodeSnappyBlockAsm12B: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B + JB repeat_extend_forward_end_encodeSnappyBlockAsm12B MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_encodeSnappyBlockAsm12B matchlen_match1_repeat_extend_encodeSnappyBlockAsm12B: - CMPL DI, $0x01 - JB repeat_extend_forward_end_encodeSnappyBlockAsm12B MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_encodeSnappyBlockAsm12B @@ -12536,7 +12526,6 @@ matchlen_loop_match_nolit_encodeSnappyBlockAsm12B: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm12B - JZ match_nolit_end_encodeSnappyBlockAsm12B matchlen_match4_match_nolit_encodeSnappyBlockAsm12B: CMPL SI, $0x04 @@ -12544,21 +12533,21 @@ matchlen_match4_match_nolit_encodeSnappyBlockAsm12B: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm12B - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeSnappyBlockAsm12B: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBlockAsm12B + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm12B + JB match_nolit_end_encodeSnappyBlockAsm12B MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm12B - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeSnappyBlockAsm12B matchlen_match1_match_nolit_encodeSnappyBlockAsm12B: - CMPL SI, $0x01 - JB match_nolit_end_encodeSnappyBlockAsm12B MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeSnappyBlockAsm12B @@ -13037,7 +13026,6 @@ matchlen_loop_repeat_extend_encodeSnappyBlockAsm10B: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm10B - JZ repeat_extend_forward_end_encodeSnappyBlockAsm10B matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B: CMPL DI, $0x04 @@ -13045,21 +13033,21 @@ matchlen_match4_repeat_extend_encodeSnappyBlockAsm10B: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm10B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_encodeSnappyBlockAsm10B: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B + JB repeat_extend_forward_end_encodeSnappyBlockAsm10B MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_encodeSnappyBlockAsm10B matchlen_match1_repeat_extend_encodeSnappyBlockAsm10B: - CMPL DI, $0x01 - JB repeat_extend_forward_end_encodeSnappyBlockAsm10B MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_encodeSnappyBlockAsm10B @@ -13320,7 +13308,6 @@ matchlen_loop_match_nolit_encodeSnappyBlockAsm10B: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm10B - JZ match_nolit_end_encodeSnappyBlockAsm10B matchlen_match4_match_nolit_encodeSnappyBlockAsm10B: CMPL SI, $0x04 @@ -13328,21 +13315,21 @@ matchlen_match4_match_nolit_encodeSnappyBlockAsm10B: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm10B - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeSnappyBlockAsm10B: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBlockAsm10B + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm10B + JB match_nolit_end_encodeSnappyBlockAsm10B MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm10B - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeSnappyBlockAsm10B matchlen_match1_match_nolit_encodeSnappyBlockAsm10B: - CMPL SI, $0x01 - JB match_nolit_end_encodeSnappyBlockAsm10B MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeSnappyBlockAsm10B @@ -13821,7 +13808,6 @@ matchlen_loop_repeat_extend_encodeSnappyBlockAsm8B: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_encodeSnappyBlockAsm8B - JZ repeat_extend_forward_end_encodeSnappyBlockAsm8B matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B: CMPL DI, $0x04 @@ -13829,21 +13815,21 @@ matchlen_match4_repeat_extend_encodeSnappyBlockAsm8B: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_encodeSnappyBlockAsm8B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_encodeSnappyBlockAsm8B: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B + JB repeat_extend_forward_end_encodeSnappyBlockAsm8B MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_encodeSnappyBlockAsm8B matchlen_match1_repeat_extend_encodeSnappyBlockAsm8B: - CMPL DI, $0x01 - JB repeat_extend_forward_end_encodeSnappyBlockAsm8B MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_encodeSnappyBlockAsm8B @@ -14102,7 +14088,6 @@ matchlen_loop_match_nolit_encodeSnappyBlockAsm8B: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBlockAsm8B - JZ match_nolit_end_encodeSnappyBlockAsm8B matchlen_match4_match_nolit_encodeSnappyBlockAsm8B: CMPL SI, $0x04 @@ -14110,21 +14095,21 @@ matchlen_match4_match_nolit_encodeSnappyBlockAsm8B: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_encodeSnappyBlockAsm8B - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_encodeSnappyBlockAsm8B: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBlockAsm8B + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBlockAsm8B + JB match_nolit_end_encodeSnappyBlockAsm8B MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_encodeSnappyBlockAsm8B - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_encodeSnappyBlockAsm8B matchlen_match1_match_nolit_encodeSnappyBlockAsm8B: - CMPL SI, $0x01 - JB match_nolit_end_encodeSnappyBlockAsm8B MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_encodeSnappyBlockAsm8B @@ -14513,7 +14498,6 @@ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm - JZ match_nolit_end_encodeSnappyBetterBlockAsm matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm: CMPL DI, $0x04 @@ -14521,21 +14505,21 @@ matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm + JB match_nolit_end_encodeSnappyBetterBlockAsm MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBetterBlockAsm matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm: - CMPL DI, $0x01 - JB match_nolit_end_encodeSnappyBetterBlockAsm MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeSnappyBetterBlockAsm @@ -14790,24 +14774,26 @@ match_nolit_dst_ok_encodeSnappyBetterBlockAsm: MOVL R8, 24(SP)(R11*4) MOVL DI, 524312(SP)(R10*4) MOVL R13, 524312(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeSnappyBetterBlockAsm: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeSnappyBetterBlockAsm - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x08, DI - IMULQ BX, DI - SHRQ $0x2f, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x08, R9 IMULQ BX, R9 SHRQ $0x2f, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x08, R10 + IMULQ BX, R10 + SHRQ $0x2f, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeSnappyBetterBlockAsm emit_remainder_encodeSnappyBetterBlockAsm: @@ -15135,7 +15121,6 @@ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm64K: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm64K - JZ match_nolit_end_encodeSnappyBetterBlockAsm64K matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K: CMPL DI, $0x04 @@ -15143,21 +15128,21 @@ matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm64K: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm64K - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm64K: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K + JB match_nolit_end_encodeSnappyBetterBlockAsm64K MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBetterBlockAsm64K matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm64K: - CMPL DI, $0x01 - JB match_nolit_end_encodeSnappyBetterBlockAsm64K MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeSnappyBetterBlockAsm64K @@ -15363,24 +15348,26 @@ match_nolit_dst_ok_encodeSnappyBetterBlockAsm64K: MOVL R8, 24(SP)(R11*4) MOVL DI, 262168(SP)(R10*4) MOVL R13, 262168(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeSnappyBetterBlockAsm64K: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeSnappyBetterBlockAsm64K - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x08, DI - IMULQ BX, DI - SHRQ $0x30, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x08, R9 IMULQ BX, R9 SHRQ $0x30, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x08, R10 + IMULQ BX, R10 + SHRQ $0x30, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeSnappyBetterBlockAsm64K emit_remainder_encodeSnappyBetterBlockAsm64K: @@ -15692,7 +15679,6 @@ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm12B: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm12B - JZ match_nolit_end_encodeSnappyBetterBlockAsm12B matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B: CMPL DI, $0x04 @@ -15700,21 +15686,21 @@ matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm12B: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm12B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm12B: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B + JB match_nolit_end_encodeSnappyBetterBlockAsm12B MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBetterBlockAsm12B matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm12B: - CMPL DI, $0x01 - JB match_nolit_end_encodeSnappyBetterBlockAsm12B MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeSnappyBetterBlockAsm12B @@ -15920,24 +15906,26 @@ match_nolit_dst_ok_encodeSnappyBetterBlockAsm12B: MOVL R8, 24(SP)(R11*4) MOVL DI, 65560(SP)(R10*4) MOVL R13, 65560(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeSnappyBetterBlockAsm12B: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeSnappyBetterBlockAsm12B - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x10, DI - IMULQ BX, DI - SHRQ $0x32, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x10, R9 IMULQ BX, R9 SHRQ $0x32, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x10, R10 + IMULQ BX, R10 + SHRQ $0x32, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeSnappyBetterBlockAsm12B emit_remainder_encodeSnappyBetterBlockAsm12B: @@ -16249,7 +16237,6 @@ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm10B: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm10B - JZ match_nolit_end_encodeSnappyBetterBlockAsm10B matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B: CMPL DI, $0x04 @@ -16257,21 +16244,21 @@ matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm10B: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm10B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm10B: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B + JB match_nolit_end_encodeSnappyBetterBlockAsm10B MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBetterBlockAsm10B matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm10B: - CMPL DI, $0x01 - JB match_nolit_end_encodeSnappyBetterBlockAsm10B MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeSnappyBetterBlockAsm10B @@ -16477,24 +16464,26 @@ match_nolit_dst_ok_encodeSnappyBetterBlockAsm10B: MOVL R8, 24(SP)(R11*4) MOVL DI, 16408(SP)(R10*4) MOVL R13, 16408(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeSnappyBetterBlockAsm10B: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeSnappyBetterBlockAsm10B - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x10, DI - IMULQ BX, DI - SHRQ $0x34, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x10, R9 IMULQ BX, R9 SHRQ $0x34, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x10, R10 + IMULQ BX, R10 + SHRQ $0x34, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeSnappyBetterBlockAsm10B emit_remainder_encodeSnappyBetterBlockAsm10B: @@ -16806,7 +16795,6 @@ matchlen_loop_match_nolit_encodeSnappyBetterBlockAsm8B: LEAL 8(R11), R11 CMPL DI, $0x08 JAE matchlen_loopback_match_nolit_encodeSnappyBetterBlockAsm8B - JZ match_nolit_end_encodeSnappyBetterBlockAsm8B matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B: CMPL DI, $0x04 @@ -16814,21 +16802,21 @@ matchlen_match4_match_nolit_encodeSnappyBetterBlockAsm8B: MOVL (R8)(R11*1), R10 CMPL (R9)(R11*1), R10 JNE matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm8B - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R11), R11 matchlen_match2_match_nolit_encodeSnappyBetterBlockAsm8B: - CMPL DI, $0x02 - JB matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B + CMPL DI, $0x01 + JE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B + JB match_nolit_end_encodeSnappyBetterBlockAsm8B MOVW (R8)(R11*1), R10 CMPW (R9)(R11*1), R10 JNE matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B - SUBL $0x02, DI LEAL 2(R11), R11 + SUBL $0x02, DI + JZ match_nolit_end_encodeSnappyBetterBlockAsm8B matchlen_match1_match_nolit_encodeSnappyBetterBlockAsm8B: - CMPL DI, $0x01 - JB match_nolit_end_encodeSnappyBetterBlockAsm8B MOVB (R8)(R11*1), R10 CMPB (R9)(R11*1), R10 JNE match_nolit_end_encodeSnappyBetterBlockAsm8B @@ -17032,24 +17020,26 @@ match_nolit_dst_ok_encodeSnappyBetterBlockAsm8B: MOVL R8, 24(SP)(R11*4) MOVL DI, 4120(SP)(R10*4) MOVL R13, 4120(SP)(R12*4) + LEAQ 1(R8)(SI*1), DI + SHRQ $0x01, DI ADDQ $0x01, SI SUBQ $0x01, R8 index_loop_encodeSnappyBetterBlockAsm8B: - CMPQ SI, R8 + CMPQ DI, R8 JAE search_loop_encodeSnappyBetterBlockAsm8B - MOVQ (DX)(SI*1), DI - MOVQ (DX)(R8*1), R9 - SHLQ $0x10, DI - IMULQ BX, DI - SHRQ $0x36, DI + MOVQ (DX)(SI*1), R9 + MOVQ (DX)(DI*1), R10 SHLQ $0x10, R9 IMULQ BX, R9 SHRQ $0x36, R9 - MOVL SI, 24(SP)(DI*4) - MOVL R8, 24(SP)(R9*4) + SHLQ $0x10, R10 + IMULQ BX, R10 + SHRQ $0x36, R10 + MOVL SI, 24(SP)(R9*4) + MOVL DI, 24(SP)(R10*4) ADDQ $0x02, SI - SUBQ $0x02, R8 + ADDQ $0x02, DI JMP index_loop_encodeSnappyBetterBlockAsm8B emit_remainder_encodeSnappyBetterBlockAsm8B: @@ -17378,7 +17368,6 @@ matchlen_loop_repeat_extend_calcBlockSize: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_calcBlockSize - JZ repeat_extend_forward_end_calcBlockSize matchlen_match4_repeat_extend_calcBlockSize: CMPL DI, $0x04 @@ -17386,21 +17375,21 @@ matchlen_match4_repeat_extend_calcBlockSize: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_calcBlockSize - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_calcBlockSize: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_calcBlockSize + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_calcBlockSize + JB repeat_extend_forward_end_calcBlockSize MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_calcBlockSize - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_calcBlockSize matchlen_match1_repeat_extend_calcBlockSize: - CMPL DI, $0x01 - JB repeat_extend_forward_end_calcBlockSize MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_calcBlockSize @@ -17590,7 +17579,6 @@ matchlen_loop_match_nolit_calcBlockSize: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_calcBlockSize - JZ match_nolit_end_calcBlockSize matchlen_match4_match_nolit_calcBlockSize: CMPL SI, $0x04 @@ -17598,21 +17586,21 @@ matchlen_match4_match_nolit_calcBlockSize: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_calcBlockSize - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_calcBlockSize: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_calcBlockSize + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_calcBlockSize + JB match_nolit_end_calcBlockSize MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_calcBlockSize - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_calcBlockSize matchlen_match1_match_nolit_calcBlockSize: - CMPL SI, $0x01 - JB match_nolit_end_calcBlockSize MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_calcBlockSize @@ -17909,7 +17897,6 @@ matchlen_loop_repeat_extend_calcBlockSizeSmall: LEAL 8(R10), R10 CMPL DI, $0x08 JAE matchlen_loopback_repeat_extend_calcBlockSizeSmall - JZ repeat_extend_forward_end_calcBlockSizeSmall matchlen_match4_repeat_extend_calcBlockSizeSmall: CMPL DI, $0x04 @@ -17917,21 +17904,21 @@ matchlen_match4_repeat_extend_calcBlockSizeSmall: MOVL (R8)(R10*1), R9 CMPL (BX)(R10*1), R9 JNE matchlen_match2_repeat_extend_calcBlockSizeSmall - SUBL $0x04, DI + LEAL -4(DI), DI LEAL 4(R10), R10 matchlen_match2_repeat_extend_calcBlockSizeSmall: - CMPL DI, $0x02 - JB matchlen_match1_repeat_extend_calcBlockSizeSmall + CMPL DI, $0x01 + JE matchlen_match1_repeat_extend_calcBlockSizeSmall + JB repeat_extend_forward_end_calcBlockSizeSmall MOVW (R8)(R10*1), R9 CMPW (BX)(R10*1), R9 JNE matchlen_match1_repeat_extend_calcBlockSizeSmall - SUBL $0x02, DI LEAL 2(R10), R10 + SUBL $0x02, DI + JZ repeat_extend_forward_end_calcBlockSizeSmall matchlen_match1_repeat_extend_calcBlockSizeSmall: - CMPL DI, $0x01 - JB repeat_extend_forward_end_calcBlockSizeSmall MOVB (R8)(R10*1), R9 CMPB (BX)(R10*1), R9 JNE repeat_extend_forward_end_calcBlockSizeSmall @@ -18091,7 +18078,6 @@ matchlen_loop_match_nolit_calcBlockSizeSmall: LEAL 8(R9), R9 CMPL SI, $0x08 JAE matchlen_loopback_match_nolit_calcBlockSizeSmall - JZ match_nolit_end_calcBlockSizeSmall matchlen_match4_match_nolit_calcBlockSizeSmall: CMPL SI, $0x04 @@ -18099,21 +18085,21 @@ matchlen_match4_match_nolit_calcBlockSizeSmall: MOVL (DI)(R9*1), R8 CMPL (BX)(R9*1), R8 JNE matchlen_match2_match_nolit_calcBlockSizeSmall - SUBL $0x04, SI + LEAL -4(SI), SI LEAL 4(R9), R9 matchlen_match2_match_nolit_calcBlockSizeSmall: - CMPL SI, $0x02 - JB matchlen_match1_match_nolit_calcBlockSizeSmall + CMPL SI, $0x01 + JE matchlen_match1_match_nolit_calcBlockSizeSmall + JB match_nolit_end_calcBlockSizeSmall MOVW (DI)(R9*1), R8 CMPW (BX)(R9*1), R8 JNE matchlen_match1_match_nolit_calcBlockSizeSmall - SUBL $0x02, SI LEAL 2(R9), R9 + SUBL $0x02, SI + JZ match_nolit_end_calcBlockSizeSmall matchlen_match1_match_nolit_calcBlockSizeSmall: - CMPL SI, $0x01 - JB match_nolit_end_calcBlockSizeSmall MOVB (DI)(R9*1), R8 CMPB (BX)(R9*1), R8 JNE match_nolit_end_calcBlockSizeSmall @@ -18879,7 +18865,6 @@ matchlen_loop_standalone: LEAL 8(SI), SI CMPL DX, $0x08 JAE matchlen_loopback_standalone - JZ gen_match_len_end matchlen_match4_standalone: CMPL DX, $0x04 @@ -18887,21 +18872,21 @@ matchlen_match4_standalone: MOVL (AX)(SI*1), BX CMPL (CX)(SI*1), BX JNE matchlen_match2_standalone - SUBL $0x04, DX + LEAL -4(DX), DX LEAL 4(SI), SI matchlen_match2_standalone: - CMPL DX, $0x02 - JB matchlen_match1_standalone + CMPL DX, $0x01 + JE matchlen_match1_standalone + JB gen_match_len_end MOVW (AX)(SI*1), BX CMPW (CX)(SI*1), BX JNE matchlen_match1_standalone - SUBL $0x02, DX LEAL 2(SI), SI + SUBL $0x02, DX + JZ gen_match_len_end matchlen_match1_standalone: - CMPL DX, $0x01 - JB gen_match_len_end MOVB (AX)(SI*1), BL CMPB (CX)(SI*1), BL JNE gen_match_len_end diff --git a/vendor/github.com/klauspost/compress/s2/reader.go b/vendor/github.com/klauspost/compress/s2/reader.go index 8b84baa6d..2f01a3987 100644 --- a/vendor/github.com/klauspost/compress/s2/reader.go +++ b/vendor/github.com/klauspost/compress/s2/reader.go @@ -147,6 +147,13 @@ type Reader struct { ignoreCRC bool } +// GetBufferCapacity returns the capacity of the internal buffer. +// This might be useful to know when reusing the same reader in combination +// with the lazy buffer option. +func (r *Reader) GetBufferCapacity() int { + return cap(r.buf) +} + // ensureBufferSize will ensure that the buffer can take at least n bytes. // If false is returned the buffer exceeds maximum allowed size. func (r *Reader) ensureBufferSize(n int) bool { diff --git a/vendor/github.com/klauspost/compress/s2/writer.go b/vendor/github.com/klauspost/compress/s2/writer.go index 5a944068c..089cd36d8 100644 --- a/vendor/github.com/klauspost/compress/s2/writer.go +++ b/vendor/github.com/klauspost/compress/s2/writer.go @@ -771,7 +771,7 @@ func (w *Writer) closeIndex(idx bool) ([]byte, error) { } var index []byte - if w.err(nil) == nil && w.writer != nil { + if w.err(err) == nil && w.writer != nil { // Create index. if idx { compSize := int64(-1) diff --git a/vendor/github.com/nats-io/nats-server/v2/conf/parse.go b/vendor/github.com/nats-io/nats-server/v2/conf/parse.go index 612b16438..4d2a20ade 100644 --- a/vendor/github.com/nats-io/nats-server/v2/conf/parse.go +++ b/vendor/github.com/nats-io/nats-server/v2/conf/parse.go @@ -146,7 +146,9 @@ func parse(data, fp string, pedantic bool) (p *parser, err error) { return nil, err } } - + if len(p.mapping) == 0 { + return nil, fmt.Errorf("config has no values or is empty") + } return p, nil } diff --git a/vendor/github.com/nats-io/nats-server/v2/logger/log.go b/vendor/github.com/nats-io/nats-server/v2/logger/log.go index 57cab62f7..b27169673 100644 --- a/vendor/github.com/nats-io/nats-server/v2/logger/log.go +++ b/vendor/github.com/nats-io/nats-server/v2/logger/log.go @@ -38,13 +38,37 @@ type Logger struct { fl *fileLogger } -// NewStdLogger creates a logger with output directed to Stderr -func NewStdLogger(time, debug, trace, colors, pid bool) *Logger { +type LogOption interface { + isLoggerOption() +} + +// LogUTC controls whether timestamps in the log output should be UTC or local time. +type LogUTC bool + +func (l LogUTC) isLoggerOption() {} + +func logFlags(time bool, opts ...LogOption) int { flags := 0 if time { flags = log.LstdFlags | log.Lmicroseconds } + for _, opt := range opts { + switch v := opt.(type) { + case LogUTC: + if time && bool(v) { + flags |= log.LUTC + } + } + } + + return flags +} + +// NewStdLogger creates a logger with output directed to Stderr +func NewStdLogger(time, debug, trace, colors, pid bool, opts ...LogOption) *Logger { + flags := logFlags(time, opts...) + pre := "" if pid { pre = pidPrefix() @@ -66,11 +90,8 @@ func NewStdLogger(time, debug, trace, colors, pid bool) *Logger { } // NewFileLogger creates a logger with output directed to a file -func NewFileLogger(filename string, time, debug, trace, pid bool) *Logger { - flags := 0 - if time { - flags = log.LstdFlags | log.Lmicroseconds - } +func NewFileLogger(filename string, time, debug, trace, pid bool, opts ...LogOption) *Logger { + flags := logFlags(time, opts...) pre := "" if pid { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certidp/certidp.go b/vendor/github.com/nats-io/nats-server/v2/server/certidp/certidp.go new file mode 100644 index 000000000..f7b660dff --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certidp/certidp.go @@ -0,0 +1,297 @@ +// Copyright 2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certidp + +import ( + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/json" + "fmt" + "net/url" + "strings" + "time" + + "golang.org/x/crypto/ocsp" +) + +const ( + DefaultAllowedClockSkew = 30 * time.Second + DefaultOCSPResponderTimeout = 2 * time.Second + DefaultTTLUnsetNextUpdate = 1 * time.Hour +) + +type StatusAssertion int + +var ( + StatusAssertionStrToVal = map[string]StatusAssertion{ + "good": ocsp.Good, + "revoked": ocsp.Revoked, + "unknown": ocsp.Unknown, + } + StatusAssertionValToStr = map[StatusAssertion]string{ + ocsp.Good: "good", + ocsp.Revoked: "revoked", + ocsp.Unknown: "unknown", + } + StatusAssertionIntToVal = map[int]StatusAssertion{ + 0: ocsp.Good, + 1: ocsp.Revoked, + 2: ocsp.Unknown, + } +) + +func GetStatusAssertionStr(sa int) string { + return StatusAssertionValToStr[StatusAssertionIntToVal[sa]] +} + +func (sa StatusAssertion) MarshalJSON() ([]byte, error) { + str, ok := StatusAssertionValToStr[sa] + if !ok { + // set unknown as fallback + str = StatusAssertionValToStr[ocsp.Unknown] + } + return json.Marshal(str) +} + +func (sa *StatusAssertion) UnmarshalJSON(in []byte) error { + v, ok := StatusAssertionStrToVal[strings.ReplaceAll(string(in), "\"", "")] + if !ok { + // set unknown as fallback + v = StatusAssertionStrToVal["unknown"] + } + *sa = v + return nil +} + +type ChainLink struct { + Leaf *x509.Certificate + Issuer *x509.Certificate + OCSPWebEndpoints *[]*url.URL +} + +// OCSPPeerConfig holds the parsed OCSP peer configuration section of TLS configuration +type OCSPPeerConfig struct { + Verify bool + Timeout float64 + ClockSkew float64 + WarnOnly bool + UnknownIsGood bool + AllowWhenCAUnreachable bool + TTLUnsetNextUpdate float64 +} + +func NewOCSPPeerConfig() *OCSPPeerConfig { + return &OCSPPeerConfig{ + Verify: false, + Timeout: DefaultOCSPResponderTimeout.Seconds(), + ClockSkew: DefaultAllowedClockSkew.Seconds(), + WarnOnly: false, + UnknownIsGood: false, + AllowWhenCAUnreachable: false, + TTLUnsetNextUpdate: DefaultTTLUnsetNextUpdate.Seconds(), + } +} + +// Log is a neutral method of passing server loggers to plugins +type Log struct { + Debugf func(format string, v ...interface{}) + Noticef func(format string, v ...interface{}) + Warnf func(format string, v ...interface{}) + Errorf func(format string, v ...interface{}) + Tracef func(format string, v ...interface{}) +} + +type CertInfo struct { + Subject string `json:"subject,omitempty"` + Issuer string `json:"issuer,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + Raw []byte `json:"raw,omitempty"` +} + +var OCSPPeerUsage = ` +For client, leaf spoke (remotes), and leaf hub connections, you may enable OCSP peer validation: + + tls { + ... + # mTLS must be enabled (with exception of Leaf remotes) + verify: true + ... + # short form enables peer verify and takes option defaults + ocsp_peer: true + + # long form includes settable options + ocsp_peer { + # Enable OCSP peer validation (default false) + verify: true + + # OCSP responder timeout in seconds (may be fractional, default 2 seconds) + ca_timeout: 2 + + # Allowed skew between server and OCSP responder time in seconds (may be fractional, default 30 seconds) + allowed_clockskew: 30 + + # Warn-only and never reject connections (default false) + warn_only: false + + # Treat response Unknown status as valid certificate (default false) + unknown_is_good: false + + # Warn-only if no CA response can be obtained and no cached revocation exists (default false) + allow_when_ca_unreachable: false + + # If response NextUpdate unset by CA, set a default cache TTL in seconds from ThisUpdate (default 1 hour) + cache_ttl_when_next_update_unset: 3600 + } + ... + } + +Note: OCSP validation for route and gateway connections is enabled using the 'ocsp' configuration option. +` + +// GenerateFingerprint returns a base64-encoded SHA256 hash of the raw certificate +func GenerateFingerprint(cert *x509.Certificate) string { + data := sha256.Sum256(cert.Raw) + return base64.StdEncoding.EncodeToString(data[:]) +} + +func getWebEndpoints(uris []string) []*url.URL { + var urls []*url.URL + for _, uri := range uris { + endpoint, err := url.ParseRequestURI(uri) + if err != nil { + // skip invalid URLs + continue + } + if endpoint.Scheme != "http" && endpoint.Scheme != "https" { + // skip non-web URLs + continue + } + urls = append(urls, endpoint) + } + return urls +} + +// GetSubjectDNForm returns RDN sequence concatenation of the certificate's subject to be +// used in logs, events, etc. Should never be used for reliable cache matching or other crypto purposes. +func GetSubjectDNForm(cert *x509.Certificate) string { + if cert == nil { + return "" + } + return strings.TrimSuffix(fmt.Sprintf("%s+", cert.Subject.ToRDNSequence()), "+") +} + +// GetIssuerDNForm returns RDN sequence concatenation of the certificate's issuer to be +// used in logs, events, etc. Should never be used for reliable cache matching or other crypto purposes. +func GetIssuerDNForm(cert *x509.Certificate) string { + if cert == nil { + return "" + } + return strings.TrimSuffix(fmt.Sprintf("%s+", cert.Issuer.ToRDNSequence()), "+") +} + +// CertOCSPEligible checks if the certificate's issuer has populated AIA with OCSP responder endpoint(s) +// and is thus eligible for OCSP validation +func CertOCSPEligible(link *ChainLink) bool { + if link == nil || link.Leaf.Raw == nil || len(link.Leaf.Raw) == 0 { + return false + } + if link.Leaf.OCSPServer == nil || len(link.Leaf.OCSPServer) == 0 { + return false + } + urls := getWebEndpoints(link.Leaf.OCSPServer) + if len(urls) == 0 { + return false + } + link.OCSPWebEndpoints = &urls + return true +} + +// GetLeafIssuerCert returns the issuer certificate of the leaf (positional) certificate in the chain +func GetLeafIssuerCert(chain []*x509.Certificate, leafPos int) *x509.Certificate { + if len(chain) == 0 || leafPos < 0 { + return nil + } + // self-signed certificate or too-big leafPos + if leafPos >= len(chain)-1 { + return nil + } + // returns pointer to issuer cert or nil + return (chain)[leafPos+1] +} + +// OCSPResponseCurrent checks if the OCSP response is current (i.e. not expired and not future effective) +func OCSPResponseCurrent(ocspr *ocsp.Response, opts *OCSPPeerConfig, log *Log) bool { + skew := time.Duration(opts.ClockSkew * float64(time.Second)) + if skew < 0*time.Second { + skew = DefaultAllowedClockSkew + } + now := time.Now().UTC() + // Typical effectivity check based on CA response ThisUpdate and NextUpdate semantics + if !ocspr.NextUpdate.IsZero() && ocspr.NextUpdate.Before(now.Add(-1*skew)) { + t := ocspr.NextUpdate.Format(time.RFC3339Nano) + nt := now.Format(time.RFC3339Nano) + log.Debugf(DbgResponseExpired, t, nt, skew) + return false + } + // CA responder can assert NextUpdate unset, in which case use config option to set a default cache TTL + if ocspr.NextUpdate.IsZero() { + ttl := time.Duration(opts.TTLUnsetNextUpdate * float64(time.Second)) + if ttl < 0*time.Second { + ttl = DefaultTTLUnsetNextUpdate + } + expiryTime := ocspr.ThisUpdate.Add(ttl) + if expiryTime.Before(now.Add(-1 * skew)) { + t := expiryTime.Format(time.RFC3339Nano) + nt := now.Format(time.RFC3339Nano) + log.Debugf(DbgResponseTTLExpired, t, nt, skew) + return false + } + } + if ocspr.ThisUpdate.After(now.Add(skew)) { + t := ocspr.ThisUpdate.Format(time.RFC3339Nano) + nt := now.Format(time.RFC3339Nano) + log.Debugf(DbgResponseFutureDated, t, nt, skew) + return false + } + return true +} + +// ValidDelegationCheck checks if the CA OCSP Response was signed by a valid CA Issuer delegate as per (RFC 6960, section 4.2.2.2) +// If a valid delegate or direct-signed by CA Issuer, true returned. +func ValidDelegationCheck(iss *x509.Certificate, ocspr *ocsp.Response) bool { + // This call assumes prior successful parse and signature validation of the OCSP response + // The Go OCSP library (as of x/crypto/ocsp v0.9) will detect and perform a 1-level delegate signature check but does not + // implement the additional criteria for delegation specified in RFC 6960, section 4.2.2.2. + if iss == nil || ocspr == nil { + return false + } + // not a delegation, no-op + if ocspr.Certificate == nil { + return true + } + // delegate is self-same with CA Issuer, not a delegation although response issued in that form + if ocspr.Certificate.Equal(iss) { + return true + } + // we need to verify CA Issuer stamped id-kp-OCSPSigning on delegate + delegatedSigner := false + for _, keyUseExt := range ocspr.Certificate.ExtKeyUsage { + if keyUseExt == x509.ExtKeyUsageOCSPSigning { + delegatedSigner = true + break + } + } + return delegatedSigner +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certidp/messages.go b/vendor/github.com/nats-io/nats-server/v2/server/certidp/messages.go new file mode 100644 index 000000000..52a799ac8 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certidp/messages.go @@ -0,0 +1,106 @@ +// Copyright 2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certidp + +var ( + // Returned errors + ErrIllegalPeerOptsConfig = "expected map to define OCSP peer options, got [%T]" + ErrIllegalCacheOptsConfig = "expected map to define OCSP peer cache options, got [%T]" + ErrParsingPeerOptFieldGeneric = "error parsing tls peer config, unknown field [%q]" + ErrParsingPeerOptFieldTypeConversion = "error parsing tls peer config, conversion error: %s" + ErrParsingCacheOptFieldTypeConversion = "error parsing OCSP peer cache config, conversion error: %s" + ErrUnableToPlugTLSEmptyConfig = "unable to plug TLS verify connection, config is nil" + ErrMTLSRequired = "OCSP peer verification for client connections requires TLS verify (mTLS) to be enabled" + ErrUnableToPlugTLSClient = "unable to register client OCSP verification" + ErrUnableToPlugTLSServer = "unable to register server OCSP verification" + ErrCannotWriteCompressed = "error writing to compression writer: %w" + ErrCannotReadCompressed = "error reading compression reader: %w" + ErrTruncatedWrite = "short write on body (%d != %d)" + ErrCannotCloseWriter = "error closing compression writer: %w" + ErrParsingCacheOptFieldGeneric = "error parsing OCSP peer cache config, unknown field [%q]" + ErrUnknownCacheType = "error parsing OCSP peer cache config, unknown type [%s]" + ErrInvalidChainlink = "invalid chain link" + ErrBadResponderHTTPStatus = "bad OCSP responder http status: [%d]" + ErrNoAvailOCSPServers = "no available OCSP servers" + ErrFailedWithAllRequests = "exhausted OCSP responders: %w" + + // Direct logged errors + ErrLoadCacheFail = "Unable to load OCSP peer cache: %s" + ErrSaveCacheFail = "Unable to save OCSP peer cache: %s" + ErrBadCacheTypeConfig = "Unimplemented OCSP peer cache type [%v]" + ErrResponseCompressFail = "Unable to compress OCSP response for key [%s]: %s" + ErrResponseDecompressFail = "Unable to decompress OCSP response for key [%s]: %s" + ErrPeerEmptyNoEvent = "Peer certificate is nil, cannot send OCSP peer reject event" + ErrPeerEmptyAutoReject = "Peer certificate is nil, rejecting OCSP peer" + + // Debug information + DbgPlugTLSForKind = "Plugging TLS OCSP peer for [%s]" + DbgNumServerChains = "Peer OCSP enabled: %d TLS server chain(s) will be evaluated" + DbgNumClientChains = "Peer OCSP enabled: %d TLS client chain(s) will be evaluated" + DbgLinksInChain = "Chain [%d]: %d total link(s)" + DbgSelfSignedValid = "Chain [%d] is self-signed, thus peer is valid" + DbgValidNonOCSPChain = "Chain [%d] has no OCSP eligible links, thus peer is valid" + DbgChainIsOCSPEligible = "Chain [%d] has %d OCSP eligible link(s)" + DbgChainIsOCSPValid = "Chain [%d] is OCSP valid for all eligible links, thus peer is valid" + DbgNoOCSPValidChains = "No OCSP valid chains, thus peer is invalid" + DbgCheckingCacheForCert = "Checking OCSP peer cache for [%s], key [%s]" + DbgCurrentResponseCached = "Cached OCSP response is current, status [%s]" + DbgExpiredResponseCached = "Cached OCSP response is expired, status [%s]" + DbgOCSPValidPeerLink = "OCSP verify pass for [%s]" + DbgCachingResponse = "Caching OCSP response for [%s], key [%s]" + DbgAchievedCompression = "OCSP response compression ratio: [%f]" + DbgCacheHit = "OCSP peer cache hit for key [%s]" + DbgCacheMiss = "OCSP peer cache miss for key [%s]" + DbgPreservedRevocation = "Revoked OCSP response for key [%s] preserved by cache policy" + DbgDeletingCacheResponse = "Deleting OCSP peer cached response for key [%s]" + DbgStartingCache = "Starting OCSP peer cache" + DbgStoppingCache = "Stopping OCSP peer cache" + DbgLoadingCache = "Loading OCSP peer cache [%s]" + DbgNoCacheFound = "No OCSP peer cache found, starting with empty cache" + DbgSavingCache = "Saving OCSP peer cache [%s]" + DbgCacheSaved = "Saved OCSP peer cache successfully (%d bytes)" + DbgMakingCARequest = "Trying OCSP responder url [%s]" + DbgResponseExpired = "OCSP response NextUpdate [%s] is before now [%s] with clockskew [%s]" + DbgResponseTTLExpired = "OCSP response cache expiry [%s] is before now [%s] with clockskew [%s]" + DbgResponseFutureDated = "OCSP response ThisUpdate [%s] is before now [%s] with clockskew [%s]" + DbgCacheSaveTimerExpired = "OCSP peer cache save timer expired" + DbgCacheDirtySave = "OCSP peer cache is dirty, saving" + + // Returned to peer as TLS reject reason + MsgTLSClientRejectConnection = "client not OCSP valid" + MsgTLSServerRejectConnection = "server not OCSP valid" + + // Expected runtime errors (direct logged) + ErrCAResponderCalloutFail = "Attempt to obtain OCSP response from CA responder for [%s] failed: %s" + ErrNewCAResponseNotCurrent = "New OCSP CA response obtained for [%s] but not current" + ErrCAResponseParseFailed = "Could not parse OCSP CA response for [%s]: %s" + ErrOCSPInvalidPeerLink = "OCSP verify fail for [%s] with CA status [%s]" + + // Policy override warnings (direct logged) + MsgAllowWhenCAUnreachableOccurred = "Failed to obtain OCSP CA response for [%s] but AllowWhenCAUnreachable set; no cached revocation so allowing" + MsgAllowWhenCAUnreachableOccurredCachedRevoke = "Failed to obtain OCSP CA response for [%s] but AllowWhenCAUnreachable set; cached revocation exists so rejecting" + MsgAllowWarnOnlyOccurred = "OCSP verify fail for [%s] but WarnOnly is true so allowing" + + // Info (direct logged) + MsgCacheOnline = "OCSP peer cache online, type [%s]" + MsgCacheOffline = "OCSP peer cache offline, type [%s]" + + // OCSP cert invalid reasons (debug and event reasons) + MsgFailedOCSPResponseFetch = "Failed OCSP response fetch" + MsgOCSPResponseNotEffective = "OCSP response not in effectivity window" + MsgFailedOCSPResponseParse = "Failed OCSP response parse" + MsgOCSPResponseInvalidStatus = "Invalid OCSP response status: %s" + MsgOCSPResponseDelegationInvalid = "Invalid OCSP response delegation: %s" + MsgCachedOCSPResponseInvalid = "Invalid cached OCSP response for [%s] with fingerprint [%s]" +) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certidp/ocsp_responder.go b/vendor/github.com/nats-io/nats-server/v2/server/certidp/ocsp_responder.go new file mode 100644 index 000000000..6e210f2b5 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certidp/ocsp_responder.go @@ -0,0 +1,83 @@ +// Copyright 2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certidp + +import ( + "encoding/base64" + "fmt" + "io" + "net/http" + "strings" + "time" + + "golang.org/x/crypto/ocsp" +) + +func FetchOCSPResponse(link *ChainLink, opts *OCSPPeerConfig, log *Log) ([]byte, error) { + if link == nil || link.Leaf == nil || link.Issuer == nil || opts == nil || log == nil { + return nil, fmt.Errorf(ErrInvalidChainlink) + } + + timeout := time.Duration(opts.Timeout * float64(time.Second)) + if timeout <= 0*time.Second { + timeout = DefaultOCSPResponderTimeout + } + + getRequestBytes := func(u string, hc *http.Client) ([]byte, error) { + resp, err := hc.Get(u) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf(ErrBadResponderHTTPStatus, resp.StatusCode) + } + return io.ReadAll(resp.Body) + } + + // Request documentation: + // https://tools.ietf.org/html/rfc6960#appendix-A.1 + + reqDER, err := ocsp.CreateRequest(link.Leaf, link.Issuer, nil) + if err != nil { + return nil, err + } + + reqEnc := base64.StdEncoding.EncodeToString(reqDER) + + responders := *link.OCSPWebEndpoints + + if len(responders) == 0 { + return nil, fmt.Errorf(ErrNoAvailOCSPServers) + } + + var raw []byte + hc := &http.Client{ + Timeout: timeout, + } + for _, u := range responders { + url := u.String() + log.Debugf(DbgMakingCARequest, url) + url = strings.TrimSuffix(url, "/") + raw, err = getRequestBytes(fmt.Sprintf("%s/%s", url, reqEnc), hc) + if err == nil { + break + } + } + if err != nil { + return nil, fmt.Errorf(ErrFailedWithAllRequests, err) + } + + return raw, nil +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore.go b/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore.go new file mode 100644 index 000000000..3d7dfde60 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore.go @@ -0,0 +1,102 @@ +// Copyright 2022-2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certstore + +import ( + "crypto" + "crypto/x509" + "io" + "runtime" + "strings" +) + +type StoreType int + +const MATCHBYEMPTY = 0 +const STOREEMPTY = 0 + +const ( + windowsCurrentUser StoreType = iota + 1 + windowsLocalMachine +) + +var StoreMap = map[string]StoreType{ + "windowscurrentuser": windowsCurrentUser, + "windowslocalmachine": windowsLocalMachine, +} + +var StoreOSMap = map[StoreType]string{ + windowsCurrentUser: "windows", + windowsLocalMachine: "windows", +} + +type MatchByType int + +const ( + matchByIssuer MatchByType = iota + 1 + matchBySubject +) + +var MatchByMap = map[string]MatchByType{ + "issuer": matchByIssuer, + "subject": matchBySubject, +} + +var Usage = ` +In place of cert_file and key_file you may use the windows certificate store: + + tls { + cert_store: "WindowsCurrentUser" + cert_match_by: "Subject" + cert_match: "MyServer123" + } +` + +func ParseCertStore(certStore string) (StoreType, error) { + certStoreType, exists := StoreMap[strings.ToLower(certStore)] + if !exists { + return 0, ErrBadCertStore + } + validOS, exists := StoreOSMap[certStoreType] + if !exists || validOS != runtime.GOOS { + return 0, ErrOSNotCompatCertStore + } + return certStoreType, nil +} + +func ParseCertMatchBy(certMatchBy string) (MatchByType, error) { + certMatchByType, exists := MatchByMap[strings.ToLower(certMatchBy)] + if !exists { + return 0, ErrBadMatchByType + } + return certMatchByType, nil +} + +func GetLeafIssuer(leaf *x509.Certificate, vOpts x509.VerifyOptions) (issuer *x509.Certificate) { + chains, err := leaf.Verify(vOpts) + if err != nil || len(chains) == 0 { + issuer = nil + } else { + issuer = chains[0][1] + } + return +} + +// credential provides access to a public key and is a crypto.Signer. +type credential interface { + // Public returns the public key corresponding to the leaf certificate. + Public() crypto.PublicKey + // Sign signs digest with the private key. + Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_other.go b/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_other.go new file mode 100644 index 000000000..a72df834a --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_other.go @@ -0,0 +1,46 @@ +// Copyright 2022-2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !windows + +package certstore + +import ( + "crypto" + "crypto/tls" + "io" +) + +var _ = MATCHBYEMPTY + +// otherKey implements crypto.Signer and crypto.Decrypter to satisfy linter on platforms that don't implement certstore +type otherKey struct{} + +func TLSConfig(certStore StoreType, certMatchBy MatchByType, certMatch string, config *tls.Config) error { + _, _, _, _ = certStore, certMatchBy, certMatch, config + return ErrOSNotCompatCertStore +} + +// Public always returns nil public key since this is a stub on non-supported platform +func (k otherKey) Public() crypto.PublicKey { + return nil +} + +// Sign always returns a nil signature since this is a stub on non-supported platform +func (k otherKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { + _, _, _ = rand, digest, opts + return nil, nil +} + +// Verify interface conformance. +var _ credential = &otherKey{} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_windows.go b/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_windows.go new file mode 100644 index 000000000..57adc187a --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certstore/certstore_windows.go @@ -0,0 +1,827 @@ +// Copyright 2022-2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Adapted, updated, and enhanced from CertToStore, https://github.com/google/certtostore/releases/tag/v1.0.2 +// Apache License, Version 2.0, Copyright 2017 Google Inc. + +package certstore + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/binary" + "fmt" + "io" + "math/big" + "reflect" + "sync" + "syscall" + "unicode/utf16" + "unsafe" + + "golang.org/x/crypto/cryptobyte" + "golang.org/x/crypto/cryptobyte/asn1" + "golang.org/x/sys/windows" +) + +const ( + // wincrypt.h constants + winAcquireCached = 0x1 // CRYPT_ACQUIRE_CACHE_FLAG + winAcquireSilent = 0x40 // CRYPT_ACQUIRE_SILENT_FLAG + winAcquireOnlyNCryptKey = 0x40000 // CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG + winEncodingX509ASN = 1 // X509_ASN_ENCODING + winEncodingPKCS7 = 65536 // PKCS_7_ASN_ENCODING + winCertStoreProvSystem = 10 // CERT_STORE_PROV_SYSTEM + winCertStoreCurrentUser = uint32(winCertStoreCurrentUserID << winCompareShift) // CERT_SYSTEM_STORE_CURRENT_USER + winCertStoreLocalMachine = uint32(winCertStoreLocalMachineID << winCompareShift) // CERT_SYSTEM_STORE_LOCAL_MACHINE + winCertStoreCurrentUserID = 1 // CERT_SYSTEM_STORE_CURRENT_USER_ID + winCertStoreLocalMachineID = 2 // CERT_SYSTEM_STORE_LOCAL_MACHINE_ID + winInfoIssuerFlag = 4 // CERT_INFO_ISSUER_FLAG + winInfoSubjectFlag = 7 // CERT_INFO_SUBJECT_FLAG + winCompareNameStrW = 8 // CERT_COMPARE_NAME_STR_A + winCompareShift = 16 // CERT_COMPARE_SHIFT + + // Reference https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore + winFindIssuerStr = winCompareNameStrW< 1 { + chain = chain[:len(chain)-1] + } + + // For tls.Certificate.Certificate need a [][]byte from []*x509.Certificate + // Approximate capacity for efficiency + rawChain = make([][]byte, 0, len(chain)) + for _, link := range chain { + rawChain = append(rawChain, link.Raw) + } + + tlsCert := tls.Certificate{ + Certificate: rawChain, + PrivateKey: pk, + Leaf: leaf, + } + config.Certificates = []tls.Certificate{tlsCert} + + // note: pk is a windows pointer (not freed by Go) but needs to live the life of the server for Signing. + // The cert context (leafCtx) windows pointer must not be freed underneath the pk so also life of the server. + return nil +} + +// winWide returns a pointer to uint16 representing the equivalent +// to a Windows LPCWSTR. +func winWide(s string) *uint16 { + w := utf16.Encode([]rune(s)) + w = append(w, 0) + return &w[0] +} + +// winOpenProvider gets a provider handle for subsequent calls +func winOpenProvider(provider string) (uintptr, error) { + var hProv uintptr + pname := winWide(provider) + // Open the provider, the last parameter is not used + r, _, err := winNCryptOpenStorageProvider.Call(uintptr(unsafe.Pointer(&hProv)), uintptr(unsafe.Pointer(pname)), 0) + if r == 0 { + return hProv, nil + } + return hProv, fmt.Errorf("NCryptOpenStorageProvider returned %X: %v", r, err) +} + +// winFindCert wraps the CertFindCertificateInStore library call. Note that any cert context passed +// into prev will be freed. If no certificate was found, nil will be returned. +func winFindCert(store windows.Handle, enc, findFlags, findType uint32, para *uint16, prev *windows.CertContext) (*windows.CertContext, error) { + h, _, err := winCertFindCertificateInStore.Call( + uintptr(store), + uintptr(enc), + uintptr(findFlags), + uintptr(findType), + uintptr(unsafe.Pointer(para)), + uintptr(unsafe.Pointer(prev)), + ) + if h == 0 { + // Actual error, or simply not found? + if errno, ok := err.(syscall.Errno); ok && errno == winCryptENotFound { + return nil, ErrFailedCertSearch + } + return nil, ErrFailedCertSearch + } + // nolint:govet + return (*windows.CertContext)(unsafe.Pointer(h)), nil +} + +// winCertStore is a store implementation for the Windows Certificate Store +type winCertStore struct { + Prov uintptr + ProvName string + stores map[string]*winStoreHandle + mu sync.Mutex +} + +// winOpenCertStore creates a winCertStore +func winOpenCertStore(provider string) (*winCertStore, error) { + cngProv, err := winOpenProvider(provider) + if err != nil { + // pass through error from winOpenProvider + return nil, err + } + + wcs := &winCertStore{ + Prov: cngProv, + ProvName: provider, + stores: make(map[string]*winStoreHandle), + } + + return wcs, nil +} + +// winCertContextToX509 creates an x509.Certificate from a Windows cert context. +func winCertContextToX509(ctx *windows.CertContext) (*x509.Certificate, error) { + var der []byte + slice := (*reflect.SliceHeader)(unsafe.Pointer(&der)) + slice.Data = uintptr(unsafe.Pointer(ctx.EncodedCert)) + slice.Len = int(ctx.Length) + slice.Cap = int(ctx.Length) + return x509.ParseCertificate(der) +} + +// certByIssuer matches and returns the first certificate found by passed issuer. +// CertContext pointer returned allows subsequent key operations like Sign. Caller specifies +// current user's personal certs or local machine's personal certs using storeType. +// See CERT_FIND_ISSUER_STR description at https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore +func (w *winCertStore) certByIssuer(issuer string, storeType uint32) (*x509.Certificate, *windows.CertContext, error) { + return w.certSearch(winFindIssuerStr, issuer, winMyStore, storeType) +} + +// certBySubject matches and returns the first certificate found by passed subject field. +// CertContext pointer returned allows subsequent key operations like Sign. Caller specifies +// current user's personal certs or local machine's personal certs using storeType. +// See CERT_FIND_SUBJECT_STR description at https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore +func (w *winCertStore) certBySubject(subject string, storeType uint32) (*x509.Certificate, *windows.CertContext, error) { + return w.certSearch(winFindSubjectStr, subject, winMyStore, storeType) +} + +// certSearch is a helper function to lookup certificates based on search type and match value. +// store is used to specify which store to perform the lookup in (system or user). +func (w *winCertStore) certSearch(searchType uint32, matchValue string, searchRoot *uint16, store uint32) (*x509.Certificate, *windows.CertContext, error) { + // store handle to "MY" store + h, err := w.storeHandle(store, searchRoot) + if err != nil { + return nil, nil, err + } + + var prev *windows.CertContext + var cert *x509.Certificate + + i, err := windows.UTF16PtrFromString(matchValue) + if err != nil { + return nil, nil, ErrFailedCertSearch + } + + // pass 0 as the third parameter because it is not used + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376064(v=vs.85).aspx + nc, err := winFindCert(h, winEncodingX509ASN|winEncodingPKCS7, 0, searchType, i, prev) + if err != nil { + return nil, nil, err + } + if nc != nil { + // certificate found + prev = nc + + // Extract the DER-encoded certificate from the cert context + xc, err := winCertContextToX509(nc) + if err == nil { + cert = xc + } else { + return nil, nil, ErrFailedX509Extract + } + } else { + return nil, nil, ErrFailedCertSearch + } + + if cert == nil { + return nil, nil, ErrFailedX509Extract + } + + return cert, prev, nil +} + +type winStoreHandle struct { + handle *windows.Handle +} + +func winNewStoreHandle(provider uint32, store *uint16) (*winStoreHandle, error) { + var s winStoreHandle + if s.handle != nil { + return &s, nil + } + st, err := windows.CertOpenStore( + winCertStoreProvSystem, + 0, + 0, + provider, + uintptr(unsafe.Pointer(store))) + if err != nil { + return nil, ErrBadCryptoStoreProvider + } + s.handle = &st + return &s, nil +} + +// winKey implements crypto.Signer and crypto.Decrypter for key based operations. +type winKey struct { + handle uintptr + pub crypto.PublicKey + Container string + AlgorithmGroup string +} + +// Public exports a public key to implement crypto.Signer +func (k winKey) Public() crypto.PublicKey { + return k.pub +} + +// Sign returns the signature of a hash to implement crypto.Signer +func (k winKey) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + switch k.AlgorithmGroup { + case "ECDSA", "ECDH": + return winSignECDSA(k.handle, digest) + case "RSA": + hf := opts.HashFunc() + algID, ok := winAlgIDs[hf] + if !ok { + return nil, ErrBadRSAHashAlgorithm + } + switch opts.(type) { + case *rsa.PSSOptions: + return winSignRSAPSSPadding(k.handle, digest, algID) + default: + return winSignRSAPKCS1Padding(k.handle, digest, algID) + } + default: + return nil, ErrBadSigningAlgorithm + } +} + +func winSignECDSA(kh uintptr, digest []byte) ([]byte, error) { + var size uint32 + // Obtain the size of the signature + r, _, _ := winNCryptSignHash.Call( + kh, + 0, + uintptr(unsafe.Pointer(&digest[0])), + uintptr(len(digest)), + 0, + 0, + uintptr(unsafe.Pointer(&size)), + 0) + if r != 0 { + return nil, ErrStoreECDSASigningError + } + + // Obtain the signature data + buf := make([]byte, size) + r, _, _ = winNCryptSignHash.Call( + kh, + 0, + uintptr(unsafe.Pointer(&digest[0])), + uintptr(len(digest)), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(size), + uintptr(unsafe.Pointer(&size)), + 0) + if r != 0 { + return nil, ErrStoreECDSASigningError + } + if len(buf) != int(size) { + return nil, ErrStoreECDSASigningError + } + + return winPackECDSASigValue(bytes.NewReader(buf[:size]), len(digest)) +} + +func winPackECDSASigValue(r io.Reader, digestLength int) ([]byte, error) { + sigR := make([]byte, digestLength) + if _, err := io.ReadFull(r, sigR); err != nil { + return nil, ErrStoreECDSASigningError + } + + sigS := make([]byte, digestLength) + if _, err := io.ReadFull(r, sigS); err != nil { + return nil, ErrStoreECDSASigningError + } + + var b cryptobyte.Builder + b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1BigInt(new(big.Int).SetBytes(sigR)) + b.AddASN1BigInt(new(big.Int).SetBytes(sigS)) + }) + return b.Bytes() +} + +func winSignRSAPKCS1Padding(kh uintptr, digest []byte, algID *uint16) ([]byte, error) { + // PKCS#1 v1.5 padding for some TLS 1.2 + padInfo := winPKCS1PaddingInfo{pszAlgID: algID} + var size uint32 + // Obtain the size of the signature + r, _, _ := winNCryptSignHash.Call( + kh, + uintptr(unsafe.Pointer(&padInfo)), + uintptr(unsafe.Pointer(&digest[0])), + uintptr(len(digest)), + 0, + 0, + uintptr(unsafe.Pointer(&size)), + winBCryptPadPKCS1) + if r != 0 { + return nil, ErrStoreRSASigningError + } + + // Obtain the signature data + sig := make([]byte, size) + r, _, _ = winNCryptSignHash.Call( + kh, + uintptr(unsafe.Pointer(&padInfo)), + uintptr(unsafe.Pointer(&digest[0])), + uintptr(len(digest)), + uintptr(unsafe.Pointer(&sig[0])), + uintptr(size), + uintptr(unsafe.Pointer(&size)), + winBCryptPadPKCS1) + if r != 0 { + return nil, ErrStoreRSASigningError + } + + return sig[:size], nil +} + +func winSignRSAPSSPadding(kh uintptr, digest []byte, algID *uint16) ([]byte, error) { + // PSS padding for TLS 1.3 and some TLS 1.2 + padInfo := winPSSPaddingInfo{pszAlgID: algID, cbSalt: winBCryptPadPSSSalt} + + var size uint32 + // Obtain the size of the signature + r, _, _ := winNCryptSignHash.Call( + kh, + uintptr(unsafe.Pointer(&padInfo)), + uintptr(unsafe.Pointer(&digest[0])), + uintptr(len(digest)), + 0, + 0, + uintptr(unsafe.Pointer(&size)), + winBCryptPadPSS) + if r != 0 { + return nil, ErrStoreRSASigningError + } + + // Obtain the signature data + sig := make([]byte, size) + r, _, _ = winNCryptSignHash.Call( + kh, + uintptr(unsafe.Pointer(&padInfo)), + uintptr(unsafe.Pointer(&digest[0])), + uintptr(len(digest)), + uintptr(unsafe.Pointer(&sig[0])), + uintptr(size), + uintptr(unsafe.Pointer(&size)), + winBCryptPadPSS) + if r != 0 { + return nil, ErrStoreRSASigningError + } + + return sig[:size], nil +} + +// certKey wraps CryptAcquireCertificatePrivateKey. It obtains the CNG private +// key of a known certificate and returns a pointer to a winKey which implements +// both crypto.Signer. When a nil cert context is passed +// a nil key is intentionally returned, to model the expected behavior of a +// non-existent cert having no private key. +// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecertificateprivatekey +func (w *winCertStore) certKey(cert *windows.CertContext) (*winKey, error) { + // Return early if a nil cert was passed. + if cert == nil { + return nil, nil + } + var ( + kh uintptr + spec uint32 + mustFree int + ) + r, _, _ := winCryptAcquireCertificatePrivateKey.Call( + uintptr(unsafe.Pointer(cert)), + winAcquireCached|winAcquireSilent|winAcquireOnlyNCryptKey, + 0, // Reserved, must be null. + uintptr(unsafe.Pointer(&kh)), + uintptr(unsafe.Pointer(&spec)), + uintptr(unsafe.Pointer(&mustFree)), + ) + // If the function succeeds, the return value is nonzero (TRUE). + if r == 0 { + return nil, ErrNoPrivateKeyStoreRef + } + if mustFree != 0 { + return nil, ErrNoPrivateKeyStoreRef + } + if spec != winNcryptKeySpec { + return nil, ErrNoPrivateKeyStoreRef + } + + return winKeyMetadata(kh) +} + +func winKeyMetadata(kh uintptr) (*winKey, error) { + // uc is used to populate the unique container name attribute of the private key + uc, err := winGetPropertyStr(kh, winNCryptUniqueNameProperty) + if err != nil { + // unable to determine key unique name + return nil, ErrExtractingPrivateKeyMetadata + } + + alg, err := winGetPropertyStr(kh, winNCryptAlgorithmGroupProperty) + if err != nil { + // unable to determine key algorithm + return nil, ErrExtractingPrivateKeyMetadata + } + + var pub crypto.PublicKey + + switch alg { + case "ECDSA", "ECDH": + buf, err := winExport(kh, winBCryptECCPublicBlob) + if err != nil { + // failed to export ECC public key + return nil, ErrExtractingECCPublicKey + } + pub, err = unmarshalECC(buf, kh) + if err != nil { + return nil, ErrExtractingECCPublicKey + } + case "RSA": + buf, err := winExport(kh, winBCryptRSAPublicBlob) + if err != nil { + return nil, ErrExtractingRSAPublicKey + } + pub, err = winUnmarshalRSA(buf) + if err != nil { + return nil, ErrExtractingRSAPublicKey + } + default: + return nil, ErrBadPublicKeyAlgorithm + } + + return &winKey{handle: kh, pub: pub, Container: uc, AlgorithmGroup: alg}, nil +} + +func winGetProperty(kh uintptr, property *uint16) ([]byte, error) { + var strSize uint32 + r, _, _ := winNCryptGetProperty.Call( + kh, + uintptr(unsafe.Pointer(property)), + 0, + 0, + uintptr(unsafe.Pointer(&strSize)), + 0, + 0) + if r != 0 { + return nil, ErrExtractPropertyFromKey + } + + buf := make([]byte, strSize) + r, _, _ = winNCryptGetProperty.Call( + kh, + uintptr(unsafe.Pointer(property)), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(strSize), + uintptr(unsafe.Pointer(&strSize)), + 0, + 0) + if r != 0 { + return nil, ErrExtractPropertyFromKey + } + + return buf, nil +} + +func winGetPropertyStr(kh uintptr, property *uint16) (string, error) { + buf, err := winFnGetProperty(kh, property) + if err != nil { + return "", ErrExtractPropertyFromKey + } + uc := bytes.ReplaceAll(buf, []byte{0x00}, []byte("")) + return string(uc), nil +} + +func winExport(kh uintptr, blobType *uint16) ([]byte, error) { + var size uint32 + // When obtaining the size of a public key, most parameters are not required + r, _, _ := winNCryptExportKey.Call( + kh, + 0, + uintptr(unsafe.Pointer(blobType)), + 0, + 0, + 0, + uintptr(unsafe.Pointer(&size)), + 0) + if r != 0 { + return nil, ErrExtractingPublicKey + } + + // Place the exported key in buf now that we know the size required + buf := make([]byte, size) + r, _, _ = winNCryptExportKey.Call( + kh, + 0, + uintptr(unsafe.Pointer(blobType)), + 0, + uintptr(unsafe.Pointer(&buf[0])), + uintptr(size), + uintptr(unsafe.Pointer(&size)), + 0) + if r != 0 { + return nil, ErrExtractingPublicKey + } + return buf, nil +} + +func unmarshalECC(buf []byte, kh uintptr) (*ecdsa.PublicKey, error) { + // BCRYPT_ECCKEY_BLOB from bcrypt.h + header := struct { + Magic uint32 + Key uint32 + }{} + + r := bytes.NewReader(buf) + if err := binary.Read(r, binary.LittleEndian, &header); err != nil { + return nil, ErrExtractingECCPublicKey + } + + curve, ok := winCurveIDs[header.Magic] + if !ok { + // Fix for b/185945636, where despite specifying the curve, nCrypt returns + // an incorrect response with BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC. + var err error + curve, err = winCurveName(kh) + if err != nil { + // unsupported header magic or cannot match the curve by name + return nil, err + } + } + + keyX := make([]byte, header.Key) + if n, err := r.Read(keyX); n != int(header.Key) || err != nil { + // failed to read key X + return nil, ErrExtractingECCPublicKey + } + + keyY := make([]byte, header.Key) + if n, err := r.Read(keyY); n != int(header.Key) || err != nil { + // failed to read key Y + return nil, ErrExtractingECCPublicKey + } + + pub := &ecdsa.PublicKey{ + Curve: curve, + X: new(big.Int).SetBytes(keyX), + Y: new(big.Int).SetBytes(keyY), + } + return pub, nil +} + +// winCurveName reads the curve name property and returns the corresponding curve. +func winCurveName(kh uintptr) (elliptic.Curve, error) { + cn, err := winGetPropertyStr(kh, winNCryptECCCurveNameProperty) + if err != nil { + // unable to determine the curve property name + return nil, ErrExtractPropertyFromKey + } + curve, ok := winCurveNames[cn] + if !ok { + // unknown curve name + return nil, ErrBadECCCurveName + } + return curve, nil +} + +func winUnmarshalRSA(buf []byte) (*rsa.PublicKey, error) { + // BCRYPT_RSA_BLOB from bcrypt.h + header := struct { + Magic uint32 + BitLength uint32 + PublicExpSize uint32 + ModulusSize uint32 + UnusedPrime1 uint32 + UnusedPrime2 uint32 + }{} + + r := bytes.NewReader(buf) + if err := binary.Read(r, binary.LittleEndian, &header); err != nil { + return nil, ErrExtractingRSAPublicKey + } + + if header.Magic != winRSA1Magic { + // invalid header magic + return nil, ErrExtractingRSAPublicKey + } + + if header.PublicExpSize > 8 { + // unsupported public exponent size + return nil, ErrExtractingRSAPublicKey + } + + exp := make([]byte, 8) + if n, err := r.Read(exp[8-header.PublicExpSize:]); n != int(header.PublicExpSize) || err != nil { + // failed to read public exponent + return nil, ErrExtractingRSAPublicKey + } + + mod := make([]byte, header.ModulusSize) + if n, err := r.Read(mod); n != int(header.ModulusSize) || err != nil { + // failed to read modulus + return nil, ErrExtractingRSAPublicKey + } + + pub := &rsa.PublicKey{ + N: new(big.Int).SetBytes(mod), + E: int(binary.BigEndian.Uint64(exp)), + } + return pub, nil +} + +// storeHandle returns a handle to a given cert store, opening the handle as needed. +func (w *winCertStore) storeHandle(provider uint32, store *uint16) (windows.Handle, error) { + w.mu.Lock() + defer w.mu.Unlock() + + key := fmt.Sprintf("%d%s", provider, windows.UTF16PtrToString(store)) + var err error + if w.stores[key] == nil { + w.stores[key], err = winNewStoreHandle(provider, store) + if err != nil { + return 0, ErrBadCryptoStoreProvider + } + } + return *w.stores[key].handle, nil +} + +// Verify interface conformance. +var _ credential = &winKey{} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/certstore/errors.go b/vendor/github.com/nats-io/nats-server/v2/server/certstore/errors.go new file mode 100644 index 000000000..bbb1c9d83 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/certstore/errors.go @@ -0,0 +1,73 @@ +package certstore + +import ( + "errors" +) + +var ( + // ErrBadCryptoStoreProvider represents inablity to establish link with a certificate store + ErrBadCryptoStoreProvider = errors.New("unable to open certificate store or store not available") + + // ErrBadRSAHashAlgorithm represents a bad or unsupported RSA hash algorithm + ErrBadRSAHashAlgorithm = errors.New("unsupported RSA hash algorithm") + + // ErrBadSigningAlgorithm represents a bad or unsupported signing algorithm + ErrBadSigningAlgorithm = errors.New("unsupported signing algorithm") + + // ErrStoreRSASigningError represents an error returned from store during RSA signature + ErrStoreRSASigningError = errors.New("unable to obtain RSA signature from store") + + // ErrStoreECDSASigningError represents an error returned from store during ECDSA signature + ErrStoreECDSASigningError = errors.New("unable to obtain ECDSA signature from store") + + // ErrNoPrivateKeyStoreRef represents an error getting a handle to a private key in store + ErrNoPrivateKeyStoreRef = errors.New("unable to obtain private key handle from store") + + // ErrExtractingPrivateKeyMetadata represents a family of errors extracting metadata about the private key in store + ErrExtractingPrivateKeyMetadata = errors.New("unable to extract private key metadata") + + // ErrExtractingECCPublicKey represents an error exporting ECC-type public key from store + ErrExtractingECCPublicKey = errors.New("unable to extract ECC public key from store") + + // ErrExtractingRSAPublicKey represents an error exporting RSA-type public key from store + ErrExtractingRSAPublicKey = errors.New("unable to extract RSA public key from store") + + // ErrExtractingPublicKey represents a general error exporting public key from store + ErrExtractingPublicKey = errors.New("unable to extract public key from store") + + // ErrBadPublicKeyAlgorithm represents a bad or unsupported public key algorithm + ErrBadPublicKeyAlgorithm = errors.New("unsupported public key algorithm") + + // ErrExtractPropertyFromKey represents a general failure to extract a metadata property field + ErrExtractPropertyFromKey = errors.New("unable to extract property from key") + + // ErrBadECCCurveName represents an ECC signature curve name that is bad or unsupported + ErrBadECCCurveName = errors.New("unsupported ECC curve name") + + // ErrFailedCertSearch represents not able to find certificate in store + ErrFailedCertSearch = errors.New("unable to find certificate in store") + + // ErrFailedX509Extract represents not being able to extract x509 certificate from found cert in store + ErrFailedX509Extract = errors.New("unable to extract x509 from certificate") + + // ErrBadMatchByType represents unknown CERT_MATCH_BY passed + ErrBadMatchByType = errors.New("cert match by type not implemented") + + // ErrBadCertStore represents unknown CERT_STORE passed + ErrBadCertStore = errors.New("cert store type not implemented") + + // ErrConflictCertFileAndStore represents ambiguous configuration of both file and store + ErrConflictCertFileAndStore = errors.New("'cert_file' and 'cert_store' may not both be configured") + + // ErrBadCertStoreField represents malformed cert_store option + ErrBadCertStoreField = errors.New("expected 'cert_store' to be a valid non-empty string") + + // ErrBadCertMatchByField represents malformed cert_match_by option + ErrBadCertMatchByField = errors.New("expected 'cert_match_by' to be a valid non-empty string") + + // ErrBadCertMatchField represents malformed cert_match option + ErrBadCertMatchField = errors.New("expected 'cert_match' to be a valid non-empty string") + + // ErrOSNotCompatCertStore represents cert_store passed that exists but is not valid on current OS + ErrOSNotCompatCertStore = errors.New("cert_store not compatible with current operating system") +) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/client.go b/vendor/github.com/nats-io/nats-server/v2/server/client.go index bcedd1340..b6f73442d 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/client.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/client.go @@ -789,15 +789,16 @@ func (c *client) subsAtLimit() bool { } func minLimit(value *int32, limit int32) bool { - if *value != jwt.NoLimit { + v := atomic.LoadInt32(value) + if v != jwt.NoLimit { if limit != jwt.NoLimit { - if limit < *value { - *value = limit + if limit < v { + atomic.StoreInt32(value, limit) return true } } } else if limit != jwt.NoLimit { - *value = limit + atomic.StoreInt32(value, limit) return true } return false @@ -810,7 +811,7 @@ func (c *client) applyAccountLimits() { if c.acc == nil || (c.kind != CLIENT && c.kind != LEAF) { return } - c.mpay = jwt.NoLimit + atomic.StoreInt32(&c.mpay, jwt.NoLimit) c.msubs = jwt.NoLimit if c.opts.JWT != _EMPTY_ { // user jwt implies account if uc, _ := jwt.DecodeUserClaims(c.opts.JWT); uc != nil { @@ -2170,7 +2171,7 @@ func (c *client) generateClientInfoJSON(info Info) []byte { if c.srv != nil { // Otherwise lame duck info can panic c.srv.websocket.mu.RLock() info.TLSAvailable = c.srv.websocket.tls - if c.srv.websocket.server != nil { + if c.srv.websocket.tls && c.srv.websocket.server != nil { if tc := c.srv.websocket.server.TLSConfig; tc != nil { info.TLSRequired = !tc.InsecureSkipVerify } @@ -3127,20 +3128,14 @@ var needFlush = struct{}{} // deliverMsg will deliver a message to a matching subscription and its underlying client. // We process all connection/client types. mh is the part that will be protocol/client specific. func (c *client) deliverMsg(prodIsMQTT bool, sub *subscription, acc *Account, subject, reply, mh, msg []byte, gwrply bool) bool { - // Check sub client and check echo - if sub.client == nil || c == sub.client && !sub.client.echo { + // Check sub client and check echo. Only do this if not a service import. + if sub.client == nil || (c == sub.client && !sub.client.echo && !sub.si) { return false } client := sub.client client.mu.Lock() - // Check echo - if c == client && !client.echo { - client.mu.Unlock() - return false - } - // Check if we have a subscribe deny clause. This will trigger us to check the subject // for a match against the denied subjects. if client.mperms != nil && client.checkDenySub(string(subject)) { @@ -3582,15 +3577,21 @@ func (c *client) processInboundClientMsg(msg []byte) (bool, bool) { } // Mostly under testing scenarios. + c.mu.Lock() if c.srv == nil || c.acc == nil { + c.mu.Unlock() return false, false } + acc := c.acc + genidAddr := &acc.sl.genid // Check pub permissions - if c.perms != nil && (c.perms.pub.allow != nil || c.perms.pub.deny != nil) && !c.pubAllowed(string(c.pa.subject)) { + if c.perms != nil && (c.perms.pub.allow != nil || c.perms.pub.deny != nil) && !c.pubAllowedFullCheck(string(c.pa.subject), true, true) { + c.mu.Unlock() c.pubPermissionViolation(c.pa.subject) return false, true } + c.mu.Unlock() // Now check for reserved replies. These are used for service imports. if c.kind == CLIENT && len(c.pa.reply) > 0 && isReservedReply(c.pa.reply) { @@ -3611,10 +3612,10 @@ func (c *client) processInboundClientMsg(msg []byte) (bool, bool) { // performance impact reported in our bench) var isGWRouted bool if c.kind != CLIENT { - if atomic.LoadInt32(&c.acc.gwReplyMapping.check) > 0 { - c.acc.mu.RLock() - c.pa.subject, isGWRouted = c.acc.gwReplyMapping.get(c.pa.subject) - c.acc.mu.RUnlock() + if atomic.LoadInt32(&acc.gwReplyMapping.check) > 0 { + acc.mu.RLock() + c.pa.subject, isGWRouted = acc.gwReplyMapping.get(c.pa.subject) + acc.mu.RUnlock() } } else if atomic.LoadInt32(&c.gwReplyMapping.check) > 0 { c.mu.Lock() @@ -3657,7 +3658,7 @@ func (c *client) processInboundClientMsg(msg []byte) (bool, bool) { var r *SublistResult var ok bool - genid := atomic.LoadUint64(&c.acc.sl.genid) + genid := atomic.LoadUint64(genidAddr) if genid == c.in.genid && c.in.results != nil { r, ok = c.in.results[string(c.pa.subject)] } else { @@ -3668,15 +3669,17 @@ func (c *client) processInboundClientMsg(msg []byte) (bool, bool) { // Go back to the sublist data structure. if !ok { - r = c.acc.sl.Match(string(c.pa.subject)) - c.in.results[string(c.pa.subject)] = r - // Prune the results cache. Keeps us from unbounded growth. Random delete. - if len(c.in.results) > maxResultCacheSize { - n := 0 - for subject := range c.in.results { - delete(c.in.results, subject) - if n++; n > pruneSize { - break + r = acc.sl.Match(string(c.pa.subject)) + if len(r.psubs)+len(r.qsubs) > 0 { + c.in.results[string(c.pa.subject)] = r + // Prune the results cache. Keeps us from unbounded growth. Random delete. + if len(c.in.results) > maxResultCacheSize { + n := 0 + for subject := range c.in.results { + delete(c.in.results, subject) + if n++; n > pruneSize { + break + } } } } @@ -3699,7 +3702,7 @@ func (c *client) processInboundClientMsg(msg []byte) (bool, bool) { atomic.LoadInt64(&c.srv.gateway.totalQSubs) > 0 { flag |= pmrCollectQueueNames } - didDeliver, qnames = c.processMsgResults(c.acc, r, msg, c.pa.deliver, c.pa.subject, c.pa.reply, flag) + didDeliver, qnames = c.processMsgResults(acc, r, msg, c.pa.deliver, c.pa.subject, c.pa.reply, flag) } // Now deal with gateways @@ -3709,7 +3712,7 @@ func (c *client) processInboundClientMsg(msg []byte) (bool, bool) { reply = append(reply, '@') reply = append(reply, c.pa.deliver...) } - didDeliver = c.sendMsgToGateways(c.acc, msg, c.pa.subject, reply, qnames) || didDeliver + didDeliver = c.sendMsgToGateways(acc, msg, c.pa.subject, reply, qnames) || didDeliver } // Check to see if we did not deliver to anyone and the client has a reply subject set @@ -3915,6 +3918,7 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt checkJS = true } } + siAcc := si.acc acc.mu.RUnlock() // We have a special case where JetStream pulls in all service imports through one export. @@ -3945,7 +3949,7 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt } } else if !isResponse && si.latency != nil && tracking { // Check to see if this was a bad request with no reply and we were supposed to be tracking. - si.acc.sendBadRequestTrackingLatency(si, c, headers) + siAcc.sendBadRequestTrackingLatency(si, c, headers) } // Send tracking info here if we are tracking this response. @@ -3973,7 +3977,7 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt // Now check to see if this account has mappings that could affect the service import. // Can't use non-locked trick like in processInboundClientMsg, so just call into selectMappedSubject // so we only lock once. - nsubj, changed := si.acc.selectMappedSubject(to) + nsubj, changed := siAcc.selectMappedSubject(to) if changed { c.pa.mapped = []byte(to) to = nsubj @@ -3990,7 +3994,7 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt // Place our client info for the request in the original message. // This will survive going across routes, etc. if !isResponse { - isSysImport := si.acc == c.srv.SystemAccount() + isSysImport := siAcc == c.srv.SystemAccount() var ci *ClientInfo if hadPrevSi && c.pa.hdr >= 0 { var cis ClientInfo @@ -4031,11 +4035,11 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt c.pa.reply = nrr if changed && c.isMqtt() && c.pa.hdr > 0 { - c.srv.mqttStoreQoS1MsgForAccountOnNewSubject(c.pa.hdr, msg, si.acc.GetName(), to) + c.srv.mqttStoreQoS1MsgForAccountOnNewSubject(c.pa.hdr, msg, siAcc.GetName(), to) } // FIXME(dlc) - Do L1 cache trick like normal client? - rr := si.acc.sl.Match(to) + rr := siAcc.sl.Match(to) // If we are a route or gateway or leafnode and this message is flipped to a queue subscriber we // need to handle that since the processMsgResults will want a queue filter. @@ -4060,10 +4064,10 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt if c.srv.gateway.enabled { flags |= pmrCollectQueueNames var queues [][]byte - didDeliver, queues = c.processMsgResults(si.acc, rr, msg, c.pa.deliver, []byte(to), nrr, flags) - didDeliver = c.sendMsgToGateways(si.acc, msg, []byte(to), nrr, queues) || didDeliver + didDeliver, queues = c.processMsgResults(siAcc, rr, msg, c.pa.deliver, []byte(to), nrr, flags) + didDeliver = c.sendMsgToGateways(siAcc, msg, []byte(to), nrr, queues) || didDeliver } else { - didDeliver, _ = c.processMsgResults(si.acc, rr, msg, c.pa.deliver, []byte(to), nrr, flags) + didDeliver, _ = c.processMsgResults(siAcc, rr, msg, c.pa.deliver, []byte(to), nrr, flags) } // Restore to original values. @@ -4096,7 +4100,7 @@ func (c *client) processServiceImport(si *serviceImport, acc *Account, msg []byt } else { // This is a main import and since we could not even deliver to the exporting account // go ahead and remove the respServiceImport we created above. - si.acc.removeRespServiceImport(rsi, reason) + siAcc.removeRespServiceImport(rsi, reason) } } } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/const.go b/vendor/github.com/nats-io/nats-server/v2/server/const.go index b53007ebb..d5789cbac 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/const.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/const.go @@ -41,7 +41,7 @@ var ( const ( // VERSION is the current version for the server. - VERSION = "2.9.19" + VERSION = "2.9.21" // PROTO is the currently supported protocol. // 0 was the original diff --git a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go index daa921b0d..b229b9fdb 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go @@ -3506,7 +3506,7 @@ func (o *consumer) loopAndGatherMsgs(qch chan struct{}) { if err == ErrStoreEOF { o.checkNumPendingOnEOF() } - if err == ErrStoreMsgNotFound || err == ErrStoreEOF || err == errMaxAckPending || err == errPartialCache { + if err == ErrStoreMsgNotFound || err == errDeletedMsg || err == ErrStoreEOF || err == errMaxAckPending || err == errPartialCache { goto waitForMsgs } else { s.Errorf("Received an error looking up message for consumer: %v", err) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/events.go b/vendor/github.com/nats-io/nats-server/v2/server/events.go index c954467a1..1b58d912d 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/events.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/events.go @@ -17,6 +17,7 @@ import ( "bytes" "compress/gzip" "crypto/sha256" + "crypto/x509" "encoding/json" "errors" "fmt" @@ -30,7 +31,9 @@ import ( "time" "github.com/klauspost/compress/s2" + "github.com/nats-io/jwt/v2" + "github.com/nats-io/nats-server/v2/server/certidp" "github.com/nats-io/nats-server/v2/server/pse" ) @@ -78,6 +81,9 @@ const ( accReqTokens = 5 accReqAccIndex = 3 + + ocspPeerRejectEventSubj = "$SYS.SERVER.%s.OCSP.PEER.CONN.REJECT" + ocspPeerChainlinkInvalidEventSubj = "$SYS.SERVER.%s.OCSP.PEER.LINK.INVALID" ) // FIXME(dlc) - make configurable. @@ -151,6 +157,34 @@ type DisconnectEventMsg struct { // DisconnectEventMsgType is the schema type for DisconnectEventMsg const DisconnectEventMsgType = "io.nats.server.advisory.v1.client_disconnect" +// OCSPPeerRejectEventMsg is sent when a peer TLS handshake is ultimately rejected due to OCSP invalidation. +// A "peer" can be an inbound client connection or a leaf connection to a remote server. Peer in event payload +// is always the peer's (TLS) leaf cert, which may or may be the invalid cert (See also OCSPPeerChainlinkInvalidEventMsg) +type OCSPPeerRejectEventMsg struct { + TypedEvent + Kind string `json:"kind"` + Peer certidp.CertInfo `json:"peer"` + Server ServerInfo `json:"server"` + Reason string `json:"reason"` +} + +// OCSPPeerRejectEventMsgType is the schema type for OCSPPeerRejectEventMsg +const OCSPPeerRejectEventMsgType = "io.nats.server.advisory.v1.ocsp_peer_reject" + +// OCSPPeerChainlinkInvalidEventMsg is sent when a certificate (link) in a valid TLS chain is found to be OCSP invalid +// during a peer TLS handshake. A "peer" can be an inbound client connection or a leaf connection to a remote server. +// Peer and Link may be the same if the invalid cert was the peer's leaf cert +type OCSPPeerChainlinkInvalidEventMsg struct { + TypedEvent + Link certidp.CertInfo `json:"link"` + Peer certidp.CertInfo `json:"peer"` + Server ServerInfo `json:"server"` + Reason string `json:"reason"` +} + +// OCSPPeerChainlinkInvalidEventMsgType is the schema type for OCSPPeerChainlinkInvalidEventMsg +const OCSPPeerChainlinkInvalidEventMsgType = "io.nats.server.advisory.v1.ocsp_peer_link_invalid" + // AccountNumConns is an event that will be sent from a server that is tracking // a given account when the number of connections changes. It will also HB // updates in the absence of any changes. @@ -843,35 +877,15 @@ func getHash(name string) string { return getHashSize(name, sysHashLen) } -var nameToHashSize8 = sync.Map{} -var nameToHashSize6 = sync.Map{} - // Computes a hash for the given `name`. The result will be `size` characters long. func getHashSize(name string, size int) string { - compute := func() string { - sha := sha256.New() - sha.Write([]byte(name)) - b := sha.Sum(nil) - for i := 0; i < size; i++ { - b[i] = digits[int(b[i]%base)] - } - return string(b[:size]) + sha := sha256.New() + sha.Write([]byte(name)) + b := sha.Sum(nil) + for i := 0; i < size; i++ { + b[i] = digits[int(b[i]%base)] } - var m *sync.Map - switch size { - case 8: - m = &nameToHashSize8 - case 6: - m = &nameToHashSize6 - default: - return compute() - } - if v, ok := m.Load(name); ok { - return v.(string) - } - h := compute() - m.Store(name, h) - return h + return string(b[:size]) } // Returns the node name for this server which is a hash of the server name. @@ -2488,3 +2502,74 @@ func (s *Server) wrapChk(f func()) func() { s.mu.Unlock() } } + +// sendOCSPPeerRejectEvent sends a system level event to system account when a peer connection is +// rejected due to OCSP invalid status of its trust chain(s). +func (s *Server) sendOCSPPeerRejectEvent(kind string, peer *x509.Certificate, reason string) { + s.mu.Lock() + defer s.mu.Unlock() + if !s.eventsEnabled() { + return + } + if peer == nil { + s.Errorf(certidp.ErrPeerEmptyNoEvent) + return + } + eid := s.nextEventID() + now := time.Now().UTC() + m := OCSPPeerRejectEventMsg{ + TypedEvent: TypedEvent{ + Type: OCSPPeerRejectEventMsgType, + ID: eid, + Time: now, + }, + Kind: kind, + Peer: certidp.CertInfo{ + Subject: certidp.GetSubjectDNForm(peer), + Issuer: certidp.GetIssuerDNForm(peer), + Fingerprint: certidp.GenerateFingerprint(peer), + Raw: peer.Raw, + }, + Reason: reason, + } + subj := fmt.Sprintf(ocspPeerRejectEventSubj, s.info.ID) + s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m) +} + +// sendOCSPPeerChainlinkInvalidEvent sends a system level event to system account when a link in a peer's trust chain +// is OCSP invalid. +func (s *Server) sendOCSPPeerChainlinkInvalidEvent(peer *x509.Certificate, link *x509.Certificate, reason string) { + s.mu.Lock() + defer s.mu.Unlock() + if !s.eventsEnabled() { + return + } + if peer == nil || link == nil { + s.Errorf(certidp.ErrPeerEmptyNoEvent) + return + } + eid := s.nextEventID() + now := time.Now().UTC() + m := OCSPPeerChainlinkInvalidEventMsg{ + TypedEvent: TypedEvent{ + Type: OCSPPeerChainlinkInvalidEventMsgType, + ID: eid, + Time: now, + }, + Link: certidp.CertInfo{ + Subject: certidp.GetSubjectDNForm(link), + Issuer: certidp.GetIssuerDNForm(link), + Fingerprint: certidp.GenerateFingerprint(link), + Raw: link.Raw, + }, + Peer: certidp.CertInfo{ + Subject: certidp.GetSubjectDNForm(peer), + Issuer: certidp.GetIssuerDNForm(peer), + Fingerprint: certidp.GenerateFingerprint(peer), + Raw: peer.Raw, + }, + Reason: reason, + } + subj := fmt.Sprintf(ocspPeerChainlinkInvalidEventSubj, s.info.ID) + s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m) +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go index 7b56d8891..7b167aefb 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go @@ -275,6 +275,8 @@ const ( wiThresh = int64(30 * time.Second) // Time threshold to write index info for non FIFO cases winfThresh = int64(2 * time.Second) + // Checksum size for hash for msg records. + recordHashSize = 8 ) func newFileStore(fcfg FileStoreConfig, cfg StreamConfig) (*fileStore, error) { @@ -349,6 +351,14 @@ func newFileStoreWithCreated(fcfg FileStoreConfig, cfg StreamConfig, created tim return nil, fmt.Errorf("could not create hash: %v", err) } + keyFile := filepath.Join(fs.fcfg.StoreDir, JetStreamMetaFileKey) + // Make sure we do not have an encrypted store underneath of us but no main key. + if fs.prf == nil { + if _, err := os.Stat(keyFile); err == nil { + return nil, errNoMainKey + } + } + // Recover our message state. if err := fs.recoverMsgs(); err != nil { return nil, err @@ -366,7 +376,6 @@ func newFileStoreWithCreated(fcfg FileStoreConfig, cfg StreamConfig, created tim // If we expect to be encrypted check that what we are restoring is not plaintext. // This can happen on snapshot restores or conversions. if fs.prf != nil { - keyFile := filepath.Join(fs.fcfg.StoreDir, JetStreamMetaFileKey) if _, err := os.Stat(keyFile); err != nil && os.IsNotExist(err) { if err := fs.writeStreamMeta(); err != nil { return nil, err @@ -964,6 +973,10 @@ func (mb *msgBlock) rebuildState() (*LostStreamData, error) { func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, error) { startLastSeq := mb.last.seq + // Remove the .fss file and clear any cache we have set. + mb.clearCacheAndOffset() + mb.removePerSubjectInfoLocked() + buf, err := mb.loadBlock(nil) if err != nil || len(buf) == 0 { var ld *LostStreamData @@ -989,9 +1002,6 @@ func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, error) { mb.last.seq, mb.last.ts = 0, 0 firstNeedsSet := true - // Remove the .fss file from disk. - mb.removePerSubjectInfoLocked() - // Check if we need to decrypt. if mb.bek != nil && len(buf) > 0 { // Recreate to reset counter. @@ -1063,12 +1073,7 @@ func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, error) { rl &^= hbit dlen := int(rl) - msgHdrSize // Do some quick sanity checks here. - if dlen < 0 || int(slen) > (dlen-8) || dlen > int(rl) || rl > rlBadThresh { - truncate(index) - return gatherLost(lbuf - index), errBadMsg - } - - if index+rl > lbuf { + if dlen < 0 || int(slen) > (dlen-recordHashSize) || dlen > int(rl) || index+rl > lbuf || rl > rlBadThresh { truncate(index) return gatherLost(lbuf - index), errBadMsg } @@ -1084,15 +1089,17 @@ func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, error) { addToDmap(seq) } index += rl - mb.last.seq = seq - mb.last.ts = ts + if seq >= mb.first.seq { + mb.last.seq = seq + mb.last.ts = ts + } continue } // This is for when we have index info that adjusts for deleted messages // at the head. So the first.seq will be already set here. If this is larger // replace what we have with this seq. - if firstNeedsSet && seq > mb.first.seq { + if firstNeedsSet && seq >= mb.first.seq { firstNeedsSet, mb.first.seq, mb.first.ts = false, seq, ts } @@ -1112,12 +1119,12 @@ func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, error) { hh.Write(hdr[4:20]) hh.Write(data[:slen]) if hasHeaders { - hh.Write(data[slen+4 : dlen-8]) + hh.Write(data[slen+4 : dlen-recordHashSize]) } else { - hh.Write(data[slen : dlen-8]) + hh.Write(data[slen : dlen-recordHashSize]) } checksum := hh.Sum(nil) - if !bytes.Equal(checksum, data[len(data)-8:]) { + if !bytes.Equal(checksum, data[len(data)-recordHashSize:]) { truncate(index) return gatherLost(lbuf - index), errBadMsg } @@ -1158,6 +1165,11 @@ func (mb *msgBlock) rebuildStateLocked() (*LostStreamData, error) { mb.last.seq = mb.first.seq - 1 } + // Update our fss file if needed. + if len(mb.fss) > 0 { + mb.writePerSubjectInfo() + } + return nil, nil } @@ -1868,12 +1880,24 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) seqStart, _ = fs.selectMsgBlockWithIndex(sseq) } - tsa := [32]string{} - fsa := [32]string{} + var tsa, fsa [32]string fts := tokenizeSubjectIntoSlice(fsa[:0], filter) isAll := filter == _EMPTY_ || filter == fwcs wc := subjectHasWildcard(filter) + // See if filter was provided but its the only subject. + if !isAll && !wc && len(fs.psim) == 1 && fs.psim[filter] != nil { + isAll = true + } + + // If we are isAll and have no deleted we can do a simpler calculation. + if isAll && (fs.state.LastSeq-fs.state.FirstSeq+1) == fs.state.Msgs { + if sseq == 0 { + return fs.state.Msgs, validThrough + } + return fs.state.LastSeq - sseq + 1, validThrough + } + isMatch := func(subj string) bool { if isAll { return true @@ -1900,6 +1924,7 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) var t uint64 if isAll && sseq <= mb.first.seq { if lastPerSubject { + mb.ensurePerSubjectInfoLoaded() for subj := range mb.fss { if !seen[subj] { total++ @@ -2023,16 +2048,20 @@ func (fs *fileStore) NumPending(sseq uint64, filter string, lastPerSubject bool) mb.mu.Lock() // Check if we should include all of this block in adjusting. If so work with metadata. if sseq > mb.last.seq { - // We need to adjust for all matches in this block. - // We will scan fss state vs messages themselves. - // Make sure we have fss loaded. - mb.ensurePerSubjectInfoLoaded() - for subj, ss := range mb.fss { - if isMatch(subj) { - if lastPerSubject { - adjust++ - } else { - adjust += ss.Msgs + if isAll && !lastPerSubject { + adjust += mb.msgs + } else { + // We need to adjust for all matches in this block. + // We will scan fss state vs messages themselves. + // Make sure we have fss loaded. + mb.ensurePerSubjectInfoLoaded() + for subj, ss := range mb.fss { + if isMatch(subj) { + if lastPerSubject { + adjust++ + } else { + adjust += ss.Msgs + } } } } @@ -2574,14 +2603,42 @@ func (fs *fileStore) enforceMsgPerSubjectLimit() { fs.scb = nil defer func() { fs.scb = cb }() + var numMsgs uint64 + // collect all that are not correct. needAttention := make(map[string]*psi) for subj, psi := range fs.psim { + numMsgs += psi.total if psi.total > maxMsgsPer { needAttention[subj] = psi } } + // We had an issue with a use case where psim (and hence fss) were correct but idx was not and was not properly being caught. + // So do a quick sanity check here. If we detect a skew do a rebuild then re-check. + if numMsgs != fs.state.Msgs { + // Clear any global subject state. + fs.psim = make(map[string]*psi) + for _, mb := range fs.blks { + mb.removeIndexFile() + ld, err := mb.rebuildState() + mb.writeIndexInfo() + if err != nil && ld != nil { + fs.addLostData(ld) + } + fs.populateGlobalPerSubjectInfo(mb) + } + // Rebuild fs state too. + fs.rebuildStateLocked(nil) + // Need to redo blocks that need attention. + needAttention = make(map[string]*psi) + for subj, psi := range fs.psim { + if psi.total > maxMsgsPer { + needAttention[subj] = psi + } + } + } + // Collect all the msgBlks we alter. blks := make(map[*msgBlock]struct{}) @@ -3026,8 +3083,7 @@ func (mb *msgBlock) compact() { return } - // Close cache and index file and wipe delete map, then rebuild. - mb.clearCacheAndOffset() + // Remove index file and wipe delete map, then rebuild. mb.removeIndexFileLocked() mb.deleteDmap() mb.rebuildStateLocked() @@ -3053,6 +3109,11 @@ func (mb *msgBlock) slotInfo(slot int) (uint32, uint32, bool, error) { bi := mb.cache.idx[slot] ri, hashChecked := (bi &^ hbit), (bi&hbit) != 0 + // If this is a deleted slot return here. + if bi == dbit { + return 0, 0, false, errDeletedMsg + } + // Determine record length var rl uint32 if len(mb.cache.idx) > slot+1 { @@ -3998,7 +4059,7 @@ func (fs *fileStore) selectMsgBlockForStart(minTime time.Time) *msgBlock { func (mb *msgBlock) indexCacheBuf(buf []byte) error { var le = binary.LittleEndian - var fseq uint64 + var fseq, pseq uint64 var idx []uint32 var index uint32 @@ -4031,7 +4092,7 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { dlen := int(rl) - msgHdrSize // Do some quick sanity checks here. - if dlen < 0 || int(slen) > dlen || dlen > int(rl) || index+rl > lbuf || rl > 32*1024*1024 { + if dlen < 0 || int(slen) > (dlen-recordHashSize) || dlen > int(rl) || index+rl > lbuf || rl > rlBadThresh { // This means something is off. // TODO(dlc) - Add into bad list? return errCorruptState @@ -4039,15 +4100,31 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { // Clear erase bit. seq = seq &^ ebit - // Adjust if we guessed wrong. - if seq != 0 && seq < fseq { - fseq = seq - } + // We defer checksum checks to individual msg cache lookups to amortorize costs and // not introduce latency for first message from a newly loaded block. - idx = append(idx, index) - mb.cache.lrl = uint32(rl) - index += mb.cache.lrl + if seq >= mb.first.seq { + // Track that we do not have holes. + // Not expected but did see it in the field. + if pseq > 0 && seq != pseq+1 { + if mb.dmap == nil { + mb.dmap = make(map[uint64]struct{}) + } + for dseq := pseq + 1; dseq < seq; dseq++ { + idx = append(idx, dbit) + mb.dmap[dseq] = struct{}{} + } + } + pseq = seq + + idx = append(idx, index) + mb.cache.lrl = uint32(rl) + // Adjust if we guessed wrong. + if seq != 0 && seq < fseq { + fseq = seq + } + } + index += rl } mb.cache.buf = buf mb.cache.idx = idx @@ -4373,6 +4450,7 @@ var ( errMsgBlkTooBig = errors.New("message block size exceeded int capacity") errUnknownCipher = errors.New("unknown cipher") errDIOStalled = errors.New("IO is stalled") + errNoMainKey = errors.New("encrypted store encountered with no main key") ) // Used for marking messages that have had their checksums checked. @@ -4382,6 +4460,9 @@ const hbit = 1 << 31 // Used for marking erased messages sequences. const ebit = 1 << 63 +// Used to mark a bad index as deleted. +const dbit = 1 << 30 + // Will do a lookup from cache. // Lock should be held. func (mb *msgBlock) cacheLookup(seq uint64, sm *StoreMsg) (*StoreMsg, error) { @@ -4392,6 +4473,7 @@ func (mb *msgBlock) cacheLookup(seq uint64, sm *StoreMsg) (*StoreMsg, error) { // If we have a delete map check it. if mb.dmap != nil { if _, ok := mb.dmap[seq]; ok { + mb.llts = time.Now().UnixNano() return nil, errDeletedMsg } } @@ -4534,9 +4616,9 @@ func (mb *msgBlock) msgFromBuf(buf []byte, sm *StoreMsg, hh hash.Hash64) (*Store hh.Write(hdr[4:20]) hh.Write(data[:slen]) if hasHeaders { - hh.Write(data[slen+4 : dlen-8]) + hh.Write(data[slen+4 : dlen-recordHashSize]) } else { - hh.Write(data[slen : dlen-8]) + hh.Write(data[slen : dlen-recordHashSize]) } if !bytes.Equal(hh.Sum(nil), data[len(data)-8:]) { return nil, errBadMsg @@ -5339,13 +5421,13 @@ func (fs *fileStore) Compact(seq uint64) (uint64, error) { var purged, bytes uint64 - // We have to delete interior messages. fs.mu.Lock() + // Same as purge all. if lseq := fs.state.LastSeq; seq > lseq { fs.mu.Unlock() return fs.purge(seq) } - + // We have to delete interior messages. smb := fs.selectMsgBlock(seq) if smb == nil { fs.mu.Unlock() @@ -5852,6 +5934,8 @@ func (mb *msgBlock) recalculateFirstForSubj(subj string, startSeq uint64, ss *Si if startSlot >= len(mb.cache.idx) { ss.First = ss.Last return + } else if startSlot < 0 { + startSlot = 0 } var le = binary.LittleEndian @@ -6261,6 +6345,9 @@ func (fs *fileStore) Stop() error { fs.cancelSyncTimer() fs.cancelAgeChk() + // We should update the upper usage layer on a stop. + cb, bytes := fs.scb, int64(fs.state.Bytes) + var _cfs [256]ConsumerStore cfs := append(_cfs[:0], fs.cfs...) fs.cfs = nil @@ -6270,6 +6357,10 @@ func (fs *fileStore) Stop() error { o.Stop() } + if bytes > 0 && cb != nil { + cb(0, -bytes, 0, _EMPTY_) + } + return nil } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/gateway.go b/vendor/github.com/nats-io/nats-server/v2/server/gateway.go index 8d194978e..97550f072 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/gateway.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/gateway.go @@ -2953,7 +2953,7 @@ func (c *client) processInboundGatewayMsg(msg []byte) { // Check if this is a service reply subject (_R_) noInterest := len(r.psubs) == 0 checkNoInterest := true - if acc.imports.services != nil { + if acc.NumServiceImports() > 0 { if isServiceReply(c.pa.subject) { checkNoInterest = false } else { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go index 4956d909b..d4f642ed3 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go @@ -2734,7 +2734,7 @@ func canonicalName(name string) string { } // To throttle the out of resources errors. -func (s *Server) resourcesExeededError() { +func (s *Server) resourcesExceededError() { var didAlert bool s.rerrMu.Lock() diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go index c7ad8b17a..7013a2ccb 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 The NATS Authors +// Copyright 2020-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -2656,7 +2656,7 @@ func (s *Server) jsLeaderAccountPurgeRequest(sub *subscription, c *client, _ *Ac } // Request to have the meta leader stepdown. -// These will only be received the meta leaders, so less checking needed. +// These will only be received by the meta leader, so less checking needed. func (s *Server) jsLeaderStepDownRequest(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) { if c == nil || !s.JetStreamEnabled() { return @@ -3360,7 +3360,7 @@ func (s *Server) processStreamRestore(ci *ClientInfo, acc *Account, cfg *StreamC // TODO(dlc) - We could check apriori and cancel initial request if we know it won't fit. total += len(msg) if js.wouldExceedLimits(FileStorage, total) { - s.resourcesExeededError() + s.resourcesExceededError() resultCh <- result{NewJSInsufficientResourcesError(), reply} return } @@ -3766,11 +3766,11 @@ func (s *Server) jsConsumerCreateRequest(sub *subscription, c *client, a *Accoun } else { streamName = streamNameFromSubject(subject) consumerName = consumerNameFromSubject(subject) - } - // New has optional filtered subject as part of main subject.. - if n > 7 { - tokens := strings.Split(subject, tsep) - filteredSubject = strings.Join(tokens[6:], tsep) + // New has optional filtered subject as part of main subject.. + if n > 6 { + tokens := strings.Split(subject, tsep) + filteredSubject = strings.Join(tokens[6:], tsep) + } } } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go index 3a86f5874..6af4fa42b 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go @@ -759,6 +759,7 @@ func (js *jetStream) setupMetaGroup() error { s.Errorf("Error creating filestore: %v", err) return err } + // Register our server. fs.registerServer(s) @@ -2290,9 +2291,13 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps case isLeader = <-lch: if isLeader { - if sendSnapshot && mset != nil && n != nil { - n.SendSnapshot(mset.stateSnapshot()) - sendSnapshot = false + if mset != nil && n != nil { + // Send a snapshot if being asked or if we are tracking + // a failed state so that followers sync. + if clfs := mset.clearCLFS(); clfs > 0 || sendSnapshot { + n.SendSnapshot(mset.stateSnapshot()) + sendSnapshot = false + } } if isRestore { acc, _ := s.LookupAccount(sa.Client.serviceAccount()) @@ -2713,15 +2718,14 @@ func (js *jetStream) applyStreamEntries(mset *stream, ce *CommittedEntry, isReco // Grab last sequence and CLFS. last, clfs := mset.lastSeqAndCLFS() - // We can skip if we know this is less than what we already have. if lseq-clfs < last { s.Debugf("Apply stream entries for '%s > %s' skipping message with sequence %d with last of %d", mset.account(), mset.name(), lseq+1-clfs, last) - // Check for any preAcks in case we are interest based. + mset.mu.Lock() - seq := lseq + 1 - mset.clfs - mset.clearAllPreAcks(seq) + // Check for any preAcks in case we are interest based. + mset.clearAllPreAcks(lseq + 1 - mset.clfs) mset.mu.Unlock() continue } @@ -2807,12 +2811,15 @@ func (js *jetStream) applyStreamEntries(mset *stream, ce *CommittedEntry, isReco } panic(err.Error()) } - // Ignore if we are recovering and we have already processed. - if isRecovering && (sp.Request == nil || sp.Request.Sequence == 0) { + // If no explicit request, fill in with leader stamped last sequence to protect ourselves on replay during server start. + if sp.Request == nil || sp.Request.Sequence == 0 { + purgeSeq := sp.LastSeq + 1 if sp.Request == nil { - sp.Request = &JSApiStreamPurgeRequest{Sequence: sp.LastSeq} - } else { - sp.Request.Sequence = sp.LastSeq + sp.Request = &JSApiStreamPurgeRequest{Sequence: purgeSeq} + } else if sp.Request.Keep == 0 { + sp.Request.Sequence = purgeSeq + } else if isRecovering { + continue } } @@ -6523,7 +6530,7 @@ LOOP: }) } - resp.Total = len(resp.Consumers) + resp.Total = ocnt resp.Limit = JSApiListLimit resp.Offset = offset resp.Missing = missingNames @@ -7222,7 +7229,7 @@ func (mset *stream) processClusteredInboundMsg(subject, reply string, hdr, msg [ // Check here pre-emptively if we have exceeded this server limits. if js.limitsExceeded(stype) { - s.resourcesExeededError() + s.resourcesExceededError() if canRespond { b, _ := json.Marshal(&JSPubAckResponse{PubAck: &PubAck{Stream: name}, Error: NewJSInsufficientResourcesError()}) outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, nil, b, nil, 0)) @@ -7768,7 +7775,7 @@ RETRY: } else if err == NewJSInsufficientResourcesError() { notifyLeaderStopCatchup(mrec, err) if mset.js.limitsExceeded(mset.cfg.Storage) { - s.resourcesExeededError() + s.resourcesExceededError() } else { s.Warnf("Catchup for stream '%s > %s' errored, account resources exceeded: %v", mset.account(), mset.name(), err) } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go b/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go index 05d94db24..39170e3c1 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go @@ -2101,8 +2101,11 @@ func (c *client) processLeafSub(argo []byte) (err error) { spoke := c.isSpokeLeafNode() c.mu.Unlock() - if err := c.addShadowSubscriptions(acc, sub); err != nil { - c.Errorf(err.Error()) + // Only add in shadow subs if a new sub or qsub. + if osub == nil { + if err := c.addShadowSubscriptions(acc, sub); err != nil { + c.Errorf(err.Error()) + } } // If we are not solicited, treat leaf node subscriptions similar to a diff --git a/vendor/github.com/nats-io/nats-server/v2/server/log.go b/vendor/github.com/nats-io/nats-server/v2/server/log.go index 4b9193c02..268719399 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/log.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/log.go @@ -66,7 +66,7 @@ func (s *Server) ConfigureLogger() { } if opts.LogFile != "" { - log = srvlog.NewFileLogger(opts.LogFile, opts.Logtime, opts.Debug, opts.Trace, true) + log = srvlog.NewFileLogger(opts.LogFile, opts.Logtime, opts.Debug, opts.Trace, true, srvlog.LogUTC(opts.LogtimeUTC)) if opts.LogSizeLimit > 0 { if l, ok := log.(*srvlog.Logger); ok { l.SetSizeLimit(opts.LogSizeLimit) @@ -84,7 +84,7 @@ func (s *Server) ConfigureLogger() { if err != nil || (stat.Mode()&os.ModeCharDevice) == 0 { colors = false } - log = srvlog.NewStdLogger(opts.Logtime, opts.Debug, opts.Trace, colors, true) + log = srvlog.NewStdLogger(opts.Logtime, opts.Debug, opts.Trace, colors, true, srvlog.LogUTC(opts.LogtimeUTC)) } s.SetLoggerV2(log, opts.Debug, opts.Trace, opts.TraceVerbose) @@ -154,8 +154,11 @@ func (s *Server) ReOpenLogFile() { if opts.LogFile == "" { s.Noticef("File log re-open ignored, not a file logger") } else { - fileLog := srvlog.NewFileLogger(opts.LogFile, - opts.Logtime, opts.Debug, opts.Trace, true) + fileLog := srvlog.NewFileLogger( + opts.LogFile, opts.Logtime, + opts.Debug, opts.Trace, true, + srvlog.LogUTC(opts.LogtimeUTC), + ) s.SetLogger(fileLog, opts.Debug, opts.Trace) if opts.LogSizeLimit > 0 { fileLog.SetSizeLimit(opts.LogSizeLimit) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go index 0b71e9069..b35e3d294 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go @@ -1144,11 +1144,12 @@ func memStoreMsgSize(subj string, hdr, msg []byte) uint64 { // Delete is same as Stop for memory store. func (ms *memStore) Delete() error { - ms.Purge() return ms.Stop() } func (ms *memStore) Stop() error { + // These can't come back, so stop is same as Delete. + ms.Purge() ms.mu.Lock() if ms.ageChk != nil { ms.ageChk.Stop() diff --git a/vendor/github.com/nats-io/nats-server/v2/server/monitor.go b/vendor/github.com/nats-io/nats-server/v2/server/monitor.go index 8e0c11a6d..b7b26b48c 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/monitor.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/monitor.go @@ -1154,6 +1154,7 @@ type Varz struct { AuthRequired bool `json:"auth_required,omitempty"` TLSRequired bool `json:"tls_required,omitempty"` TLSVerify bool `json:"tls_verify,omitempty"` + TLSOCSPPeerVerify bool `json:"tls_ocsp_peer_verify,omitempty"` IP string `json:"ip,omitempty"` ClientConnectURLs []string `json:"connect_urls,omitempty"` WSConnectURLs []string `json:"ws_connect_urls,omitempty"` @@ -1202,6 +1203,7 @@ type Varz struct { TrustedOperatorsClaim []*jwt.OperatorClaims `json:"trusted_operators_claim,omitempty"` SystemAccount string `json:"system_account,omitempty"` PinnedAccountFail uint64 `json:"pinned_account_fails,omitempty"` + OCSPResponseCache OCSPResponseCacheVarz `json:"ocsp_peer_cache,omitempty"` } // JetStreamVarz contains basic runtime information about jetstream @@ -1247,13 +1249,14 @@ type RemoteGatewayOptsVarz struct { // LeafNodeOptsVarz contains monitoring leaf node information type LeafNodeOptsVarz struct { - Host string `json:"host,omitempty"` - Port int `json:"port,omitempty"` - AuthTimeout float64 `json:"auth_timeout,omitempty"` - TLSTimeout float64 `json:"tls_timeout,omitempty"` - TLSRequired bool `json:"tls_required,omitempty"` - TLSVerify bool `json:"tls_verify,omitempty"` - Remotes []RemoteLeafOptsVarz `json:"remotes,omitempty"` + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + AuthTimeout float64 `json:"auth_timeout,omitempty"` + TLSTimeout float64 `json:"tls_timeout,omitempty"` + TLSRequired bool `json:"tls_required,omitempty"` + TLSVerify bool `json:"tls_verify,omitempty"` + Remotes []RemoteLeafOptsVarz `json:"remotes,omitempty"` + TLSOCSPPeerVerify bool `json:"tls_ocsp_peer_verify,omitempty"` } // DenyRules Contains lists of subjects not allowed to be imported/exported @@ -1264,41 +1267,55 @@ type DenyRules struct { // RemoteLeafOptsVarz contains monitoring remote leaf node information type RemoteLeafOptsVarz struct { - LocalAccount string `json:"local_account,omitempty"` - TLSTimeout float64 `json:"tls_timeout,omitempty"` - URLs []string `json:"urls,omitempty"` - Deny *DenyRules `json:"deny,omitempty"` + LocalAccount string `json:"local_account,omitempty"` + TLSTimeout float64 `json:"tls_timeout,omitempty"` + URLs []string `json:"urls,omitempty"` + Deny *DenyRules `json:"deny,omitempty"` + TLSOCSPPeerVerify bool `json:"tls_ocsp_peer_verify,omitempty"` } // MQTTOptsVarz contains monitoring MQTT information type MQTTOptsVarz struct { - Host string `json:"host,omitempty"` - Port int `json:"port,omitempty"` - NoAuthUser string `json:"no_auth_user,omitempty"` - AuthTimeout float64 `json:"auth_timeout,omitempty"` - TLSMap bool `json:"tls_map,omitempty"` - TLSTimeout float64 `json:"tls_timeout,omitempty"` - TLSPinnedCerts []string `json:"tls_pinned_certs,omitempty"` - JsDomain string `json:"js_domain,omitempty"` - AckWait time.Duration `json:"ack_wait,omitempty"` - MaxAckPending uint16 `json:"max_ack_pending,omitempty"` + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + NoAuthUser string `json:"no_auth_user,omitempty"` + AuthTimeout float64 `json:"auth_timeout,omitempty"` + TLSMap bool `json:"tls_map,omitempty"` + TLSTimeout float64 `json:"tls_timeout,omitempty"` + TLSPinnedCerts []string `json:"tls_pinned_certs,omitempty"` + JsDomain string `json:"js_domain,omitempty"` + AckWait time.Duration `json:"ack_wait,omitempty"` + MaxAckPending uint16 `json:"max_ack_pending,omitempty"` + TLSOCSPPeerVerify bool `json:"tls_ocsp_peer_verify,omitempty"` } // WebsocketOptsVarz contains monitoring websocket information type WebsocketOptsVarz struct { - Host string `json:"host,omitempty"` - Port int `json:"port,omitempty"` - Advertise string `json:"advertise,omitempty"` - NoAuthUser string `json:"no_auth_user,omitempty"` - JWTCookie string `json:"jwt_cookie,omitempty"` - HandshakeTimeout time.Duration `json:"handshake_timeout,omitempty"` - AuthTimeout float64 `json:"auth_timeout,omitempty"` - NoTLS bool `json:"no_tls,omitempty"` - TLSMap bool `json:"tls_map,omitempty"` - TLSPinnedCerts []string `json:"tls_pinned_certs,omitempty"` - SameOrigin bool `json:"same_origin,omitempty"` - AllowedOrigins []string `json:"allowed_origins,omitempty"` - Compression bool `json:"compression,omitempty"` + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + Advertise string `json:"advertise,omitempty"` + NoAuthUser string `json:"no_auth_user,omitempty"` + JWTCookie string `json:"jwt_cookie,omitempty"` + HandshakeTimeout time.Duration `json:"handshake_timeout,omitempty"` + AuthTimeout float64 `json:"auth_timeout,omitempty"` + NoTLS bool `json:"no_tls,omitempty"` + TLSMap bool `json:"tls_map,omitempty"` + TLSPinnedCerts []string `json:"tls_pinned_certs,omitempty"` + SameOrigin bool `json:"same_origin,omitempty"` + AllowedOrigins []string `json:"allowed_origins,omitempty"` + Compression bool `json:"compression,omitempty"` + TLSOCSPPeerVerify bool `json:"tls_ocsp_peer_verify,omitempty"` +} + +// OCSPResponseCacheVarz contains OCSP response cache information +type OCSPResponseCacheVarz struct { + Type string `json:"cache_type,omitempty"` + Hits int64 `json:"cache_hits,omitempty"` + Misses int64 `json:"cache_misses,omitempty"` + Responses int64 `json:"cached_responses,omitempty"` + Revokes int64 `json:"cached_revoked_responses,omitempty"` + Goods int64 `json:"cached_good_responses,omitempty"` + Unknowns int64 `json:"cached_unknown_responses,omitempty"` } // VarzOptions are the options passed to Varz(). @@ -1452,6 +1469,9 @@ func (s *Server) createVarz(pcpu float64, rss int64) *Varz { gatewayTlsReq := gw.TLSConfig != nil leafTlsReq := ln.TLSConfig != nil leafTlsVerify := leafTlsReq && ln.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert + leafTlsOCSPPeerVerify := s.ocspPeerVerify && leafTlsReq && ln.tlsConfigOpts.OCSPPeerConfig != nil && ln.tlsConfigOpts.OCSPPeerConfig.Verify + mqttTlsOCSPPeerVerify := s.ocspPeerVerify && mqtt.TLSConfig != nil && mqtt.tlsConfigOpts.OCSPPeerConfig != nil && mqtt.tlsConfigOpts.OCSPPeerConfig.Verify + wsTlsOCSPPeerVerify := s.ocspPeerVerify && ws.TLSConfig != nil && ws.tlsConfigOpts.OCSPPeerConfig != nil && ws.tlsConfigOpts.OCSPPeerConfig.Verify varz := &Varz{ ID: info.ID, Version: info.Version, @@ -1489,38 +1509,41 @@ func (s *Server) createVarz(pcpu float64, rss int64) *Varz { RejectUnknown: gw.RejectUnknown, }, LeafNode: LeafNodeOptsVarz{ - Host: ln.Host, - Port: ln.Port, - AuthTimeout: ln.AuthTimeout, - TLSTimeout: ln.TLSTimeout, - TLSRequired: leafTlsReq, - TLSVerify: leafTlsVerify, - Remotes: []RemoteLeafOptsVarz{}, + Host: ln.Host, + Port: ln.Port, + AuthTimeout: ln.AuthTimeout, + TLSTimeout: ln.TLSTimeout, + TLSRequired: leafTlsReq, + TLSVerify: leafTlsVerify, + TLSOCSPPeerVerify: leafTlsOCSPPeerVerify, + Remotes: []RemoteLeafOptsVarz{}, }, MQTT: MQTTOptsVarz{ - Host: mqtt.Host, - Port: mqtt.Port, - NoAuthUser: mqtt.NoAuthUser, - AuthTimeout: mqtt.AuthTimeout, - TLSMap: mqtt.TLSMap, - TLSTimeout: mqtt.TLSTimeout, - JsDomain: mqtt.JsDomain, - AckWait: mqtt.AckWait, - MaxAckPending: mqtt.MaxAckPending, + Host: mqtt.Host, + Port: mqtt.Port, + NoAuthUser: mqtt.NoAuthUser, + AuthTimeout: mqtt.AuthTimeout, + TLSMap: mqtt.TLSMap, + TLSTimeout: mqtt.TLSTimeout, + JsDomain: mqtt.JsDomain, + AckWait: mqtt.AckWait, + MaxAckPending: mqtt.MaxAckPending, + TLSOCSPPeerVerify: mqttTlsOCSPPeerVerify, }, Websocket: WebsocketOptsVarz{ - Host: ws.Host, - Port: ws.Port, - Advertise: ws.Advertise, - NoAuthUser: ws.NoAuthUser, - JWTCookie: ws.JWTCookie, - AuthTimeout: ws.AuthTimeout, - NoTLS: ws.NoTLS, - TLSMap: ws.TLSMap, - SameOrigin: ws.SameOrigin, - AllowedOrigins: copyStrings(ws.AllowedOrigins), - Compression: ws.Compression, - HandshakeTimeout: ws.HandshakeTimeout, + Host: ws.Host, + Port: ws.Port, + Advertise: ws.Advertise, + NoAuthUser: ws.NoAuthUser, + JWTCookie: ws.JWTCookie, + AuthTimeout: ws.AuthTimeout, + NoTLS: ws.NoTLS, + TLSMap: ws.TLSMap, + SameOrigin: ws.SameOrigin, + AllowedOrigins: copyStrings(ws.AllowedOrigins), + Compression: ws.Compression, + HandshakeTimeout: ws.HandshakeTimeout, + TLSOCSPPeerVerify: wsTlsOCSPPeerVerify, }, Start: s.start.UTC(), MaxSubs: opts.MaxSubs, @@ -1553,11 +1576,14 @@ func (s *Server) createVarz(pcpu float64, rss int64) *Varz { Exports: r.DenyExports, } } + remoteTlsOCSPPeerVerify := s.ocspPeerVerify && r.tlsConfigOpts != nil && r.tlsConfigOpts.OCSPPeerConfig != nil && r.tlsConfigOpts.OCSPPeerConfig.Verify + rlna[i] = RemoteLeafOptsVarz{ - LocalAccount: r.LocalAccount, - URLs: urlsToStrings(r.URLs), - TLSTimeout: r.TLSTimeout, - Deny: deny, + LocalAccount: r.LocalAccount, + URLs: urlsToStrings(r.URLs), + TLSTimeout: r.TLSTimeout, + Deny: deny, + TLSOCSPPeerVerify: remoteTlsOCSPPeerVerify, } } varz.LeafNode.Remotes = rlna @@ -1611,6 +1637,8 @@ func (s *Server) updateVarzConfigReloadableFields(v *Varz) { } v.MQTT.TLSPinnedCerts = getPinnedCertsAsSlice(opts.MQTT.TLSPinnedCerts) v.Websocket.TLSPinnedCerts = getPinnedCertsAsSlice(opts.Websocket.TLSPinnedCerts) + + v.TLSOCSPPeerVerify = s.ocspPeerVerify && v.TLSRequired && s.opts.tlsConfigOpts != nil && s.opts.tlsConfigOpts.OCSPPeerConfig != nil && s.opts.tlsConfigOpts.OCSPPeerConfig.Verify } func getPinnedCertsAsSlice(certs PinnedCertSet) []string { @@ -1702,6 +1730,21 @@ func (s *Server) updateVarzRuntimeFields(v *Varz, forceUpdate bool, pcpu float64 } } gw.RUnlock() + + if s.ocsprc != nil && s.ocsprc.Type() != "none" { + stats := s.ocsprc.Stats() + if stats != nil { + v.OCSPResponseCache = OCSPResponseCacheVarz{ + s.ocsprc.Type(), + stats.Hits, + stats.Misses, + stats.Responses, + stats.Revokes, + stats.Goods, + stats.Unknowns, + } + } + } } // HandleVarz will process HTTP requests for server information. @@ -3147,7 +3190,8 @@ func (s *Server) healthz(opts *HealthzOptions) *HealthStatus { for acc, asa := range cc.streams { nasa := make(map[string]*streamAssignment) for stream, sa := range asa { - if sa.Group.isMember(ourID) { + // If we are a member and we are not being restored, select for check. + if sa.Group.isMember(ourID) && sa.Restore == nil { csa := sa.copyGroup() csa.consumers = make(map[string]*consumerAssignment) for consumer, ca := range sa.consumers { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go index 2d214e011..8680e8dde 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go @@ -1,4 +1,4 @@ -// Copyright 2020-2021 The NATS Authors +// Copyright 2020-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -3407,8 +3407,8 @@ func mqttSubscribeTrace(pi uint16, filters []*mqttFilter) string { // message and this is the callback for a QoS1 subscription because in // that case, it will be handled by the other callback. This avoid getting // duplicate deliveries. -func mqttDeliverMsgCbQos0(sub *subscription, pc *client, _ *Account, subject, _ string, rmsg []byte) { - if pc.kind == JETSTREAM { +func mqttDeliverMsgCbQos0(sub *subscription, pc *client, _ *Account, subject, reply string, rmsg []byte) { + if pc.kind == JETSTREAM && len(reply) > 0 && strings.HasPrefix(reply, jsAckPre) { return } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/ocsp.go b/vendor/github.com/nats-io/nats-server/v2/server/ocsp.go index 9c5dcb938..cd950ae3c 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/ocsp.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/ocsp.go @@ -30,6 +30,9 @@ import ( "time" "golang.org/x/crypto/ocsp" + + "github.com/nats-io/nats-server/v2/server/certidp" + "github.com/nats-io/nats-server/v2/server/certstore" ) const ( @@ -389,7 +392,7 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni } // TODO: Add OCSP 'responder_cert' option in case CA cert not available. - issuers, err := getOCSPIssuer(caFile, cert.Certificate) + issuer, err := getOCSPIssuer(caFile, cert.Certificate) if err != nil { return nil, nil, err } @@ -402,7 +405,7 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni certFile: certFile, stopCh: make(chan struct{}, 1), Leaf: cert.Leaf, - Issuer: issuers[len(issuers)-1], + Issuer: issuer, } // Get the certificate status from the memory, then remote OCSP responder. @@ -448,21 +451,20 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni } chain := s.VerifiedChains[0] - leaf := chain[0] - parent := issuers[len(issuers)-1] + peerLeaf := chain[0] + peerIssuer := certidp.GetLeafIssuerCert(chain, 0) + if peerIssuer == nil { + return fmt.Errorf("failed to get issuer certificate for %s peer", kind) + } - resp, err := ocsp.ParseResponseForCert(oresp, leaf, parent) + // Response signature of issuer or issuer delegate is checked in the library parse + resp, err := ocsp.ParseResponseForCert(oresp, peerLeaf, peerIssuer) if err != nil { return fmt.Errorf("failed to parse OCSP response from %s peer: %w", kind, err) } - if resp.Certificate == nil { - if err := resp.CheckSignatureFrom(parent); err != nil { - return fmt.Errorf("OCSP staple not issued by issuer: %w", err) - } - } else { - if err := resp.Certificate.CheckSignatureFrom(parent); err != nil { - return fmt.Errorf("OCSP staple's signer not signed by issuer: %w", err) - } + + // If signer was issuer delegate double-check issuer delegate authorization + if resp.Certificate != nil { ok := false for _, eku := range resp.Certificate.ExtKeyUsage { if eku == x509.ExtKeyUsageOCSPSigning { @@ -474,6 +476,14 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni return fmt.Errorf("OCSP staple's signer missing authorization by CA to act as OCSP signer") } } + + // Check that the OCSP response is effective, take defaults for clockskew and default validity + peerOpts := certidp.OCSPPeerConfig{ClockSkew: -1, TTLUnsetNextUpdate: -1} + sLog := certidp.Log{Debugf: srv.Debugf} + if !certidp.OCSPResponseCurrent(resp, &peerOpts, &sLog) { + return fmt.Errorf("OCSP staple from %s peer not current", kind) + } + if resp.Status != ocsp.Good { return fmt.Errorf("bad status for OCSP Staple from %s peer: %s", kind, ocspStatusString(resp.Status)) } @@ -520,10 +530,11 @@ func (s *Server) setupOCSPStapleStoreDir() error { } type tlsConfigKind struct { - tlsConfig *tls.Config - tlsOpts *TLSConfigOpts - kind string - apply func(*tls.Config) + tlsConfig *tls.Config + tlsOpts *TLSConfigOpts + kind string + isLeafSpoke bool + apply func(*tls.Config) } func (s *Server) configureOCSP() []*tlsConfigKind { @@ -541,6 +552,26 @@ func (s *Server) configureOCSP() []*tlsConfigKind { } configs = append(configs, o) } + if config := sopts.Websocket.TLSConfig; config != nil { + opts := sopts.Websocket.tlsConfigOpts + o := &tlsConfigKind{ + kind: kindStringMap[CLIENT], + tlsConfig: config, + tlsOpts: opts, + apply: func(tc *tls.Config) { sopts.Websocket.TLSConfig = tc }, + } + configs = append(configs, o) + } + if config := sopts.MQTT.TLSConfig; config != nil { + opts := sopts.tlsConfigOpts + o := &tlsConfigKind{ + kind: kindStringMap[CLIENT], + tlsConfig: config, + tlsOpts: opts, + apply: func(tc *tls.Config) { sopts.MQTT.TLSConfig = tc }, + } + configs = append(configs, o) + } if config := sopts.Cluster.TLSConfig; config != nil { opts := sopts.Cluster.tlsConfigOpts o := &tlsConfigKind{ @@ -557,16 +588,7 @@ func (s *Server) configureOCSP() []*tlsConfigKind { kind: kindStringMap[LEAF], tlsConfig: config, tlsOpts: opts, - apply: func(tc *tls.Config) { - // RequireAndVerifyClientCert is used to tell a client that it - // should send the client cert to the server. - if opts.Verify { - tc.ClientAuth = tls.RequireAndVerifyClientCert - } - // We're a leaf hub server, so we must not set this. - tc.GetClientCertificate = nil - sopts.LeafNode.TLSConfig = tc - }, + apply: func(tc *tls.Config) { sopts.LeafNode.TLSConfig = tc }, } configs = append(configs, o) } @@ -576,14 +598,11 @@ func (s *Server) configureOCSP() []*tlsConfigKind { // in the apply func callback below. r, opts := remote, remote.tlsConfigOpts o := &tlsConfigKind{ - kind: kindStringMap[LEAF], - tlsConfig: config, - tlsOpts: opts, - apply: func(tc *tls.Config) { - // We're a leaf client, so we must not set this. - tc.GetCertificate = nil - r.TLSConfig = tc - }, + kind: kindStringMap[LEAF], + tlsConfig: config, + tlsOpts: opts, + isLeafSpoke: true, + apply: func(tc *tls.Config) { r.TLSConfig = tc }, } configs = append(configs, o) } @@ -605,9 +624,7 @@ func (s *Server) configureOCSP() []*tlsConfigKind { kind: kindStringMap[GATEWAY], tlsConfig: config, tlsOpts: opts, - apply: func(tc *tls.Config) { - gw.TLSConfig = tc - }, + apply: func(tc *tls.Config) { gw.TLSConfig = tc }, } configs = append(configs, o) } @@ -619,16 +636,33 @@ func (s *Server) enableOCSP() error { configs := s.configureOCSP() for _, config := range configs { - tc, mon, err := s.NewOCSPMonitor(config) - if err != nil { - return err - } - // Check if an OCSP stapling monitor is required for this certificate. - if mon != nil { - s.ocsps = append(s.ocsps, mon) - // Override the TLS config with one that follows OCSP. - config.apply(tc) + // We do not staple Leaf Hub and Leaf Spokes, use ocsp_peer + if config.kind != kindStringMap[LEAF] { + // OCSP Stapling feature, will also enable tls server peer check for gateway and route peers + tc, mon, err := s.NewOCSPMonitor(config) + if err != nil { + return err + } + // Check if an OCSP stapling monitor is required for this certificate. + if mon != nil { + s.ocsps = append(s.ocsps, mon) + + // Override the TLS config with one that follows OCSP stapling + config.apply(tc) + } + } + + // OCSP peer check (client mTLS, leaf mTLS, leaf remote TLS) + if config.kind == kindStringMap[CLIENT] || config.kind == kindStringMap[LEAF] { + tc, plugged, err := s.plugTLSOCSPPeer(config) + if err != nil { + return err + } + if plugged && tc != nil { + s.ocspPeerVerify = true + config.apply(tc) + } } } @@ -670,17 +704,39 @@ func (s *Server) reloadOCSP() error { // Restart the monitors under the new configuration. ocspm := make([]*OCSPMonitor, 0) - for _, config := range configs { - tc, mon, err := s.NewOCSPMonitor(config) - if err != nil { - return err - } - // Check if an OCSP stapling monitor is required for this certificate. - if mon != nil { - ocspm = append(ocspm, mon) - // Apply latest TLS configuration. - config.apply(tc) + // Reset server's ocspPeerVerify flag to re-detect at least one plugged OCSP peer + s.mu.Lock() + s.ocspPeerVerify = false + s.mu.Unlock() + s.stopOCSPResponseCache() + + for _, config := range configs { + // We do not staple Leaf Hub and Leaf Spokes, use ocsp_peer + if config.kind != kindStringMap[LEAF] { + tc, mon, err := s.NewOCSPMonitor(config) + if err != nil { + return err + } + // Check if an OCSP stapling monitor is required for this certificate. + if mon != nil { + ocspm = append(ocspm, mon) + + // Apply latest TLS configuration. + config.apply(tc) + } + } + + // OCSP peer check (client mTLS, leaf mTLS, leaf remote TLS) + if config.kind == kindStringMap[CLIENT] || config.kind == kindStringMap[LEAF] { + tc, plugged, err := s.plugTLSOCSPPeer(config) + if err != nil { + return err + } + if plugged && tc != nil { + s.ocspPeerVerify = true + config.apply(tc) + } } } @@ -692,6 +748,11 @@ func (s *Server) reloadOCSP() error { // Dispatch all goroutines once again. s.startOCSPMonitoring() + // Init and restart OCSP responder cache + s.stopOCSPResponseCache() + s.initOCSPResponseCache() + s.startOCSPResponseCache() + return nil } @@ -782,37 +843,81 @@ func parseCertPEM(name string) ([]*x509.Certificate, error) { return x509.ParseCertificates(pemBytes) } -// getOCSPIssuer returns a CA cert from the given path. If the path is empty, -// then this checks a given cert chain. If both are empty, then it returns an -// error. -func getOCSPIssuer(issuerCert string, chain [][]byte) ([]*x509.Certificate, error) { - var issuers []*x509.Certificate - var err error - switch { - case len(chain) == 1 && issuerCert == _EMPTY_: - err = fmt.Errorf("ocsp ca required in chain or configuration") - case issuerCert != _EMPTY_: - issuers, err = parseCertPEM(issuerCert) - case len(chain) > 1 && issuerCert == _EMPTY_: - issuers, err = x509.ParseCertificates(chain[1]) - default: - err = fmt.Errorf("invalid ocsp ca configuration") - } - if err != nil { - return nil, err +// getOCSPIssuerLocally determines a leaf's issuer from locally configured certificates +func getOCSPIssuerLocally(trustedCAs []*x509.Certificate, certBundle []*x509.Certificate) (*x509.Certificate, error) { + var vOpts x509.VerifyOptions + var leaf *x509.Certificate + trustedCAPool := x509.NewCertPool() + + // Require Leaf as first cert in bundle + if len(certBundle) > 0 { + leaf = certBundle[0] + } else { + return nil, fmt.Errorf("invalid ocsp ca configuration") } - if len(issuers) == 0 { - return nil, fmt.Errorf("no issuers found") - } - - for _, issuer := range issuers { - if !issuer.IsCA { - return nil, fmt.Errorf("%s invalid ca basic constraints: is not ca", issuer.Subject) + // Allow Issuer to be configured as second cert in bundle + if len(certBundle) > 1 { + // The operator may have misconfigured the cert bundle + issuerCandidate := certBundle[1] + err := issuerCandidate.CheckSignature(leaf.SignatureAlgorithm, leaf.RawTBSCertificate, leaf.Signature) + if err != nil { + return nil, fmt.Errorf("invalid issuer configuration: %w", err) + } else { + return issuerCandidate, nil } } - return issuers, nil + // Operator did not provide the Leaf Issuer in cert bundle second position + // so we will attempt to create at least one ordered verified chain from the + // trusted CA pool. + + // Specify CA trust store to validator; if unset, system trust store used + if len(trustedCAs) > 0 { + for _, ca := range trustedCAs { + trustedCAPool.AddCert(ca) + } + vOpts.Roots = trustedCAPool + } + + return certstore.GetLeafIssuer(leaf, vOpts), nil +} + +// getOCSPIssuer determines an issuer certificate from the cert (bundle) or the file-based CA trust store +func getOCSPIssuer(caFile string, chain [][]byte) (*x509.Certificate, error) { + var issuer *x509.Certificate + var trustedCAs []*x509.Certificate + var certBundle []*x509.Certificate + var err error + + // FIXME(tgb): extend if pluggable CA store provider added to NATS (i.e. other than PEM file) + + // Non-system default CA trust store passed + if caFile != _EMPTY_ { + trustedCAs, err = parseCertPEM(caFile) + if err != nil { + return nil, fmt.Errorf("failed to parse ca_file: %v", err) + } + } + + // Specify bundled intermediate CA store + for _, certBytes := range chain { + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse cert: %v", err) + } + certBundle = append(certBundle, cert) + } + + issuer, err = getOCSPIssuerLocally(trustedCAs, certBundle) + if err != nil || issuer == nil { + return nil, fmt.Errorf("no issuers found") + } + + if !issuer.IsCA { + return nil, fmt.Errorf("%s invalid ca basic constraints: is not ca", issuer.Subject) + } + return issuer, nil } func ocspStatusString(n int) string { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/ocsp_peer.go b/vendor/github.com/nats-io/nats-server/v2/server/ocsp_peer.go new file mode 100644 index 000000000..0ddcc0c80 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/ocsp_peer.go @@ -0,0 +1,405 @@ +// Copyright 2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "strings" + "time" + + "golang.org/x/crypto/ocsp" + + "github.com/nats-io/nats-server/v2/server/certidp" +) + +func parseOCSPPeer(v interface{}) (pcfg *certidp.OCSPPeerConfig, retError error) { + var lt token + defer convertPanicToError(<, &retError) + tk, v := unwrapValue(v, <) + cm, ok := v.(map[string]interface{}) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrIllegalPeerOptsConfig, v)} + } + pcfg = certidp.NewOCSPPeerConfig() + retError = nil + for mk, mv := range cm { + tk, mv = unwrapValue(mv, <) + switch strings.ToLower(mk) { + case "verify": + verify, ok := mv.(bool) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)} + } + pcfg.Verify = verify + case "allowed_clockskew": + at := float64(0) + switch mv := mv.(type) { + case int64: + at = float64(mv) + case float64: + at = mv + case string: + d, err := time.ParseDuration(mv) + if err != nil { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")} + } + at = d.Seconds() + default: + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")} + } + if at >= 0 { + pcfg.ClockSkew = at + } + case "ca_timeout": + at := float64(0) + switch mv := mv.(type) { + case int64: + at = float64(mv) + case float64: + at = mv + case string: + d, err := time.ParseDuration(mv) + if err != nil { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)} + } + at = d.Seconds() + default: + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")} + } + if at >= 0 { + pcfg.Timeout = at + } + case "cache_ttl_when_next_update_unset": + at := float64(0) + switch mv := mv.(type) { + case int64: + at = float64(mv) + case float64: + at = mv + case string: + d, err := time.ParseDuration(mv) + if err != nil { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)} + } + at = d.Seconds() + default: + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")} + } + if at >= 0 { + pcfg.TTLUnsetNextUpdate = at + } + case "warn_only": + warnOnly, ok := mv.(bool) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)} + } + pcfg.WarnOnly = warnOnly + case "unknown_is_good": + unknownIsGood, ok := mv.(bool) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)} + } + pcfg.UnknownIsGood = unknownIsGood + case "allow_when_ca_unreachable": + allowWhenCAUnreachable, ok := mv.(bool) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)} + } + pcfg.AllowWhenCAUnreachable = allowWhenCAUnreachable + default: + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)} + } + } + return pcfg, nil +} + +func peerFromVerifiedChains(chains [][]*x509.Certificate) *x509.Certificate { + if len(chains) == 0 || len(chains[0]) == 0 { + return nil + } + return chains[0][0] +} + +// plugTLSOCSPPeer will plug the TLS handshake lifecycle for client mTLS connections and Leaf connections +func (s *Server) plugTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) { + if config == nil || config.tlsConfig == nil { + return nil, false, errors.New(certidp.ErrUnableToPlugTLSEmptyConfig) + } + s.Debugf(certidp.DbgPlugTLSForKind, config.kind) + kind := config.kind + isSpoke := config.isLeafSpoke + tcOpts := config.tlsOpts + if tcOpts == nil || tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify { + return nil, false, nil + } + // peer is a tls client + if kind == kindStringMap[CLIENT] || (kind == kindStringMap[LEAF] && !isSpoke) { + if !tcOpts.Verify { + return nil, false, errors.New(certidp.ErrMTLSRequired) + } + return s.plugClientTLSOCSPPeer(config) + } + // peer is a tls server + if kind == kindStringMap[LEAF] && isSpoke { + return s.plugServerTLSOCSPPeer(config) + } + return nil, false, nil +} + +func (s *Server) plugClientTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) { + if config == nil || config.tlsConfig == nil || config.tlsOpts == nil { + return nil, false, errors.New(certidp.ErrUnableToPlugTLSClient) + } + tc := config.tlsConfig + tcOpts := config.tlsOpts + kind := config.kind + if tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify { + return tc, false, nil + } + tc.VerifyConnection = func(cs tls.ConnectionState) error { + if !s.tlsClientOCSPValid(cs.VerifiedChains, tcOpts.OCSPPeerConfig) { + s.sendOCSPPeerRejectEvent(kind, peerFromVerifiedChains(cs.VerifiedChains), certidp.MsgTLSClientRejectConnection) + return errors.New(certidp.MsgTLSClientRejectConnection) + } + return nil + } + return tc, true, nil +} + +func (s *Server) plugServerTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) { + if config == nil || config.tlsConfig == nil || config.tlsOpts == nil { + return nil, false, errors.New(certidp.ErrUnableToPlugTLSServer) + } + tc := config.tlsConfig + tcOpts := config.tlsOpts + kind := config.kind + if tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify { + return tc, false, nil + } + tc.VerifyConnection = func(cs tls.ConnectionState) error { + if !s.tlsServerOCSPValid(cs.VerifiedChains, tcOpts.OCSPPeerConfig) { + s.sendOCSPPeerRejectEvent(kind, peerFromVerifiedChains(cs.VerifiedChains), certidp.MsgTLSServerRejectConnection) + return errors.New(certidp.MsgTLSServerRejectConnection) + } + return nil + } + return tc, true, nil +} + +// tlsServerOCSPValid evaluates verified chains (post successful TLS handshake) against OCSP +// eligibility. A verified chain is considered OCSP Valid if either none of the links are +// OCSP eligible, or current "good" responses from the CA can be obtained for each eligible link. +// Upon first OCSP Valid chain found, the Server is deemed OCSP Valid. If none of the chains are +// OCSP Valid, the Server is deemed OCSP Invalid. A verified self-signed certificate (chain length 1) +// is also considered OCSP Valid. +func (s *Server) tlsServerOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool { + s.Debugf(certidp.DbgNumServerChains, len(chains)) + return s.peerOCSPValid(chains, opts) +} + +// tlsClientOCSPValid evaluates verified chains (post successful TLS handshake) against OCSP +// eligibility. A verified chain is considered OCSP Valid if either none of the links are +// OCSP eligible, or current "good" responses from the CA can be obtained for each eligible link. +// Upon first OCSP Valid chain found, the Client is deemed OCSP Valid. If none of the chains are +// OCSP Valid, the Client is deemed OCSP Invalid. A verified self-signed certificate (chain length 1) +// is also considered OCSP Valid. +func (s *Server) tlsClientOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool { + s.Debugf(certidp.DbgNumClientChains, len(chains)) + return s.peerOCSPValid(chains, opts) +} + +func (s *Server) peerOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool { + peer := peerFromVerifiedChains(chains) + if peer == nil { + s.Errorf(certidp.ErrPeerEmptyAutoReject) + return false + } + for ci, chain := range chains { + s.Debugf(certidp.DbgLinksInChain, ci, len(chain)) + // Self-signed certificate is Client OCSP Valid (no CA) + if len(chain) == 1 { + s.Debugf(certidp.DbgSelfSignedValid, ci) + return true + } + // Check if any of the links in the chain are OCSP eligible + chainEligible := false + var eligibleLinks []*certidp.ChainLink + // Iterate over links skipping the root cert which is not OCSP eligible (self == issuer) + for linkPos := 0; linkPos < len(chain)-1; linkPos++ { + cert := chain[linkPos] + link := &certidp.ChainLink{ + Leaf: cert, + } + if certidp.CertOCSPEligible(link) { + chainEligible = true + issuerCert := certidp.GetLeafIssuerCert(chain, linkPos) + if issuerCert == nil { + // unexpected chain condition, reject Client as OCSP Invalid + return false + } + link.Issuer = issuerCert + eligibleLinks = append(eligibleLinks, link) + } + } + // A trust-store verified chain that is not OCSP eligible is always OCSP Valid + if !chainEligible { + s.Debugf(certidp.DbgValidNonOCSPChain, ci) + return true + } + s.Debugf(certidp.DbgChainIsOCSPEligible, ci, len(eligibleLinks)) + // Chain has at least one OCSP eligible link, so check each eligible link; + // any link with a !good OCSP response chain OCSP Invalid + chainValid := true + for _, link := range eligibleLinks { + // if option selected, good could reflect either ocsp.Good or ocsp.Unknown + if badReason, good := s.certOCSPGood(link, opts); !good { + s.Debugf(badReason) + s.sendOCSPPeerChainlinkInvalidEvent(peer, link.Leaf, badReason) + chainValid = false + break + } + } + if chainValid { + s.Debugf(certidp.DbgChainIsOCSPValid, ci) + return true + } + } + // If we are here, all chains had OCSP eligible links, but none of the chains achieved OCSP valid + s.Debugf(certidp.DbgNoOCSPValidChains) + return false +} + +func (s *Server) certOCSPGood(link *certidp.ChainLink, opts *certidp.OCSPPeerConfig) (string, bool) { + if link == nil || link.Leaf == nil || link.Issuer == nil || link.OCSPWebEndpoints == nil || len(*link.OCSPWebEndpoints) < 1 { + return "Empty chainlink found", false + } + var err error + sLogs := &certidp.Log{ + Debugf: s.Debugf, + Noticef: s.Noticef, + Warnf: s.Warnf, + Errorf: s.Errorf, + Tracef: s.Tracef, + } + fingerprint := certidp.GenerateFingerprint(link.Leaf) + // Used for debug/operator only, not match + subj := certidp.GetSubjectDNForm(link.Leaf) + var rawResp []byte + var ocspr *ocsp.Response + var useCachedResp bool + var rc = s.ocsprc + var cachedRevocation bool + // Check our cache before calling out to the CA OCSP responder + s.Debugf(certidp.DbgCheckingCacheForCert, subj, fingerprint) + if rawResp = rc.Get(fingerprint, sLogs); len(rawResp) > 0 { + // Signature validation of CA's OCSP response occurs in ParseResponse + ocspr, err = ocsp.ParseResponse(rawResp, link.Issuer) + if err == nil && ocspr != nil { + // Check if OCSP Response delegation present and if so is valid + if !certidp.ValidDelegationCheck(link.Issuer, ocspr) { + // Invalid delegation was already in cache, purge it and don't use it + s.Debugf(certidp.MsgCachedOCSPResponseInvalid, subj) + rc.Delete(fingerprint, true, sLogs) + goto AFTERCACHE + } + if certidp.OCSPResponseCurrent(ocspr, opts, sLogs) { + s.Debugf(certidp.DbgCurrentResponseCached, certidp.GetStatusAssertionStr(ocspr.Status)) + useCachedResp = true + } else { + // Cached response is not current, delete it and tidy runtime stats to reflect a miss; + // if preserve_revoked is enabled, the cache will not delete the cached response + s.Debugf(certidp.DbgExpiredResponseCached, certidp.GetStatusAssertionStr(ocspr.Status)) + rc.Delete(fingerprint, true, sLogs) + } + // Regardless of currency, record a cached revocation found in case AllowWhenCAUnreachable is set + if ocspr.Status == ocsp.Revoked { + cachedRevocation = true + } + } else { + // Bogus cached assertion, purge it and don't use it + s.Debugf(certidp.MsgCachedOCSPResponseInvalid, subj, fingerprint) + rc.Delete(fingerprint, true, sLogs) + goto AFTERCACHE + } + } +AFTERCACHE: + if !useCachedResp { + // CA OCSP responder callout needed + rawResp, err = certidp.FetchOCSPResponse(link, opts, sLogs) + if err != nil || rawResp == nil || len(rawResp) == 0 { + s.Warnf(certidp.ErrCAResponderCalloutFail, subj, err) + if opts.WarnOnly { + s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj) + return _EMPTY_, true + } + if opts.AllowWhenCAUnreachable && !cachedRevocation { + // Link has no cached history of revocation, so allow it to pass + s.Warnf(certidp.MsgAllowWhenCAUnreachableOccurred, subj) + return _EMPTY_, true + } else if opts.AllowWhenCAUnreachable { + // Link has cached but expired revocation so reject when CA is unreachable + s.Warnf(certidp.MsgAllowWhenCAUnreachableOccurredCachedRevoke, subj) + } + return certidp.MsgFailedOCSPResponseFetch, false + } + // Signature validation of CA's OCSP response occurs in ParseResponse + ocspr, err = ocsp.ParseResponse(rawResp, link.Issuer) + if err == nil && ocspr != nil { + // Check if OCSP Response delegation present and if so is valid + if !certidp.ValidDelegationCheck(link.Issuer, ocspr) { + s.Warnf(certidp.MsgOCSPResponseDelegationInvalid, subj) + if opts.WarnOnly { + // Can't use bogus assertion, but warn-only set so allow link to pass + s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj) + return _EMPTY_, true + } + return fmt.Sprintf(certidp.MsgOCSPResponseDelegationInvalid, subj), false + } + if !certidp.OCSPResponseCurrent(ocspr, opts, sLogs) { + s.Warnf(certidp.ErrNewCAResponseNotCurrent, subj) + if opts.WarnOnly { + // Can't use non-effective assertion, but warn-only set so allow link to pass + s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj) + return _EMPTY_, true + } + return certidp.MsgOCSPResponseNotEffective, false + } + } else { + s.Errorf(certidp.ErrCAResponseParseFailed, subj, err) + if opts.WarnOnly { + // Can't use bogus assertion, but warn-only set so allow link to pass + s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj) + return _EMPTY_, true + } + return certidp.MsgFailedOCSPResponseParse, false + } + // cache the valid fetched CA OCSP Response + rc.Put(fingerprint, ocspr, subj, sLogs) + } + + // Whether through valid cache response available or newly fetched valid response, now check the status + if ocspr.Status == ocsp.Revoked || (ocspr.Status == ocsp.Unknown && !opts.UnknownIsGood) { + s.Warnf(certidp.ErrOCSPInvalidPeerLink, subj, certidp.GetStatusAssertionStr(ocspr.Status)) + if opts.WarnOnly { + s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj) + return _EMPTY_, true + } + return fmt.Sprintf(certidp.MsgOCSPResponseInvalidStatus, certidp.GetStatusAssertionStr(ocspr.Status)), false + } + s.Debugf(certidp.DbgOCSPValidPeerLink, subj) + return _EMPTY_, true +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/ocsp_responsecache.go b/vendor/github.com/nats-io/nats-server/v2/server/ocsp_responsecache.go new file mode 100644 index 000000000..b64c7fad4 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/ocsp_responsecache.go @@ -0,0 +1,636 @@ +// Copyright 2023 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/klauspost/compress/s2" + "golang.org/x/crypto/ocsp" + + "github.com/nats-io/nats-server/v2/server/certidp" +) + +const ( + OCSPResponseCacheDefaultDir = "_rc_" + OCSPResponseCacheDefaultFilename = "cache.json" + OCSPResponseCacheDefaultTempFilePrefix = "ocsprc-*" + OCSPResponseCacheMinimumSaveInterval = 1 * time.Second + OCSPResponseCacheDefaultSaveInterval = 5 * time.Minute +) + +type OCSPResponseCacheType int + +const ( + NONE OCSPResponseCacheType = iota + 1 + LOCAL +) + +var OCSPResponseCacheTypeMap = map[string]OCSPResponseCacheType{ + "none": NONE, + "local": LOCAL, +} + +type OCSPResponseCacheConfig struct { + Type OCSPResponseCacheType + LocalStore string + PreserveRevoked bool + SaveInterval float64 +} + +func NewOCSPResponseCacheConfig() *OCSPResponseCacheConfig { + return &OCSPResponseCacheConfig{ + Type: LOCAL, + LocalStore: OCSPResponseCacheDefaultDir, + PreserveRevoked: false, + SaveInterval: OCSPResponseCacheDefaultSaveInterval.Seconds(), + } +} + +type OCSPResponseCacheStats struct { + Responses int64 `json:"size"` + Hits int64 `json:"hits"` + Misses int64 `json:"misses"` + Revokes int64 `json:"revokes"` + Goods int64 `json:"goods"` + Unknowns int64 `json:"unknowns"` +} + +type OCSPResponseCacheItem struct { + Subject string `json:"subject,omitempty"` + CachedAt time.Time `json:"cached_at"` + RespStatus certidp.StatusAssertion `json:"resp_status"` + RespExpires time.Time `json:"resp_expires,omitempty"` + Resp []byte `json:"resp"` +} + +type OCSPResponseCache interface { + Put(key string, resp *ocsp.Response, subj string, log *certidp.Log) + Get(key string, log *certidp.Log) []byte + Delete(key string, miss bool, log *certidp.Log) + Type() string + Start(s *Server) + Stop(s *Server) + Online() bool + Config() *OCSPResponseCacheConfig + Stats() *OCSPResponseCacheStats +} + +// NoOpCache is a no-op implementation of OCSPResponseCache +type NoOpCache struct { + config *OCSPResponseCacheConfig + stats *OCSPResponseCacheStats + online bool + mu *sync.RWMutex +} + +func (c *NoOpCache) Put(_ string, _ *ocsp.Response, _ string, _ *certidp.Log) {} + +func (c *NoOpCache) Get(_ string, _ *certidp.Log) []byte { + return nil +} + +func (c *NoOpCache) Delete(_ string, _ bool, _ *certidp.Log) {} + +func (c *NoOpCache) Start(_ *Server) { + c.mu.Lock() + defer c.mu.Unlock() + c.stats = &OCSPResponseCacheStats{} + c.online = true +} + +func (c *NoOpCache) Stop(_ *Server) { + c.mu.Lock() + defer c.mu.Unlock() + c.online = false +} + +func (c *NoOpCache) Online() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.online +} + +func (c *NoOpCache) Type() string { + c.mu.RLock() + defer c.mu.RUnlock() + return "none" +} + +func (c *NoOpCache) Config() *OCSPResponseCacheConfig { + c.mu.RLock() + defer c.mu.RUnlock() + return c.config +} + +func (c *NoOpCache) Stats() *OCSPResponseCacheStats { + c.mu.RLock() + defer c.mu.RUnlock() + return c.stats +} + +// LocalCache is a local file implementation of OCSPResponseCache +type LocalCache struct { + config *OCSPResponseCacheConfig + stats *OCSPResponseCacheStats + online bool + cache map[string]OCSPResponseCacheItem + mu *sync.RWMutex + saveInterval time.Duration + dirty bool + timer *time.Timer +} + +// Put captures a CA OCSP response to the OCSP peer cache indexed by response fingerprint (a hash) +func (c *LocalCache) Put(key string, caResp *ocsp.Response, subj string, log *certidp.Log) { + c.mu.RLock() + if !c.online || caResp == nil || key == "" { + c.mu.RUnlock() + return + } + c.mu.RUnlock() + log.Debugf(certidp.DbgCachingResponse, subj, key) + rawC, err := c.Compress(caResp.Raw) + if err != nil { + log.Errorf(certidp.ErrResponseCompressFail, key, err) + return + } + log.Debugf(certidp.DbgAchievedCompression, float64(len(rawC))/float64(len(caResp.Raw))) + c.mu.Lock() + defer c.mu.Unlock() + // check if we are replacing and do stats + item, ok := c.cache[key] + if ok { + c.adjustStats(-1, item.RespStatus) + } + item = OCSPResponseCacheItem{ + Subject: subj, + CachedAt: time.Now().UTC().Round(time.Second), + RespStatus: certidp.StatusAssertionIntToVal[caResp.Status], + RespExpires: caResp.NextUpdate, + Resp: rawC, + } + c.cache[key] = item + c.adjustStats(1, item.RespStatus) + c.dirty = true +} + +// Get returns a CA OCSP response from the OCSP peer cache matching the response fingerprint (a hash) +func (c *LocalCache) Get(key string, log *certidp.Log) []byte { + c.mu.RLock() + defer c.mu.RUnlock() + if !c.online || key == "" { + return nil + } + val, ok := c.cache[key] + if ok { + atomic.AddInt64(&c.stats.Hits, 1) + log.Debugf(certidp.DbgCacheHit, key) + } else { + atomic.AddInt64(&c.stats.Misses, 1) + log.Debugf(certidp.DbgCacheMiss, key) + return nil + } + resp, err := c.Decompress(val.Resp) + if err != nil { + log.Errorf(certidp.ErrResponseDecompressFail, key, err) + return nil + } + return resp +} + +func (c *LocalCache) adjustStatsHitToMiss() { + atomic.AddInt64(&c.stats.Misses, 1) + atomic.AddInt64(&c.stats.Hits, -1) +} + +func (c *LocalCache) adjustStats(delta int64, rs certidp.StatusAssertion) { + if delta == 0 { + return + } + atomic.AddInt64(&c.stats.Responses, delta) + switch rs { + case ocsp.Good: + atomic.AddInt64(&c.stats.Goods, delta) + case ocsp.Revoked: + atomic.AddInt64(&c.stats.Revokes, delta) + case ocsp.Unknown: + atomic.AddInt64(&c.stats.Unknowns, delta) + } +} + +// Delete removes a CA OCSP response from the OCSP peer cache matching the response fingerprint (a hash) +func (c *LocalCache) Delete(key string, wasMiss bool, log *certidp.Log) { + c.mu.Lock() + defer c.mu.Unlock() + if !c.online || key == "" || c.config == nil { + return + } + item, ok := c.cache[key] + if !ok { + return + } + if item.RespStatus == ocsp.Revoked && c.config.PreserveRevoked { + log.Debugf(certidp.DbgPreservedRevocation, key) + if wasMiss { + c.adjustStatsHitToMiss() + } + return + } + log.Debugf(certidp.DbgDeletingCacheResponse, key) + delete(c.cache, key) + c.adjustStats(-1, item.RespStatus) + if wasMiss { + c.adjustStatsHitToMiss() + } + c.dirty = true +} + +// Start initializes the configured OCSP peer cache, loads a saved cache from disk (if present), and initializes runtime statistics +func (c *LocalCache) Start(s *Server) { + s.Debugf(certidp.DbgStartingCache) + c.loadCache(s) + c.initStats() + c.mu.Lock() + c.online = true + c.mu.Unlock() +} + +func (c *LocalCache) Stop(s *Server) { + c.mu.Lock() + s.Debugf(certidp.DbgStoppingCache) + c.online = false + c.timer.Stop() + c.mu.Unlock() + c.saveCache(s) +} + +func (c *LocalCache) Online() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.online +} + +func (c *LocalCache) Type() string { + c.mu.RLock() + defer c.mu.RUnlock() + return "local" +} + +func (c *LocalCache) Config() *OCSPResponseCacheConfig { + c.mu.RLock() + defer c.mu.RUnlock() + return c.config +} + +func (c *LocalCache) Stats() *OCSPResponseCacheStats { + c.mu.RLock() + defer c.mu.RUnlock() + if c.stats == nil { + return nil + } + stats := OCSPResponseCacheStats{ + Responses: c.stats.Responses, + Hits: c.stats.Hits, + Misses: c.stats.Misses, + Revokes: c.stats.Revokes, + Goods: c.stats.Goods, + Unknowns: c.stats.Unknowns, + } + return &stats +} + +func (c *LocalCache) initStats() { + c.mu.Lock() + defer c.mu.Unlock() + c.stats = &OCSPResponseCacheStats{} + c.stats.Hits = 0 + c.stats.Misses = 0 + c.stats.Responses = int64(len(c.cache)) + for _, resp := range c.cache { + switch resp.RespStatus { + case ocsp.Good: + c.stats.Goods++ + case ocsp.Revoked: + c.stats.Revokes++ + case ocsp.Unknown: + c.stats.Unknowns++ + } + } +} + +func (c *LocalCache) Compress(buf []byte) ([]byte, error) { + bodyLen := int64(len(buf)) + var output bytes.Buffer + writer := s2.NewWriter(&output) + input := bytes.NewReader(buf[:bodyLen]) + if n, err := io.CopyN(writer, input, bodyLen); err != nil { + return nil, fmt.Errorf(certidp.ErrCannotWriteCompressed, err) + } else if n != bodyLen { + return nil, fmt.Errorf(certidp.ErrTruncatedWrite, n, bodyLen) + } + if err := writer.Close(); err != nil { + return nil, fmt.Errorf(certidp.ErrCannotCloseWriter, err) + } + return output.Bytes(), nil +} + +func (c *LocalCache) Decompress(buf []byte) ([]byte, error) { + bodyLen := int64(len(buf)) + input := bytes.NewReader(buf[:bodyLen]) + reader := io.NopCloser(s2.NewReader(input)) + output, err := io.ReadAll(reader) + if err != nil { + return nil, fmt.Errorf(certidp.ErrCannotReadCompressed, err) + } + return output, reader.Close() +} + +func (c *LocalCache) loadCache(s *Server) { + d := s.opts.OCSPCacheConfig.LocalStore + if d == _EMPTY_ { + d = OCSPResponseCacheDefaultDir + } + f := OCSPResponseCacheDefaultFilename + store, err := filepath.Abs(path.Join(d, f)) + if err != nil { + s.Errorf(certidp.ErrLoadCacheFail, err) + return + } + s.Debugf(certidp.DbgLoadingCache, store) + c.mu.Lock() + defer c.mu.Unlock() + c.cache = make(map[string]OCSPResponseCacheItem) + dat, err := os.ReadFile(store) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + s.Debugf(certidp.DbgNoCacheFound) + } else { + s.Warnf(certidp.ErrLoadCacheFail, err) + } + return + } + err = json.Unmarshal(dat, &c.cache) + if err != nil { + // make sure clean cache + c.cache = make(map[string]OCSPResponseCacheItem) + s.Warnf(certidp.ErrLoadCacheFail, err) + c.dirty = true + return + } + c.dirty = false +} + +func (c *LocalCache) saveCache(s *Server) { + c.mu.RLock() + dirty := c.dirty + c.mu.RUnlock() + if !dirty { + return + } + s.Debugf(certidp.DbgCacheDirtySave) + var d string + if c.config.LocalStore != _EMPTY_ { + d = c.config.LocalStore + } else { + d = OCSPResponseCacheDefaultDir + } + f := OCSPResponseCacheDefaultFilename + store, err := filepath.Abs(path.Join(d, f)) + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + s.Debugf(certidp.DbgSavingCache, store) + if _, err := os.Stat(d); os.IsNotExist(err) { + err = os.Mkdir(d, defaultDirPerms) + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + } + tmp, err := os.CreateTemp(d, OCSPResponseCacheDefaultTempFilePrefix) + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + defer func() { + tmp.Close() + os.Remove(tmp.Name()) + }() // clean up any temp files + + // RW lock here because we're going to snapshot the cache to disk and mark as clean if successful + c.mu.Lock() + defer c.mu.Unlock() + dat, err := json.MarshalIndent(c.cache, "", " ") + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + cacheSize, err := tmp.Write(dat) + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + err = tmp.Sync() + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + err = tmp.Close() + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + // do the final swap and overwrite any old saved peer cache + err = os.Rename(tmp.Name(), store) + if err != nil { + s.Errorf(certidp.ErrSaveCacheFail, err) + return + } + c.dirty = false + s.Debugf(certidp.DbgCacheSaved, cacheSize) +} + +var OCSPResponseCacheUsage = ` +You may enable OCSP peer response cacheing at server configuration root level: + +(If no TLS blocks are configured with OCSP peer verification, ocsp_cache is ignored.) + + ... + # short form enables with defaults + ocsp_cache: true + + # if false or undefined and one or more TLS blocks are configured with OCSP peer verification, "none" is implied + + # long form includes settable options + ocsp_cache { + + # Cache type (default local) + type: local + + # Cache file directory for local-type cache (default _rc_ in current working directory) + local_store: "_rc_" + + # Ignore cache deletes if cached OCSP response is Revoked status (default false) + preserve_revoked: false + + # For local store, interval to save in-memory cache to disk in seconds (default 300 seconds, minimum 1 second) + save_interval: 300 + } + ... + +Note: Cache of server's own OCSP response (staple) is enabled using the 'ocsp' configuration option. +` + +func (s *Server) initOCSPResponseCache() { + // No mTLS OCSP or Leaf OCSP enablements, so no need to init cache + s.mu.RLock() + if !s.ocspPeerVerify { + s.mu.RUnlock() + return + } + s.mu.RUnlock() + so := s.getOpts() + if so.OCSPCacheConfig == nil { + so.OCSPCacheConfig = NewOCSPResponseCacheConfig() + } + var cc = so.OCSPCacheConfig + s.mu.Lock() + defer s.mu.Unlock() + switch cc.Type { + case NONE: + s.ocsprc = &NoOpCache{config: cc, online: true, mu: &sync.RWMutex{}} + case LOCAL: + c := &LocalCache{ + config: cc, + online: false, + cache: make(map[string]OCSPResponseCacheItem), + mu: &sync.RWMutex{}, + dirty: false, + } + c.saveInterval = time.Duration(cc.SaveInterval) * time.Second + c.timer = time.AfterFunc(c.saveInterval, func() { + s.Debugf(certidp.DbgCacheSaveTimerExpired) + c.saveCache(s) + c.timer.Reset(c.saveInterval) + }) + s.ocsprc = c + default: + s.Fatalf(certidp.ErrBadCacheTypeConfig, cc.Type) + } +} + +func (s *Server) startOCSPResponseCache() { + // No mTLS OCSP or Leaf OCSP enablements, so no need to start cache + s.mu.RLock() + if !s.ocspPeerVerify || s.ocsprc == nil { + s.mu.RUnlock() + return + } + s.mu.RUnlock() + + // Could be heavier operation depending on cache implementation + s.ocsprc.Start(s) + if s.ocsprc.Online() { + s.Noticef(certidp.MsgCacheOnline, s.ocsprc.Type()) + } else { + s.Noticef(certidp.MsgCacheOffline, s.ocsprc.Type()) + } +} + +func (s *Server) stopOCSPResponseCache() { + s.mu.RLock() + if s.ocsprc == nil { + s.mu.RUnlock() + return + } + s.mu.RUnlock() + s.ocsprc.Stop(s) +} + +func parseOCSPResponseCache(v interface{}) (pcfg *OCSPResponseCacheConfig, retError error) { + var lt token + defer convertPanicToError(<, &retError) + tk, v := unwrapValue(v, <) + cm, ok := v.(map[string]interface{}) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrIllegalCacheOptsConfig, v)} + } + pcfg = NewOCSPResponseCacheConfig() + retError = nil + for mk, mv := range cm { + // Again, unwrap token value if line check is required. + tk, mv = unwrapValue(mv, <) + switch strings.ToLower(mk) { + case "type": + cache, ok := mv.(string) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingCacheOptFieldGeneric, mk)} + } + cacheType, exists := OCSPResponseCacheTypeMap[strings.ToLower(cache)] + if !exists { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrUnknownCacheType, cache)} + } + pcfg.Type = cacheType + case "local_store": + store, ok := mv.(string) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingCacheOptFieldGeneric, mk)} + } + pcfg.LocalStore = store + case "preserve_revoked": + preserve, ok := mv.(bool) + if !ok { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingCacheOptFieldGeneric, mk)} + } + pcfg.PreserveRevoked = preserve + case "save_interval": + at := float64(0) + switch mv := mv.(type) { + case int64: + at = float64(mv) + case float64: + at = mv + case string: + d, err := time.ParseDuration(mv) + if err != nil { + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)} + } + at = d.Seconds() + default: + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingCacheOptFieldTypeConversion, "unexpected type")} + } + si := time.Duration(at) * time.Second + if si < OCSPResponseCacheMinimumSaveInterval { + si = OCSPResponseCacheMinimumSaveInterval + } + pcfg.SaveInterval = si.Seconds() + default: + return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingCacheOptFieldGeneric, mk)} + } + } + return pcfg, nil +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/opts.go b/vendor/github.com/nats-io/nats-server/v2/server/opts.go index 7c34bc212..a2b231aa1 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/opts.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/opts.go @@ -1,4 +1,4 @@ -// Copyright 2012-2022 The NATS Authors +// Copyright 2012-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -34,9 +34,10 @@ import ( "time" "github.com/nats-io/jwt/v2" - "github.com/nats-io/nkeys" - "github.com/nats-io/nats-server/v2/conf" + "github.com/nats-io/nats-server/v2/server/certidp" + "github.com/nats-io/nats-server/v2/server/certstore" + "github.com/nats-io/nkeys" ) var allowUnknownTopLevelField = int32(0) @@ -53,7 +54,7 @@ func NoErrOnUnknownFields(noError bool) { atomic.StoreInt32(&allowUnknownTopLevelField, val) } -// Set of lower case hex-encoded sha256 of DER encoded SubjectPublicKeyInfo +// PinnedCertSet is a set of lower case hex-encoded sha256 of DER encoded SubjectPublicKeyInfo type PinnedCertSet map[string]struct{} // ClusterOpts are options for clusters. @@ -221,6 +222,7 @@ type Options struct { NoHeaderSupport bool `json:"-"` DisableShortFirstPing bool `json:"-"` Logtime bool `json:"-"` + LogtimeUTC bool `json:"-"` MaxConn int `json:"max_connections"` MaxSubs int `json:"max_subscriptions,omitempty"` MaxSubTokens uint8 `json:"-"` @@ -340,6 +342,9 @@ type Options struct { // JetStream maxMemSet bool maxStoreSet bool + + // OCSP Cache config enables next-gen cache for OCSP features + OCSPCacheConfig *OCSPResponseCacheConfig } // WebsocketOpts are options for websocket @@ -403,6 +408,9 @@ type WebsocketOpts struct { // and write the response back to the client. This include the // time needed for the TLS Handshake. HandshakeTimeout time.Duration + + // Snapshot of configured TLS options. + tlsConfigOpts *TLSConfigOpts } // MQTTOpts are options for MQTT @@ -483,6 +491,9 @@ type MQTTOpts struct { // subscription ending with "#" will use 2 times the MaxAckPending value. // Note that changes to this option is applied only to new subscriptions. MaxAckPending uint16 + + // Snapshot of configured TLS options. + tlsConfigOpts *TLSConfigOpts } type netResolver interface { @@ -574,6 +585,10 @@ type TLSConfigOpts struct { Ciphers []uint16 CurvePreferences []tls.CurveID PinnedCerts PinnedCertSet + CertStore certstore.StoreType + CertMatchBy certstore.MatchByType + CertMatch string + OCSPPeerConfig *certidp.OCSPPeerConfig } // OCSPConfig represents the options of OCSP stapling options. @@ -786,6 +801,9 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error case "logtime": o.Logtime = v.(bool) trackExplicitVal(o, &o.inConfig, "Logtime", o.Logtime) + case "logtime_utc": + o.LogtimeUTC = v.(bool) + trackExplicitVal(o, &o.inConfig, "LogtimeUTC", o.LogtimeUTC) case "mappings", "maps": gacc := NewAccount(globalAccountName) o.Accounts = append(o.Accounts, gacc) @@ -1179,17 +1197,22 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error *errors = append(*errors, &configErr{tk, err.Error()}) return } - if dir == "" { - *errors = append(*errors, &configErr{tk, "dir has no value and needs to point to a directory"}) - return - } - if info, _ := os.Stat(dir); info != nil && (!info.IsDir() || info.Mode().Perm()&(1<<(uint(7))) == 0) { - *errors = append(*errors, &configErr{tk, "dir needs to point to an accessible directory"}) - return + + checkDir := func() { + if dir == _EMPTY_ { + *errors = append(*errors, &configErr{tk, "dir has no value and needs to point to a directory"}) + return + } + if info, _ := os.Stat(dir); info != nil && (!info.IsDir() || info.Mode().Perm()&(1<<(uint(7))) == 0) { + *errors = append(*errors, &configErr{tk, "dir needs to point to an accessible directory"}) + return + } } + var res AccountResolver switch strings.ToUpper(dirType) { case "CACHE": + checkDir() if sync != 0 { *errors = append(*errors, &configErr{tk, "CACHE does not accept sync"}) } @@ -1201,6 +1224,7 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error } res, err = NewCacheDirAccResolver(dir, limit, ttl, opts...) case "FULL": + checkDir() if ttl != 0 { *errors = append(*errors, &configErr{tk, "FULL does not accept ttl"}) } @@ -1216,6 +1240,8 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error } } res, err = NewDirAccResolver(dir, limit, sync, delete, opts...) + case "MEM", "MEMORY": + res = &MemAccResolver{} } if err != nil { *errors = append(*errors, &configErr{tk, err.Error()}) @@ -1393,6 +1419,34 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error m[kk] = v.(string) } o.JsAccDefaultDomain = m + case "ocsp_cache": + var err error + switch vv := v.(type) { + case bool: + pc := NewOCSPResponseCacheConfig() + if vv { + // Set enabled + pc.Type = LOCAL + o.OCSPCacheConfig = pc + } else { + // Set disabled (none cache) + pc.Type = NONE + o.OCSPCacheConfig = pc + } + case map[string]interface{}: + pc, err := parseOCSPResponseCache(v) + if err != nil { + *errors = append(*errors, err) + return + } + o.OCSPCacheConfig = pc + default: + err = &configErr{tk, fmt.Sprintf("error parsing tags: unsupported type %T", v)} + } + if err != nil { + *errors = append(*errors, err) + return + } default: if au := atomic.LoadInt32(&allowUnknownTopLevelField); au == 0 && !tk.IsUsedVariable() { err := &unknownConfigFieldErr{ @@ -3847,6 +3901,11 @@ func PrintTLSHelpAndDie() { for k := range curvePreferenceMap { fmt.Printf(" %s\n", k) } + if runtime.GOOS == "windows" { + fmt.Printf("%s\n", certstore.Usage) + } + fmt.Printf("%s", certidp.OCSPPeerUsage) + fmt.Printf("%s", OCSPResponseCacheUsage) os.Exit(0) } @@ -4004,6 +4063,54 @@ func parseTLS(v interface{}, isClientCtx bool) (t *TLSConfigOpts, retErr error) } tc.PinnedCerts = wl } + case "cert_store": + certStore, ok := mv.(string) + if !ok || certStore == _EMPTY_ { + return nil, &configErr{tk, certstore.ErrBadCertStoreField.Error()} + } + certStoreType, err := certstore.ParseCertStore(certStore) + if err != nil { + return nil, &configErr{tk, err.Error()} + } + tc.CertStore = certStoreType + case "cert_match_by": + certMatchBy, ok := mv.(string) + if !ok || certMatchBy == _EMPTY_ { + return nil, &configErr{tk, certstore.ErrBadCertMatchByField.Error()} + } + certMatchByType, err := certstore.ParseCertMatchBy(certMatchBy) + if err != nil { + return nil, &configErr{tk, err.Error()} + } + tc.CertMatchBy = certMatchByType + case "cert_match": + certMatch, ok := mv.(string) + if !ok || certMatch == _EMPTY_ { + return nil, &configErr{tk, certstore.ErrBadCertMatchField.Error()} + } + tc.CertMatch = certMatch + case "ocsp_peer": + switch vv := mv.(type) { + case bool: + pc := certidp.NewOCSPPeerConfig() + if vv { + // Set enabled + pc.Verify = true + tc.OCSPPeerConfig = pc + } else { + // Set disabled + pc.Verify = false + tc.OCSPPeerConfig = pc + } + case map[string]interface{}: + pc, err := parseOCSPPeer(mv) + if err != nil { + return nil, &configErr{tk, err.Error()} + } + tc.OCSPPeerConfig = pc + default: + return nil, &configErr{tk, fmt.Sprintf("error parsing ocsp peer config: unsupported type %T", v)} + } default: return nil, &configErr{tk, fmt.Sprintf("error parsing tls config, unknown field [%q]", mk)} } @@ -4134,6 +4241,7 @@ func parseWebsocket(v interface{}, o *Options, errors *[]error, warnings *[]erro } o.Websocket.TLSMap = tc.Map o.Websocket.TLSPinnedCerts = tc.PinnedCerts + o.Websocket.tlsConfigOpts = tc case "same_origin": o.Websocket.SameOrigin = mv.(bool) case "allowed_origins", "allowed_origin", "allow_origins", "allow_origin", "origins", "origin": @@ -4224,6 +4332,7 @@ func parseMQTT(v interface{}, o *Options, errors *[]error, warnings *[]error) er o.MQTT.TLSTimeout = tc.Timeout o.MQTT.TLSMap = tc.Map o.MQTT.TLSPinnedCerts = tc.PinnedCerts + o.MQTT.tlsConfigOpts = tc case "authorization", "authentication": auth := parseSimpleAuth(tk, errors, warnings) o.MQTT.Username = auth.user @@ -4290,11 +4399,13 @@ func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) { } switch { - case tc.CertFile != "" && tc.KeyFile == "": + case tc.CertFile != _EMPTY_ && tc.CertStore != certstore.STOREEMPTY: + return nil, certstore.ErrConflictCertFileAndStore + case tc.CertFile != _EMPTY_ && tc.KeyFile == _EMPTY_: return nil, fmt.Errorf("missing 'key_file' in TLS configuration") - case tc.CertFile == "" && tc.KeyFile != "": + case tc.CertFile == _EMPTY_ && tc.KeyFile != _EMPTY_: return nil, fmt.Errorf("missing 'cert_file' in TLS configuration") - case tc.CertFile != "" && tc.KeyFile != "": + case tc.CertFile != _EMPTY_ && tc.KeyFile != _EMPTY_: // Now load in cert and private key cert, err := tls.LoadX509KeyPair(tc.CertFile, tc.KeyFile) if err != nil { @@ -4305,6 +4416,11 @@ func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) { return nil, fmt.Errorf("error parsing certificate: %v", err) } config.Certificates = []tls.Certificate{cert} + case tc.CertStore != certstore.STOREEMPTY: + err := certstore.TLSConfig(tc.CertStore, tc.CertMatchBy, tc.CertMatch, &config) + if err != nil { + return nil, err + } } // Require client certificates as needed @@ -4690,9 +4806,10 @@ func ConfigureOptions(fs *flag.FlagSet, args []string, printVersion, printHelp, fs.BoolVar(&dbgAndTrcAndVerboseTrc, "DVV", false, "Enable Debug and Verbose Trace logging. (Traces system account as well)") fs.BoolVar(&opts.Logtime, "T", true, "Timestamp log entries.") fs.BoolVar(&opts.Logtime, "logtime", true, "Timestamp log entries.") - fs.StringVar(&opts.Username, "user", "", "Username required for connection.") - fs.StringVar(&opts.Password, "pass", "", "Password required for connection.") - fs.StringVar(&opts.Authorization, "auth", "", "Authorization token required for connection.") + fs.BoolVar(&opts.LogtimeUTC, "logtime_utc", false, "Timestamps in UTC instead of local timezone.") + fs.StringVar(&opts.Username, "user", _EMPTY_, "Username required for connection.") + fs.StringVar(&opts.Password, "pass", _EMPTY_, "Password required for connection.") + fs.StringVar(&opts.Authorization, "auth", _EMPTY_, "Authorization token required for connection.") fs.IntVar(&opts.HTTPPort, "m", 0, "HTTP Port for /varz, /connz endpoints.") fs.IntVar(&opts.HTTPPort, "http_port", 0, "HTTP Port for /varz, /connz endpoints.") fs.IntVar(&opts.HTTPSPort, "ms", 0, "HTTPS Port for /varz, /connz endpoints.") diff --git a/vendor/github.com/nats-io/nats-server/v2/server/reload.go b/vendor/github.com/nats-io/nats-server/v2/server/reload.go index a64f8286a..35e97b52e 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/reload.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/reload.go @@ -162,6 +162,17 @@ func (l *logtimeOption) Apply(server *Server) { server.Noticef("Reloaded: logtime = %v", l.newValue) } +// logtimeUTCOption implements the option interface for the `logtime_utc` setting. +type logtimeUTCOption struct { + loggingOption + newValue bool +} + +// Apply is a no-op because logging will be reloaded after options are applied. +func (l *logtimeUTCOption) Apply(server *Server) { + server.Noticef("Reloaded: logtime_utc = %v", l.newValue) +} + // logfileOption implements the option interface for the `log_file` setting. type logfileOption struct { loggingOption @@ -609,7 +620,7 @@ func (jso jetStreamOption) IsStatszChange() bool { } type ocspOption struct { - noopOption + tlsOption newValue *OCSPConfig } @@ -617,6 +628,15 @@ func (a *ocspOption) Apply(s *Server) { s.Noticef("Reloaded: OCSP") } +type ocspResponseCacheOption struct { + tlsOption + newValue *OCSPResponseCacheConfig +} + +func (a *ocspResponseCacheOption) Apply(s *Server) { + s.Noticef("Reloaded OCSP peer cache") +} + // connectErrorReports implements the option interface for the `connect_error_reports` // setting. type connectErrorReports struct { @@ -940,7 +960,7 @@ func imposeOrder(value interface{}) error { sort.Strings(value.AllowedOrigins) case string, bool, uint8, int, int32, int64, time.Duration, float64, nil, LeafNodeOpts, ClusterOpts, *tls.Config, PinnedCertSet, *URLAccResolver, *MemAccResolver, *DirAccResolver, *CacheDirAccResolver, Authentication, MQTTOpts, jwt.TagList, - *OCSPConfig, map[string]string, JSLimitOpts, StoreCipher: + *OCSPConfig, map[string]string, JSLimitOpts, StoreCipher, *OCSPResponseCacheConfig: // explicitly skipped types default: // this will fail during unit tests @@ -1009,6 +1029,8 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) { diffOpts = append(diffOpts, &debugOption{newValue: newValue.(bool)}) case "logtime": diffOpts = append(diffOpts, &logtimeOption{newValue: newValue.(bool)}) + case "logtimeutc": + diffOpts = append(diffOpts, &logtimeUTCOption{newValue: newValue.(bool)}) case "logfile": diffOpts = append(diffOpts, &logfileOption{newValue: newValue.(string)}) case "syslog": @@ -1264,8 +1286,8 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) { // Similar to gateways tmpOld := oldValue.(WebsocketOpts) tmpNew := newValue.(WebsocketOpts) - tmpOld.TLSConfig = nil - tmpNew.TLSConfig = nil + tmpOld.TLSConfig, tmpOld.tlsConfigOpts = nil, nil + tmpNew.TLSConfig, tmpNew.tlsConfigOpts = nil, nil // If there is really a change prevents reload. if !reflect.DeepEqual(tmpOld, tmpNew) { // See TODO(ik) note below about printing old/new values. @@ -1284,9 +1306,9 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) { // we only fail reload if some that we don't support are changed. tmpOld := oldValue.(MQTTOpts) tmpNew := newValue.(MQTTOpts) - tmpOld.TLSConfig, tmpOld.AckWait, tmpOld.MaxAckPending, tmpOld.StreamReplicas, tmpOld.ConsumerReplicas, tmpOld.ConsumerMemoryStorage = nil, 0, 0, 0, 0, false + tmpOld.TLSConfig, tmpOld.tlsConfigOpts, tmpOld.AckWait, tmpOld.MaxAckPending, tmpOld.StreamReplicas, tmpOld.ConsumerReplicas, tmpOld.ConsumerMemoryStorage = nil, nil, 0, 0, 0, 0, false tmpOld.ConsumerInactiveThreshold = 0 - tmpNew.TLSConfig, tmpNew.AckWait, tmpNew.MaxAckPending, tmpNew.StreamReplicas, tmpNew.ConsumerReplicas, tmpNew.ConsumerMemoryStorage = nil, 0, 0, 0, 0, false + tmpNew.TLSConfig, tmpNew.tlsConfigOpts, tmpNew.AckWait, tmpNew.MaxAckPending, tmpNew.StreamReplicas, tmpNew.ConsumerReplicas, tmpNew.ConsumerMemoryStorage = nil, nil, 0, 0, 0, 0, false tmpNew.ConsumerInactiveThreshold = 0 if !reflect.DeepEqual(tmpOld, tmpNew) { @@ -1339,6 +1361,8 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) { } case "ocspconfig": diffOpts = append(diffOpts, &ocspOption{newValue: newValue.(*OCSPConfig)}) + case "ocspcacheconfig": + diffOpts = append(diffOpts, &ocspResponseCacheOption{newValue: newValue.(*OCSPResponseCacheConfig)}) default: // TODO(ik): Implement String() on those options to have a nice print. // %v is difficult to figure what's what, %+v print private fields and @@ -1476,10 +1500,12 @@ func (s *Server) applyOptions(ctx *reloadContext, opts []option) { s.updateRemoteLeafNodesTLSConfig(newOpts) } + // This will fire if TLS enabled at root (NATS listener) -or- if ocsp or ocsp_cache + // appear in the config. if reloadTLS { // Restart OCSP monitoring. if err := s.reloadOCSP(); err != nil { - s.Warnf("Can't restart OCSP Stapling: %v", err) + s.Warnf("Can't restart OCSP features: %v", err) } } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/server.go b/vendor/github.com/nats-io/nats-server/v2/server/server.go index ec6239900..11e715b9b 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/server.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/server.go @@ -246,6 +246,12 @@ type Server struct { // OCSP monitoring ocsps []*OCSPMonitor + // OCSP peer verification (at least one TLS block) + ocspPeerVerify bool + + // OCSP response cache + ocsprc OCSPResponseCache + // exporting account name the importer experienced issues with incompleteAccExporterMap sync.Map @@ -453,8 +459,8 @@ func NewServer(opts *Options) (*Server, error) { // Ensure that non-exported options (used in tests) are properly set. s.setLeafNodeNonExportedOptions() - // Setup OCSP Stapling. This will abort server from starting if there - // are no valid staples and OCSP policy is set to Always or MustStaple. + // Setup OCSP Stapling and OCSP Peer. This will abort server from starting if there + // are no valid staples and OCSP Stapling policy is set to Always or MustStaple. if err := s.enableOCSP(); err != nil { return nil, err } @@ -753,6 +759,12 @@ func (s *Server) configureAccounts(reloading bool) (map[string]struct{}, error) opts := s.getOpts() + // We need to track service imports since we can not swap them out (unsub and re-sub) + // until the proper server struct accounts have been swapped in properly. Doing it in + // place could lead to data loss or server panic since account under new si has no real + // account and hence no sublist, so will panic on inbound message. + siMap := make(map[*Account][][]byte) + // Check opts and walk through them. We need to copy them here // so that we do not keep a real one sitting in the options. for _, acc := range opts.Accounts { @@ -773,12 +785,16 @@ func (s *Server) configureAccounts(reloading bool) (map[string]struct{}, error) // Collect the sids for the service imports since we are going to // replace with new ones. var sids [][]byte - c := a.ic for _, si := range a.imports.services { - if c != nil && si.sid != nil { + if si.sid != nil { sids = append(sids, si.sid) } } + // Setup to process later if needed. + if len(sids) > 0 || len(acc.imports.services) > 0 { + siMap[a] = sids + } + // Now reset all export/imports fields since they are going to be // filled in shallowCopy() a.imports.streams, a.imports.services = nil, nil @@ -787,14 +803,6 @@ func (s *Server) configureAccounts(reloading bool) (map[string]struct{}, error) // and pass `a` (our existing account) to get it updated. acc.shallowCopy(a) a.mu.Unlock() - // Need to release the lock for this. - s.mu.Unlock() - for _, sid := range sids { - c.processUnsub(sid) - } - // Add subscriptions for existing service imports. - a.addAllServiceImportSubs() - s.mu.Lock() create = false } } @@ -862,6 +870,7 @@ func (s *Server) configureAccounts(reloading bool) (map[string]struct{}, error) for _, si := range acc.imports.services { if v, ok := s.accounts.Load(si.acc.Name); ok { si.acc = v.(*Account) + // It is possible to allow for latency tracking inside your // own account, so lock only when not the same account. if si.acc == acc { @@ -889,6 +898,19 @@ func (s *Server) configureAccounts(reloading bool) (map[string]struct{}, error) return true }) + // Check if we need to process service imports pending from above. + // This processing needs to be after we swap in the real accounts above. + for acc, sids := range siMap { + c := acc.ic + for _, sid := range sids { + c.processUnsub(sid) + } + acc.addAllServiceImportSubs() + s.mu.Unlock() + s.registerSystemImports(acc) + s.mu.Lock() + } + // Set the system account if it was configured. // Otherwise create a default one. if opts.SystemAccount != _EMPTY_ { @@ -1888,9 +1910,13 @@ func (s *Server) Start() { } } - // Start OCSP Stapling monitoring for TLS certificates if enabled. + // Start OCSP Stapling monitoring for TLS certificates if enabled. Hook TLS handshake for + // OCSP check on peers (LEAF and CLIENT kind) if enabled. s.startOCSPMonitoring() + // Configure OCSP Response Cache for peer OCSP checks if enabled. + s.initOCSPResponseCache() + // Start up gateway if needed. Do this before starting the routes, because // we want to resolve the gateway host:port so that this information can // be sent to other routes. @@ -1957,6 +1983,9 @@ func (s *Server) Start() { if !opts.DontListen { s.AcceptLoop(clientListenReady) } + + // Bring OSCP Response cache online after accept loop started in anticipation of NATS-enabled cache types + s.startOCSPResponseCache() } // Shutdown will shutdown the server instance by kicking out the AcceptLoop @@ -2117,6 +2146,12 @@ func (s *Server) Shutdown() { } s.Noticef("Server Exiting..") + + // Stop OCSP Response Cache + if s.ocsprc != nil { + s.ocsprc.Stop(s) + } + // Close logger if applicable. It allows tests on Windows // to be able to do proper cleanup (delete log file). s.logging.RLock() @@ -2217,7 +2252,7 @@ func (s *Server) AcceptLoop(clr chan struct{}) { func (s *Server) InProcessConn() (net.Conn, error) { pl, pr := net.Pipe() if !s.startGoRoutine(func() { - s.createClient(pl) + s.createClientInProcess(pl) s.grWG.Done() }) { pl.Close() @@ -2572,6 +2607,14 @@ func (c *tlsMixConn) Read(b []byte) (int, error) { } func (s *Server) createClient(conn net.Conn) *client { + return s.createClientEx(conn, false) +} + +func (s *Server) createClientInProcess(conn net.Conn) *client { + return s.createClientEx(conn, true) +} + +func (s *Server) createClientEx(conn net.Conn, inProcess bool) *client { // Snapshot server options. opts := s.getOpts() @@ -2609,6 +2652,13 @@ func (s *Server) createClient(conn net.Conn) *client { info.AuthRequired = false } + // Check to see if this is an in-process connection with tls_required. + // If so, set as not required, but available. + if inProcess && info.TLSRequired { + info.TLSRequired = false + info.TLSAvailable = true + } + s.totalClients++ s.mu.Unlock() @@ -2670,8 +2720,9 @@ func (s *Server) createClient(conn net.Conn) *client { var pre []byte // If we have both TLS and non-TLS allowed we need to see which - // one the client wants. - if !isClosed && opts.TLSConfig != nil && opts.AllowNonTLS { + // one the client wants. We'll always allow this for in-process + // connections. + if !isClosed && opts.TLSConfig != nil && (inProcess || opts.AllowNonTLS) { pre = make([]byte, 4) c.nc.SetReadDeadline(time.Now().Add(secondsToDuration(opts.TLSTimeout))) n, _ := io.ReadFull(c.nc, pre[:]) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stream.go b/vendor/github.com/nats-io/nats-server/v2/server/stream.go index 81c13e659..d03306d74 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stream.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stream.go @@ -838,6 +838,14 @@ func (mset *stream) lastSeqAndCLFS() (uint64, uint64) { return mset.lseq, mset.clfs } +func (mset *stream) clearCLFS() uint64 { + mset.mu.Lock() + defer mset.mu.Unlock() + clfs := mset.clfs + mset.clfs, mset.clseq = 0, 0 + return clfs +} + func (mset *stream) lastSeq() uint64 { mset.mu.RLock() lseq := mset.lseq @@ -2093,7 +2101,7 @@ func (mset *stream) processInboundMirrorMsg(m *inMsg) bool { var err error if node != nil { if js.limitsExceeded(stype) { - s.resourcesExeededError() + s.resourcesExceededError() err = ApiErrors[JSInsufficientResourcesErr] } else { err = node.Propose(encodeStreamMsg(m.subj, _EMPTY_, m.hdr, m.msg, sseq-1, ts)) @@ -3364,9 +3372,9 @@ func (mset *stream) setupStore(fsCfg *FileStoreConfig) error { // Register our server. fs.registerServer(s) } - mset.mu.Unlock() - + // This will fire the callback but we do not require the lock since md will be 0 here. mset.store.RegisterStorageUpdates(mset.storeUpdates) + mset.mu.Unlock() return nil } @@ -3838,7 +3846,7 @@ func (mset *stream) processJetStreamMsg(subject, reply string, hdr, msg []byte, } // Expected last sequence per subject. // If we are clustered we have prechecked seq > 0. - if seq, exists := getExpectedLastSeqPerSubject(hdr); exists && (!isClustered || seq == 0) { + if seq, exists := getExpectedLastSeqPerSubject(hdr); exists { // TODO(dlc) - We could make a new store func that does this all in one. var smv StoreMsg var fseq uint64 @@ -3952,7 +3960,7 @@ func (mset *stream) processJetStreamMsg(subject, reply string, hdr, msg []byte, // Check to see if we have exceeded our limits. if js.limitsExceeded(stype) { - s.resourcesExeededError() + s.resourcesExceededError() mset.clfs++ mset.mu.Unlock() if canRespond { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/sublist.go b/vendor/github.com/nats-io/nats-server/v2/server/sublist.go index 48375b6b4..d7ffac0c9 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/sublist.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/sublist.go @@ -48,7 +48,7 @@ const ( // cacheMax is used to bound limit the frontend cache slCacheMax = 1024 // If we run a sweeper we will drain to this count. - slCacheSweep = 512 + slCacheSweep = 256 // plistMin is our lower bounds to create a fast plist for Match. plistMin = 256 ) diff --git a/vendor/github.com/nats-io/nats.go/.travis.yml b/vendor/github.com/nats-io/nats.go/.travis.yml index a5f06f459..962109d90 100644 --- a/vendor/github.com/nats-io/nats.go/.travis.yml +++ b/vendor/github.com/nats-io/nats.go/.travis.yml @@ -25,3 +25,17 @@ script: - if [[ "$TRAVIS_GO_VERSION" =~ 1.20 ]]; then ./scripts/cov.sh TRAVIS; else go test -modfile=go_test.mod -race -v -p=1 ./... --failfast -vet=off; fi after_success: - if [[ "$TRAVIS_GO_VERSION" =~ 1.20 ]]; then $HOME/gopath/bin/goveralls -coverprofile=acc.out -service travis-ci; fi + +jobs: + include: + - name: "Go: 1.20.x (nats-server@dev)" + go: "1.20.x" + before_script: + - go get -modfile go_test.mod github.com/nats-io/nats-server/v2@dev + - name: "Go: 1.20.x (nats-server@main)" + go: "1.20.x" + before_script: + - go get -modfile go_test.mod github.com/nats-io/nats-server/v2@main + allow_failures: + - name: "Go: 1.20.x (nats-server@dev)" + - name: "Go: 1.20.x (nats-server@main)" diff --git a/vendor/github.com/nats-io/nats.go/README.md b/vendor/github.com/nats-io/nats.go/README.md index 02e3d4efd..b38f98d0a 100644 --- a/vendor/github.com/nats-io/nats.go/README.md +++ b/vendor/github.com/nats-io/nats.go/README.md @@ -29,7 +29,7 @@ When using or transitioning to Go modules support: ```bash # Go client latest or explicit version go get github.com/nats-io/nats.go/@latest -go get github.com/nats-io/nats.go/@v1.27.0 +go get github.com/nats-io/nats.go/@v1.28.0 # For latest NATS Server, add /v2 at the end go get github.com/nats-io/nats-server/v2 @@ -90,91 +90,47 @@ nc.Drain() nc.Close() ``` -## JetStream Basic Usage +## JetStream -> __NOTE__ -> -> We encourage you to try out a new, simplified version on JetStream API. -> The new API is currently in preview and is available under `jetstream` package. -> -> You can find more information on the new API [here](https://github.com/nats-io/nats.go/blob/main/jetstream/README.md) +JetStream is the built-in NATS persistence system. `nats.go` provides a built-in +API enabling both managing JetStream assets as well as publishing/consuming +persistent messages. + +### Basic usage ```go -import "github.com/nats-io/nats.go" - -// Connect to NATS +// connect to nats server nc, _ := nats.Connect(nats.DefaultURL) -// Create JetStream Context -js, _ := nc.JetStream(nats.PublishAsyncMaxPending(256)) +// create jetstream context from nats connection +js, _ := jetstream.New(nc) -// Simple Stream Publisher -js.Publish("ORDERS.scratch", []byte("hello")) +ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) +defer cancel() -// Simple Async Stream Publisher -for i := 0; i < 500; i++ { - js.PublishAsync("ORDERS.scratch", []byte("hello")) -} -select { -case <-js.PublishAsyncComplete(): -case <-time.After(5 * time.Second): - fmt.Println("Did not resolve in time") -} +// get existing stream handle +stream, _ := js.Stream(ctx, "foo") -// Simple Async Ephemeral Consumer -js.Subscribe("ORDERS.*", func(m *nats.Msg) { - fmt.Printf("Received a JetStream message: %s\n", string(m.Data)) +// retrieve consumer handle from a stream +cons, _ := stream.Consumer(ctx, "cons") + +// consume messages from the consumer in callback +cc, _ := cons.Consume(func(msg jetstream.Msg) { + fmt.Println("Received jetstream message: ", string(msg.Data())) + msg.Ack() }) - -// Simple Sync Durable Consumer (optional SubOpts at the end) -sub, err := js.SubscribeSync("ORDERS.*", nats.Durable("MONITOR"), nats.MaxDeliver(3)) -m, err := sub.NextMsg(timeout) - -// Simple Pull Consumer -sub, err := js.PullSubscribe("ORDERS.*", "MONITOR") -msgs, err := sub.Fetch(10) - -// Unsubscribe -sub.Unsubscribe() - -// Drain -sub.Drain() +defer cc.Stop() ``` -## JetStream Basic Management +To find more information on `nats.go` JetStream API, visit +[`jetstream/README.md`](jetstream/README.md) -```go -import "github.com/nats-io/nats.go" +> The current JetStream API replaces the [legacy JetStream API](legacy_jetstream.md) -// Connect to NATS -nc, _ := nats.Connect(nats.DefaultURL) +## Service API -// Create JetStream Context -js, _ := nc.JetStream() - -// Create a Stream -js.AddStream(&nats.StreamConfig{ - Name: "ORDERS", - Subjects: []string{"ORDERS.*"}, -}) - -// Update a Stream -js.UpdateStream(&nats.StreamConfig{ - Name: "ORDERS", - MaxBytes: 8, -}) - -// Create a Consumer -js.AddConsumer("ORDERS", &nats.ConsumerConfig{ - Durable: "MONITOR", -}) - -// Delete Consumer -js.DeleteConsumer("ORDERS", "MONITOR") - -// Delete Stream -js.DeleteStream("ORDERS") -``` +The service API (`micro`) allows you to [easily build NATS services](micro/README.md) The +services API is currently in beta release. ## Encoded Connections diff --git a/vendor/github.com/nats-io/nats.go/context.go b/vendor/github.com/nats-io/nats.go/context.go index 8c3c45478..c4ef4be17 100644 --- a/vendor/github.com/nats-io/nats.go/context.go +++ b/vendor/github.com/nats-io/nats.go/context.go @@ -217,7 +217,7 @@ func (nc *Conn) FlushWithContext(ctx context.Context) error { // RequestWithContext will create an Inbox and perform a Request // using the provided cancellation context with the Inbox reply // for the data v. A response will be decoded into the vPtr last parameter. -func (c *EncodedConn) RequestWithContext(ctx context.Context, subject string, v interface{}, vPtr interface{}) error { +func (c *EncodedConn) RequestWithContext(ctx context.Context, subject string, v any, vPtr any) error { if ctx == nil { return ErrInvalidContext } diff --git a/vendor/github.com/nats-io/nats.go/enc.go b/vendor/github.com/nats-io/nats.go/enc.go index 450fa76c8..a1c54f246 100644 --- a/vendor/github.com/nats-io/nats.go/enc.go +++ b/vendor/github.com/nats-io/nats.go/enc.go @@ -26,8 +26,8 @@ import ( // Encoder interface is for all register encoders type Encoder interface { - Encode(subject string, v interface{}) ([]byte, error) - Decode(subject string, data []byte, vPtr interface{}) error + Encode(subject string, v any) ([]byte, error) + Decode(subject string, data []byte, vPtr any) error } var encMap map[string]Encoder @@ -88,7 +88,7 @@ func EncoderForType(encType string) Encoder { // Publish publishes the data argument to the given subject. The data argument // will be encoded using the associated encoder. -func (c *EncodedConn) Publish(subject string, v interface{}) error { +func (c *EncodedConn) Publish(subject string, v any) error { b, err := c.Enc.Encode(subject, v) if err != nil { return err @@ -99,7 +99,7 @@ func (c *EncodedConn) Publish(subject string, v interface{}) error { // PublishRequest will perform a Publish() expecting a response on the // reply subject. Use Request() for automatically waiting for a response // inline. -func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error { +func (c *EncodedConn) PublishRequest(subject, reply string, v any) error { b, err := c.Enc.Encode(subject, v) if err != nil { return err @@ -110,7 +110,7 @@ func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error // Request will create an Inbox and perform a Request() call // with the Inbox reply for the data v. A response will be // decoded into the vPtr Response. -func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error { +func (c *EncodedConn) Request(subject string, v any, vPtr any, timeout time.Duration) error { b, err := c.Enc.Encode(subject, v) if err != nil { return err @@ -129,7 +129,7 @@ func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, t } // Handler is a specific callback used for Subscribe. It is generalized to -// an interface{}, but we will discover its format and arguments at runtime +// an any, but we will discover its format and arguments at runtime // and perform the correct callback, including demarshaling encoded data // back into the appropriate struct based on the signature of the Handler. // @@ -150,7 +150,7 @@ func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, t // and demarshal it into the given struct, e.g. person. // There are also variants where the callback wants either the subject, or the // subject and the reply subject. -type Handler interface{} +type Handler any // Dissect the cb Handler's signature func argInfo(cb Handler) (reflect.Type, int) { diff --git a/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go b/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go index 46d918eea..65c2d68bb 100644 --- a/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go +++ b/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go @@ -35,7 +35,7 @@ var falseB = []byte("false") var nilB = []byte("") // Encode -func (je *DefaultEncoder) Encode(subject string, v interface{}) ([]byte, error) { +func (je *DefaultEncoder) Encode(subject string, v any) ([]byte, error) { switch arg := v.(type) { case string: bytes := *(*[]byte)(unsafe.Pointer(&arg)) @@ -58,7 +58,7 @@ func (je *DefaultEncoder) Encode(subject string, v interface{}) ([]byte, error) } // Decode -func (je *DefaultEncoder) Decode(subject string, data []byte, vPtr interface{}) error { +func (je *DefaultEncoder) Decode(subject string, data []byte, vPtr any) error { // Figure out what it's pointing to... sData := *(*string)(unsafe.Pointer(&data)) switch arg := vPtr.(type) { diff --git a/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go b/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go index 632bcbd39..4e7cecba2 100644 --- a/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go +++ b/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go @@ -28,7 +28,7 @@ type GobEncoder struct { // FIXME(dlc) - This could probably be more efficient. // Encode -func (ge *GobEncoder) Encode(subject string, v interface{}) ([]byte, error) { +func (ge *GobEncoder) Encode(subject string, v any) ([]byte, error) { b := new(bytes.Buffer) enc := gob.NewEncoder(b) if err := enc.Encode(v); err != nil { @@ -38,7 +38,7 @@ func (ge *GobEncoder) Encode(subject string, v interface{}) ([]byte, error) { } // Decode -func (ge *GobEncoder) Decode(subject string, data []byte, vPtr interface{}) (err error) { +func (ge *GobEncoder) Decode(subject string, data []byte, vPtr any) (err error) { dec := gob.NewDecoder(bytes.NewBuffer(data)) err = dec.Decode(vPtr) return diff --git a/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go b/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go index c9670f313..9b6ffc017 100644 --- a/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go +++ b/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go @@ -26,7 +26,7 @@ type JsonEncoder struct { } // Encode -func (je *JsonEncoder) Encode(subject string, v interface{}) ([]byte, error) { +func (je *JsonEncoder) Encode(subject string, v any) ([]byte, error) { b, err := json.Marshal(v) if err != nil { return nil, err @@ -35,7 +35,7 @@ func (je *JsonEncoder) Encode(subject string, v interface{}) ([]byte, error) { } // Decode -func (je *JsonEncoder) Decode(subject string, data []byte, vPtr interface{}) (err error) { +func (je *JsonEncoder) Decode(subject string, data []byte, vPtr any) (err error) { switch arg := vPtr.(type) { case *string: // If they want a string and it is a JSON string, strip quotes diff --git a/vendor/github.com/nats-io/nats.go/go_test.mod b/vendor/github.com/nats-io/nats.go/go_test.mod index ebdf14346..af69c5ab2 100644 --- a/vendor/github.com/nats-io/nats.go/go_test.mod +++ b/vendor/github.com/nats-io/nats.go/go_test.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/golang/protobuf v1.4.2 github.com/klauspost/compress v1.16.5 - github.com/nats-io/nats-server/v2 v2.9.16 + github.com/nats-io/nats-server/v2 v2.9.19 github.com/nats-io/nkeys v0.4.4 github.com/nats-io/nuid v1.0.1 go.uber.org/goleak v1.2.1 @@ -16,7 +16,7 @@ require ( require ( github.com/minio/highwayhash v1.0.2 // indirect github.com/nats-io/jwt/v2 v2.4.1 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/time v0.3.0 // indirect ) diff --git a/vendor/github.com/nats-io/nats.go/go_test.sum b/vendor/github.com/nats-io/nats.go/go_test.sum index e1cf0d614..b9c9fd476 100644 --- a/vendor/github.com/nats-io/nats.go/go_test.sum +++ b/vendor/github.com/nats-io/nats.go/go_test.sum @@ -16,8 +16,8 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4= github.com/nats-io/jwt/v2 v2.4.1/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI= -github.com/nats-io/nats-server/v2 v2.9.16 h1:SuNe6AyCcVy0g5326wtyU8TdqYmcPqzTjhkHojAjprc= -github.com/nats-io/nats-server/v2 v2.9.16/go.mod h1:z1cc5Q+kqJkz9mLUdlcSsdYnId4pyImHjNgoh6zxSC0= +github.com/nats-io/nats-server/v2 v2.9.19 h1:OF9jSKZGo425C/FcVVIvNgpd36CUe7aVTTXEZRJk6kA= +github.com/nats-io/nats-server/v2 v2.9.19/go.mod h1:aTb/xtLCGKhfTFLxP591CMWfkdgBmcUUSkiSOe5A3gw= github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= @@ -26,11 +26,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= diff --git a/vendor/github.com/nats-io/nats.go/js.go b/vendor/github.com/nats-io/nats.go/js.go index 76a55d468..79a92828a 100644 --- a/vendor/github.com/nats-io/nats.go/js.go +++ b/vendor/github.com/nats-io/nats.go/js.go @@ -227,13 +227,14 @@ type js struct { opts *jsOpts // For async publish context. - mu sync.RWMutex - rpre string - rsub *Subscription - pafs map[string]*pubAckFuture - stc chan struct{} - dch chan struct{} - rr *rand.Rand + mu sync.RWMutex + rpre string + rsub *Subscription + pafs map[string]*pubAckFuture + stc chan struct{} + dch chan struct{} + rr *rand.Rand + connStatusCh chan (Status) } type jsOpts struct { @@ -666,6 +667,10 @@ func (js *js) newAsyncReply() string { js.rsub = sub js.rr = rand.New(rand.NewSource(time.Now().UnixNano())) } + if js.connStatusCh == nil { + js.connStatusCh = js.nc.StatusChanged(RECONNECTING, CLOSED) + go js.resetPendingAcksOnReconnect() + } var sb strings.Builder sb.WriteString(js.rpre) rn := js.rr.Int63() @@ -679,12 +684,34 @@ func (js *js) newAsyncReply() string { return sb.String() } +func (js *js) resetPendingAcksOnReconnect() { + js.mu.Lock() + connStatusCh := js.connStatusCh + js.mu.Unlock() + for { + newStatus, ok := <-connStatusCh + if !ok || newStatus == CLOSED { + return + } + js.mu.Lock() + for _, paf := range js.pafs { + paf.err = ErrDisconnected + } + js.pafs = nil + js.mu.Unlock() + } +} + func (js *js) cleanupReplySub() { js.mu.Lock() if js.rsub != nil { js.rsub.Unsubscribe() js.rsub = nil } + if js.connStatusCh != nil { + close(js.connStatusCh) + js.connStatusCh = nil + } js.mu.Unlock() } @@ -1352,7 +1379,7 @@ func processConsInfo(info *ConsumerInfo, userCfg *ConsumerConfig, isPullMode boo } func checkConfig(s, u *ConsumerConfig) error { - makeErr := func(fieldName string, usrVal, srvVal interface{}) error { + makeErr := func(fieldName string, usrVal, srvVal any) error { return fmt.Errorf("configuration requests %s to be %v, but consumer's value is %v", fieldName, usrVal, srvVal) } @@ -1991,6 +2018,9 @@ func (sub *Subscription) resetOrderedConsumer(sseq uint64) { cfg.DeliverSubject = newDeliver cfg.DeliverPolicy = DeliverByStartSequencePolicy cfg.OptStartSeq = sseq + // In case the consumer was created with a start time, we need to clear it + // since we are now using a start sequence. + cfg.OptStartTime = nil js := jsi.js sub.mu.Unlock() diff --git a/vendor/github.com/nats-io/nats.go/jsm.go b/vendor/github.com/nats-io/nats.go/jsm.go index a2b68417b..c6684692b 100644 --- a/vendor/github.com/nats-io/nats.go/jsm.go +++ b/vendor/github.com/nats-io/nats.go/jsm.go @@ -361,20 +361,29 @@ func (js *js) upsertConsumer(stream, consumerName string, cfg *ConsumerConfig, o var ccSubj string if consumerName == _EMPTY_ { - // if consumer name is empty, use the legacy ephemeral endpoint + // if consumer name is empty (neither Durable nor Name is set), use the legacy ephemeral endpoint ccSubj = fmt.Sprintf(apiLegacyConsumerCreateT, stream) } else if err := checkConsumerName(consumerName); err != nil { return nil, err - } else if !js.nc.serverMinVersion(2, 9, 0) || (cfg.Durable != "" && js.opts.featureFlags.useDurableConsumerCreate) { - // if server version is lower than 2.9.0 or user set the useDurableConsumerCreate flag, use the legacy DURABLE.CREATE endpoint - ccSubj = fmt.Sprintf(apiDurableCreateT, stream, consumerName) - } else { - // if above server version 2.9.0, use the endpoints with consumer name - if cfg.FilterSubject == _EMPTY_ || cfg.FilterSubject == ">" { + } else if js.nc.serverMinVersion(2, 9, 0) { + if cfg.Durable != "" && js.opts.featureFlags.useDurableConsumerCreate { + // if user set the useDurableConsumerCreate flag, use the legacy DURABLE.CREATE endpoint + ccSubj = fmt.Sprintf(apiDurableCreateT, stream, consumerName) + } else if cfg.FilterSubject == _EMPTY_ || cfg.FilterSubject == ">" { + // if filter subject is empty or ">", use the endpoint without filter subject ccSubj = fmt.Sprintf(apiConsumerCreateT, stream, consumerName) } else { + // if filter subject is not empty, use the endpoint with filter subject ccSubj = fmt.Sprintf(apiConsumerCreateWithFilterSubjectT, stream, consumerName, cfg.FilterSubject) } + } else { + if cfg.Durable != "" { + // if Durable is set, use the DURABLE.CREATE endpoint + ccSubj = fmt.Sprintf(apiDurableCreateT, stream, consumerName) + } else { + // if Durable is not set, use the legacy ephemeral endpoint + ccSubj = fmt.Sprintf(apiLegacyConsumerCreateT, stream) + } } resp, err := js.apiRequestWithContext(o.ctx, js.apiSubj(ccSubj), req) diff --git a/vendor/github.com/nats-io/nats.go/legacy_jetstream.md b/vendor/github.com/nats-io/nats.go/legacy_jetstream.md new file mode 100644 index 000000000..43e1c73b3 --- /dev/null +++ b/vendor/github.com/nats-io/nats.go/legacy_jetstream.md @@ -0,0 +1,83 @@ +# Legacy JetStream API + +This is a documentation for the legacy JetStream API. A README for the current +API can be found [here](jetstream/README.md) + +## JetStream Basic Usage + +```go +import "github.com/nats-io/nats.go" + +// Connect to NATS +nc, _ := nats.Connect(nats.DefaultURL) + +// Create JetStream Context +js, _ := nc.JetStream(nats.PublishAsyncMaxPending(256)) + +// Simple Stream Publisher +js.Publish("ORDERS.scratch", []byte("hello")) + +// Simple Async Stream Publisher +for i := 0; i < 500; i++ { + js.PublishAsync("ORDERS.scratch", []byte("hello")) +} +select { +case <-js.PublishAsyncComplete(): +case <-time.After(5 * time.Second): + fmt.Println("Did not resolve in time") +} + +// Simple Async Ephemeral Consumer +js.Subscribe("ORDERS.*", func(m *nats.Msg) { + fmt.Printf("Received a JetStream message: %s\n", string(m.Data)) +}) + +// Simple Sync Durable Consumer (optional SubOpts at the end) +sub, err := js.SubscribeSync("ORDERS.*", nats.Durable("MONITOR"), nats.MaxDeliver(3)) +m, err := sub.NextMsg(timeout) + +// Simple Pull Consumer +sub, err := js.PullSubscribe("ORDERS.*", "MONITOR") +msgs, err := sub.Fetch(10) + +// Unsubscribe +sub.Unsubscribe() + +// Drain +sub.Drain() +``` + +## JetStream Basic Management + +```go +import "github.com/nats-io/nats.go" + +// Connect to NATS +nc, _ := nats.Connect(nats.DefaultURL) + +// Create JetStream Context +js, _ := nc.JetStream() + +// Create a Stream +js.AddStream(&nats.StreamConfig{ + Name: "ORDERS", + Subjects: []string{"ORDERS.*"}, +}) + +// Update a Stream +js.UpdateStream(&nats.StreamConfig{ + Name: "ORDERS", + MaxBytes: 8, +}) + +// Create a Consumer +js.AddConsumer("ORDERS", &nats.ConsumerConfig{ + Durable: "MONITOR", +}) + +// Delete Consumer +js.DeleteConsumer("ORDERS", "MONITOR") + +// Delete Stream +js.DeleteStream("ORDERS") +``` diff --git a/vendor/github.com/nats-io/nats.go/nats.go b/vendor/github.com/nats-io/nats.go/nats.go index 00ec369d0..350f2306a 100644 --- a/vendor/github.com/nats-io/nats.go/nats.go +++ b/vendor/github.com/nats-io/nats.go/nats.go @@ -47,7 +47,7 @@ import ( // Default Constants const ( - Version = "1.27.0" + Version = "1.28.0" DefaultURL = "nats://127.0.0.1:4222" DefaultPort = 4222 DefaultMaxReconnect = 60 @@ -5471,7 +5471,7 @@ func (nc *Conn) StatusChanged(statuses ...Status) chan Status { if len(statuses) == 0 { statuses = []Status{CONNECTED, RECONNECTING, DISCONNECTED, CLOSED} } - ch := make(chan Status) + ch := make(chan Status, 10) for _, s := range statuses { nc.registerStatusChangeListener(s, ch) } diff --git a/vendor/github.com/nats-io/nats.go/netchan.go b/vendor/github.com/nats-io/nats.go/netchan.go index a1af9e06e..060721eb4 100644 --- a/vendor/github.com/nats-io/nats.go/netchan.go +++ b/vendor/github.com/nats-io/nats.go/netchan.go @@ -23,7 +23,7 @@ import ( // Data will be encoded and decoded via the EncodedConn and its associated encoders. // BindSendChan binds a channel for send operations to NATS. -func (c *EncodedConn) BindSendChan(subject string, channel interface{}) error { +func (c *EncodedConn) BindSendChan(subject string, channel any) error { chVal := reflect.ValueOf(channel) if chVal.Kind() != reflect.Chan { return ErrChanArg @@ -61,17 +61,17 @@ func chPublish(c *EncodedConn, chVal reflect.Value, subject string) { } // BindRecvChan binds a channel for receive operations from NATS. -func (c *EncodedConn) BindRecvChan(subject string, channel interface{}) (*Subscription, error) { +func (c *EncodedConn) BindRecvChan(subject string, channel any) (*Subscription, error) { return c.bindRecvChan(subject, _EMPTY_, channel) } // BindRecvQueueChan binds a channel for queue-based receive operations from NATS. -func (c *EncodedConn) BindRecvQueueChan(subject, queue string, channel interface{}) (*Subscription, error) { +func (c *EncodedConn) BindRecvQueueChan(subject, queue string, channel any) (*Subscription, error) { return c.bindRecvChan(subject, queue, channel) } // Internal function to bind receive operations for a channel. -func (c *EncodedConn) bindRecvChan(subject, queue string, channel interface{}) (*Subscription, error) { +func (c *EncodedConn) bindRecvChan(subject, queue string, channel any) (*Subscription, error) { chVal := reflect.ValueOf(channel) if chVal.Kind() != reflect.Chan { return nil, ErrChanArg diff --git a/vendor/github.com/nats-io/nats.go/object.go b/vendor/github.com/nats-io/nats.go/object.go index 6bce60575..9d558cc6e 100644 --- a/vendor/github.com/nats-io/nats.go/object.go +++ b/vendor/github.com/nats-io/nats.go/object.go @@ -622,6 +622,7 @@ func (obs *obs) Get(name string, opts ...GetObjectOpt) (ObjectResult, error) { result.digest = sha256.New() processChunk := func(m *Msg) { + var err error if ctx != nil { select { case <-ctx.Done(): diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go new file mode 100644 index 000000000..6fc2838a3 --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -0,0 +1,824 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cryptobyte + +import ( + encoding_asn1 "encoding/asn1" + "fmt" + "math/big" + "reflect" + "time" + + "golang.org/x/crypto/cryptobyte/asn1" +) + +// This file contains ASN.1-related methods for String and Builder. + +// Builder + +// AddASN1Int64 appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1Int64(v int64) { + b.addASN1Signed(asn1.INTEGER, v) +} + +// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the +// given tag. +func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) { + b.addASN1Signed(tag, v) +} + +// AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION. +func (b *Builder) AddASN1Enum(v int64) { + b.addASN1Signed(asn1.ENUM, v) +} + +func (b *Builder) addASN1Signed(tag asn1.Tag, v int64) { + b.AddASN1(tag, func(c *Builder) { + length := 1 + for i := v; i >= 0x80 || i < -0x80; i >>= 8 { + length++ + } + + for ; length > 0; length-- { + i := v >> uint((length-1)*8) & 0xff + c.AddUint8(uint8(i)) + } + }) +} + +// AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1Uint64(v uint64) { + b.AddASN1(asn1.INTEGER, func(c *Builder) { + length := 1 + for i := v; i >= 0x80; i >>= 8 { + length++ + } + + for ; length > 0; length-- { + i := v >> uint((length-1)*8) & 0xff + c.AddUint8(uint8(i)) + } + }) +} + +// AddASN1BigInt appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1BigInt(n *big.Int) { + if b.err != nil { + return + } + + b.AddASN1(asn1.INTEGER, func(c *Builder) { + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement form. So we + // invert and subtract 1. If the most-significant-bit isn't set then + // we'll need to pad the beginning with 0xff in order to keep the number + // negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + c.add(0xff) + } + c.add(bytes...) + } else if n.Sign() == 0 { + c.add(0) + } else { + bytes := n.Bytes() + if bytes[0]&0x80 != 0 { + c.add(0) + } + c.add(bytes...) + } + }) +} + +// AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING. +func (b *Builder) AddASN1OctetString(bytes []byte) { + b.AddASN1(asn1.OCTET_STRING, func(c *Builder) { + c.AddBytes(bytes) + }) +} + +const generalizedTimeFormatStr = "20060102150405Z0700" + +// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME. +func (b *Builder) AddASN1GeneralizedTime(t time.Time) { + if t.Year() < 0 || t.Year() > 9999 { + b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t) + return + } + b.AddASN1(asn1.GeneralizedTime, func(c *Builder) { + c.AddBytes([]byte(t.Format(generalizedTimeFormatStr))) + }) +} + +// AddASN1UTCTime appends a DER-encoded ASN.1 UTCTime. +func (b *Builder) AddASN1UTCTime(t time.Time) { + b.AddASN1(asn1.UTCTime, func(c *Builder) { + // As utilized by the X.509 profile, UTCTime can only + // represent the years 1950 through 2049. + if t.Year() < 1950 || t.Year() >= 2050 { + b.err = fmt.Errorf("cryptobyte: cannot represent %v as a UTCTime", t) + return + } + c.AddBytes([]byte(t.Format(defaultUTCTimeFormatStr))) + }) +} + +// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING. This does not +// support BIT STRINGs that are not a whole number of bytes. +func (b *Builder) AddASN1BitString(data []byte) { + b.AddASN1(asn1.BIT_STRING, func(b *Builder) { + b.AddUint8(0) + b.AddBytes(data) + }) +} + +func (b *Builder) addBase128Int(n int64) { + var length int + if n == 0 { + length = 1 + } else { + for i := n; i > 0; i >>= 7 { + length++ + } + } + + for i := length - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + + b.add(o) + } +} + +func isValidOID(oid encoding_asn1.ObjectIdentifier) bool { + if len(oid) < 2 { + return false + } + + if oid[0] > 2 || (oid[0] <= 1 && oid[1] >= 40) { + return false + } + + for _, v := range oid { + if v < 0 { + return false + } + } + + return true +} + +func (b *Builder) AddASN1ObjectIdentifier(oid encoding_asn1.ObjectIdentifier) { + b.AddASN1(asn1.OBJECT_IDENTIFIER, func(b *Builder) { + if !isValidOID(oid) { + b.err = fmt.Errorf("cryptobyte: invalid OID: %v", oid) + return + } + + b.addBase128Int(int64(oid[0])*40 + int64(oid[1])) + for _, v := range oid[2:] { + b.addBase128Int(int64(v)) + } + }) +} + +func (b *Builder) AddASN1Boolean(v bool) { + b.AddASN1(asn1.BOOLEAN, func(b *Builder) { + if v { + b.AddUint8(0xff) + } else { + b.AddUint8(0) + } + }) +} + +func (b *Builder) AddASN1NULL() { + b.add(uint8(asn1.NULL), 0) +} + +// MarshalASN1 calls encoding_asn1.Marshal on its input and appends the result if +// successful or records an error if one occurred. +func (b *Builder) MarshalASN1(v interface{}) { + // NOTE(martinkr): This is somewhat of a hack to allow propagation of + // encoding_asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a + // value embedded into a struct, its tag information is lost. + if b.err != nil { + return + } + bytes, err := encoding_asn1.Marshal(v) + if err != nil { + b.err = err + return + } + b.AddBytes(bytes) +} + +// AddASN1 appends an ASN.1 object. The object is prefixed with the given tag. +// Tags greater than 30 are not supported and result in an error (i.e. +// low-tag-number form only). The child builder passed to the +// BuilderContinuation can be used to build the content of the ASN.1 object. +func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) { + if b.err != nil { + return + } + // Identifiers with the low five bits set indicate high-tag-number format + // (two or more octets), which we don't support. + if tag&0x1f == 0x1f { + b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag) + return + } + b.AddUint8(uint8(tag)) + b.addLengthPrefixed(1, true, f) +} + +// String + +// ReadASN1Boolean decodes an ASN.1 BOOLEAN and converts it to a boolean +// representation into out and advances. It reports whether the read +// was successful. +func (s *String) ReadASN1Boolean(out *bool) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.BOOLEAN) || len(bytes) != 1 { + return false + } + + switch bytes[0] { + case 0: + *out = false + case 0xff: + *out = true + default: + return false + } + + return true +} + +// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does +// not point to an integer, to a big.Int, or to a []byte it panics. Only +// positive and zero values can be decoded into []byte, and they are returned as +// big-endian binary values that share memory with s. Positive values will have +// no leading zeroes, and zero will be returned as a single zero byte. +// ReadASN1Integer reports whether the read was successful. +func (s *String) ReadASN1Integer(out interface{}) bool { + switch out := out.(type) { + case *int, *int8, *int16, *int32, *int64: + var i int64 + if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) { + return false + } + reflect.ValueOf(out).Elem().SetInt(i) + return true + case *uint, *uint8, *uint16, *uint32, *uint64: + var u uint64 + if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) { + return false + } + reflect.ValueOf(out).Elem().SetUint(u) + return true + case *big.Int: + return s.readASN1BigInt(out) + case *[]byte: + return s.readASN1Bytes(out) + default: + panic("out does not point to an integer type") + } +} + +func checkASN1Integer(bytes []byte) bool { + if len(bytes) == 0 { + // An INTEGER is encoded with at least one octet. + return false + } + if len(bytes) == 1 { + return true + } + if bytes[0] == 0 && bytes[1]&0x80 == 0 || bytes[0] == 0xff && bytes[1]&0x80 == 0x80 { + // Value is not minimally encoded. + return false + } + return true +} + +var bigOne = big.NewInt(1) + +func (s *String) readASN1BigInt(out *big.Int) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) { + return false + } + if bytes[0]&0x80 == 0x80 { + // Negative number. + neg := make([]byte, len(bytes)) + for i, b := range bytes { + neg[i] = ^b + } + out.SetBytes(neg) + out.Add(out, bigOne) + out.Neg(out) + } else { + out.SetBytes(bytes) + } + return true +} + +func (s *String) readASN1Bytes(out *[]byte) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) { + return false + } + if bytes[0]&0x80 == 0x80 { + return false + } + for len(bytes) > 1 && bytes[0] == 0 { + bytes = bytes[1:] + } + *out = bytes + return true +} + +func (s *String) readASN1Int64(out *int64) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) { + return false + } + return true +} + +func asn1Signed(out *int64, n []byte) bool { + length := len(n) + if length > 8 { + return false + } + for i := 0; i < length; i++ { + *out <<= 8 + *out |= int64(n[i]) + } + // Shift up and down in order to sign extend the result. + *out <<= 64 - uint8(length)*8 + *out >>= 64 - uint8(length)*8 + return true +} + +func (s *String) readASN1Uint64(out *uint64) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) { + return false + } + return true +} + +func asn1Unsigned(out *uint64, n []byte) bool { + length := len(n) + if length > 9 || length == 9 && n[0] != 0 { + // Too large for uint64. + return false + } + if n[0]&0x80 != 0 { + // Negative number. + return false + } + for i := 0; i < length; i++ { + *out <<= 8 + *out |= uint64(n[i]) + } + return true +} + +// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out +// and advances. It reports whether the read was successful and resulted in a +// value that can be represented in an int64. +func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool { + var bytes String + return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes) +} + +// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports +// whether the read was successful. +func (s *String) ReadASN1Enum(out *int) bool { + var bytes String + var i int64 + if !s.ReadASN1(&bytes, asn1.ENUM) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) { + return false + } + if int64(int(i)) != i { + return false + } + *out = int(i) + return true +} + +func (s *String) readBase128Int(out *int) bool { + ret := 0 + for i := 0; len(*s) > 0; i++ { + if i == 5 { + return false + } + // Avoid overflowing int on a 32-bit platform. + // We don't want different behavior based on the architecture. + if ret >= 1<<(31-7) { + return false + } + ret <<= 7 + b := s.read(1)[0] + + // ITU-T X.690, section 8.19.2: + // The subidentifier shall be encoded in the fewest possible octets, + // that is, the leading octet of the subidentifier shall not have the value 0x80. + if i == 0 && b == 0x80 { + return false + } + + ret |= int(b & 0x7f) + if b&0x80 == 0 { + *out = ret + return true + } + } + return false // truncated +} + +// ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and +// advances. It reports whether the read was successful. +func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 { + return false + } + + // In the worst case, we get two elements from the first byte (which is + // encoded differently) and then every varint is a single byte long. + components := make([]int, len(bytes)+1) + + // The first varint is 40*value1 + value2: + // According to this packing, value1 can take the values 0, 1 and 2 only. + // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, + // then there are no restrictions on value2. + var v int + if !bytes.readBase128Int(&v) { + return false + } + if v < 80 { + components[0] = v / 40 + components[1] = v % 40 + } else { + components[0] = 2 + components[1] = v - 80 + } + + i := 2 + for ; len(bytes) > 0; i++ { + if !bytes.readBase128Int(&v) { + return false + } + components[i] = v + } + *out = components[:i] + return true +} + +// ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and +// advances. It reports whether the read was successful. +func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.GeneralizedTime) { + return false + } + t := string(bytes) + res, err := time.Parse(generalizedTimeFormatStr, t) + if err != nil { + return false + } + if serialized := res.Format(generalizedTimeFormatStr); serialized != t { + return false + } + *out = res + return true +} + +const defaultUTCTimeFormatStr = "060102150405Z0700" + +// ReadASN1UTCTime decodes an ASN.1 UTCTime into out and advances. +// It reports whether the read was successful. +func (s *String) ReadASN1UTCTime(out *time.Time) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.UTCTime) { + return false + } + t := string(bytes) + + formatStr := defaultUTCTimeFormatStr + var err error + res, err := time.Parse(formatStr, t) + if err != nil { + // Fallback to minute precision if we can't parse second + // precision. If we are following X.509 or X.690 we shouldn't + // support this, but we do. + formatStr = "0601021504Z0700" + res, err = time.Parse(formatStr, t) + } + if err != nil { + return false + } + + if serialized := res.Format(formatStr); serialized != t { + return false + } + + if res.Year() >= 2050 { + // UTCTime interprets the low order digits 50-99 as 1950-99. + // This only applies to its use in the X.509 profile. + // See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + res = res.AddDate(-100, 0, 0) + } + *out = res + return true +} + +// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. +// It reports whether the read was successful. +func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 || + len(bytes)*8/8 != len(bytes) { + return false + } + + paddingBits := bytes[0] + bytes = bytes[1:] + if paddingBits > 7 || + len(bytes) == 0 && paddingBits != 0 || + len(bytes) > 0 && bytes[len(bytes)-1]&(1< 4 || len(*s) < int(2+lenLen) { + return false + } + + lenBytes := String((*s)[2 : 2+lenLen]) + if !lenBytes.readUnsigned(&len32, int(lenLen)) { + return false + } + + // ITU-T X.690 section 10.1 (DER length forms) requires encoding the length + // with the minimum number of octets. + if len32 < 128 { + // Length should have used short-form encoding. + return false + } + if len32>>((lenLen-1)*8) == 0 { + // Leading octet is 0. Length should have been at least one byte shorter. + return false + } + + headerLen = 2 + uint32(lenLen) + if headerLen+len32 < len32 { + // Overflow. + return false + } + length = headerLen + len32 + } + + if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) { + return false + } + if skipHeader && !out.Skip(int(headerLen)) { + panic("cryptobyte: internal error") + } + + return true +} diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go new file mode 100644 index 000000000..cda8e3edf --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go @@ -0,0 +1,46 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package asn1 contains supporting types for parsing and building ASN.1 +// messages with the cryptobyte package. +package asn1 // import "golang.org/x/crypto/cryptobyte/asn1" + +// Tag represents an ASN.1 identifier octet, consisting of a tag number +// (indicating a type) and class (such as context-specific or constructed). +// +// Methods in the cryptobyte package only support the low-tag-number form, i.e. +// a single identifier octet with bits 7-8 encoding the class and bits 1-6 +// encoding the tag number. +type Tag uint8 + +const ( + classConstructed = 0x20 + classContextSpecific = 0x80 +) + +// Constructed returns t with the constructed class bit set. +func (t Tag) Constructed() Tag { return t | classConstructed } + +// ContextSpecific returns t with the context-specific class bit set. +func (t Tag) ContextSpecific() Tag { return t | classContextSpecific } + +// The following is a list of standard tag and class combinations. +const ( + BOOLEAN = Tag(1) + INTEGER = Tag(2) + BIT_STRING = Tag(3) + OCTET_STRING = Tag(4) + NULL = Tag(5) + OBJECT_IDENTIFIER = Tag(6) + ENUM = Tag(10) + UTF8String = Tag(12) + SEQUENCE = Tag(16 | classConstructed) + SET = Tag(17 | classConstructed) + PrintableString = Tag(19) + T61String = Tag(20) + IA5String = Tag(22) + UTCTime = Tag(23) + GeneralizedTime = Tag(24) + GeneralString = Tag(27) +) diff --git a/vendor/golang.org/x/crypto/cryptobyte/builder.go b/vendor/golang.org/x/crypto/cryptobyte/builder.go new file mode 100644 index 000000000..c05ac7d16 --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/builder.go @@ -0,0 +1,345 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cryptobyte + +import ( + "errors" + "fmt" +) + +// A Builder builds byte strings from fixed-length and length-prefixed values. +// Builders either allocate space as needed, or are ‘fixed’, which means that +// they write into a given buffer and produce an error if it's exhausted. +// +// The zero value is a usable Builder that allocates space as needed. +// +// Simple values are marshaled and appended to a Builder using methods on the +// Builder. Length-prefixed values are marshaled by providing a +// BuilderContinuation, which is a function that writes the inner contents of +// the value to a given Builder. See the documentation for BuilderContinuation +// for details. +type Builder struct { + err error + result []byte + fixedSize bool + child *Builder + offset int + pendingLenLen int + pendingIsASN1 bool + inContinuation *bool +} + +// NewBuilder creates a Builder that appends its output to the given buffer. +// Like append(), the slice will be reallocated if its capacity is exceeded. +// Use Bytes to get the final buffer. +func NewBuilder(buffer []byte) *Builder { + return &Builder{ + result: buffer, + } +} + +// NewFixedBuilder creates a Builder that appends its output into the given +// buffer. This builder does not reallocate the output buffer. Writes that +// would exceed the buffer's capacity are treated as an error. +func NewFixedBuilder(buffer []byte) *Builder { + return &Builder{ + result: buffer, + fixedSize: true, + } +} + +// SetError sets the value to be returned as the error from Bytes. Writes +// performed after calling SetError are ignored. +func (b *Builder) SetError(err error) { + b.err = err +} + +// Bytes returns the bytes written by the builder or an error if one has +// occurred during building. +func (b *Builder) Bytes() ([]byte, error) { + if b.err != nil { + return nil, b.err + } + return b.result[b.offset:], nil +} + +// BytesOrPanic returns the bytes written by the builder or panics if an error +// has occurred during building. +func (b *Builder) BytesOrPanic() []byte { + if b.err != nil { + panic(b.err) + } + return b.result[b.offset:] +} + +// AddUint8 appends an 8-bit value to the byte string. +func (b *Builder) AddUint8(v uint8) { + b.add(byte(v)) +} + +// AddUint16 appends a big-endian, 16-bit value to the byte string. +func (b *Builder) AddUint16(v uint16) { + b.add(byte(v>>8), byte(v)) +} + +// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest +// byte of the 32-bit input value is silently truncated. +func (b *Builder) AddUint24(v uint32) { + b.add(byte(v>>16), byte(v>>8), byte(v)) +} + +// AddUint32 appends a big-endian, 32-bit value to the byte string. +func (b *Builder) AddUint32(v uint32) { + b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + +// AddUint64 appends a big-endian, 64-bit value to the byte string. +func (b *Builder) AddUint64(v uint64) { + b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + +// AddBytes appends a sequence of bytes to the byte string. +func (b *Builder) AddBytes(v []byte) { + b.add(v...) +} + +// BuilderContinuation is a continuation-passing interface for building +// length-prefixed byte sequences. Builder methods for length-prefixed +// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation +// supplied to them. The child builder passed to the continuation can be used +// to build the content of the length-prefixed sequence. For example: +// +// parent := cryptobyte.NewBuilder() +// parent.AddUint8LengthPrefixed(func (child *Builder) { +// child.AddUint8(42) +// child.AddUint8LengthPrefixed(func (grandchild *Builder) { +// grandchild.AddUint8(5) +// }) +// }) +// +// It is an error to write more bytes to the child than allowed by the reserved +// length prefix. After the continuation returns, the child must be considered +// invalid, i.e. users must not store any copies or references of the child +// that outlive the continuation. +// +// If the continuation panics with a value of type BuildError then the inner +// error will be returned as the error from Bytes. If the child panics +// otherwise then Bytes will repanic with the same value. +type BuilderContinuation func(child *Builder) + +// BuildError wraps an error. If a BuilderContinuation panics with this value, +// the panic will be recovered and the inner error will be returned from +// Builder.Bytes. +type BuildError struct { + Err error +} + +// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence. +func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(1, false, f) +} + +// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence. +func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(2, false, f) +} + +// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence. +func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(3, false, f) +} + +// AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence. +func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(4, false, f) +} + +func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) { + if !*b.inContinuation { + *b.inContinuation = true + + defer func() { + *b.inContinuation = false + + r := recover() + if r == nil { + return + } + + if buildError, ok := r.(BuildError); ok { + b.err = buildError.Err + } else { + panic(r) + } + }() + } + + f(arg) +} + +func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) { + // Subsequent writes can be ignored if the builder has encountered an error. + if b.err != nil { + return + } + + offset := len(b.result) + b.add(make([]byte, lenLen)...) + + if b.inContinuation == nil { + b.inContinuation = new(bool) + } + + b.child = &Builder{ + result: b.result, + fixedSize: b.fixedSize, + offset: offset, + pendingLenLen: lenLen, + pendingIsASN1: isASN1, + inContinuation: b.inContinuation, + } + + b.callContinuation(f, b.child) + b.flushChild() + if b.child != nil { + panic("cryptobyte: internal error") + } +} + +func (b *Builder) flushChild() { + if b.child == nil { + return + } + b.child.flushChild() + child := b.child + b.child = nil + + if child.err != nil { + b.err = child.err + return + } + + length := len(child.result) - child.pendingLenLen - child.offset + + if length < 0 { + panic("cryptobyte: internal error") // result unexpectedly shrunk + } + + if child.pendingIsASN1 { + // For ASN.1, we reserved a single byte for the length. If that turned out + // to be incorrect, we have to move the contents along in order to make + // space. + if child.pendingLenLen != 1 { + panic("cryptobyte: internal error") + } + var lenLen, lenByte uint8 + if int64(length) > 0xfffffffe { + b.err = errors.New("pending ASN.1 child too long") + return + } else if length > 0xffffff { + lenLen = 5 + lenByte = 0x80 | 4 + } else if length > 0xffff { + lenLen = 4 + lenByte = 0x80 | 3 + } else if length > 0xff { + lenLen = 3 + lenByte = 0x80 | 2 + } else if length > 0x7f { + lenLen = 2 + lenByte = 0x80 | 1 + } else { + lenLen = 1 + lenByte = uint8(length) + length = 0 + } + + // Insert the initial length byte, make space for successive length bytes, + // and adjust the offset. + child.result[child.offset] = lenByte + extraBytes := int(lenLen - 1) + if extraBytes != 0 { + child.add(make([]byte, extraBytes)...) + childStart := child.offset + child.pendingLenLen + copy(child.result[childStart+extraBytes:], child.result[childStart:]) + } + child.offset++ + child.pendingLenLen = extraBytes + } + + l := length + for i := child.pendingLenLen - 1; i >= 0; i-- { + child.result[child.offset+i] = uint8(l) + l >>= 8 + } + if l != 0 { + b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen) + return + } + + if b.fixedSize && &b.result[0] != &child.result[0] { + panic("cryptobyte: BuilderContinuation reallocated a fixed-size buffer") + } + + b.result = child.result +} + +func (b *Builder) add(bytes ...byte) { + if b.err != nil { + return + } + if b.child != nil { + panic("cryptobyte: attempted write while child is pending") + } + if len(b.result)+len(bytes) < len(bytes) { + b.err = errors.New("cryptobyte: length overflow") + } + if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) { + b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer") + return + } + b.result = append(b.result, bytes...) +} + +// Unwrite rolls back non-negative n bytes written directly to the Builder. +// An attempt by a child builder passed to a continuation to unwrite bytes +// from its parent will panic. +func (b *Builder) Unwrite(n int) { + if b.err != nil { + return + } + if b.child != nil { + panic("cryptobyte: attempted unwrite while child is pending") + } + length := len(b.result) - b.pendingLenLen - b.offset + if length < 0 { + panic("cryptobyte: internal error") + } + if n < 0 { + panic("cryptobyte: attempted to unwrite negative number of bytes") + } + if n > length { + panic("cryptobyte: attempted to unwrite more than was written") + } + b.result = b.result[:len(b.result)-n] +} + +// A MarshalingValue marshals itself into a Builder. +type MarshalingValue interface { + // Marshal is called by Builder.AddValue. It receives a pointer to a builder + // to marshal itself into. It may return an error that occurred during + // marshaling, such as unset or invalid values. + Marshal(b *Builder) error +} + +// AddValue calls Marshal on v, passing a pointer to the builder to append to. +// If Marshal returns an error, it is set on the Builder so that subsequent +// appends don't have an effect. +func (b *Builder) AddValue(v MarshalingValue) { + err := v.Marshal(b) + if err != nil { + b.err = err + } +} diff --git a/vendor/golang.org/x/crypto/cryptobyte/string.go b/vendor/golang.org/x/crypto/cryptobyte/string.go new file mode 100644 index 000000000..0531a3d6f --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/string.go @@ -0,0 +1,172 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cryptobyte contains types that help with parsing and constructing +// length-prefixed, binary messages, including ASN.1 DER. (The asn1 subpackage +// contains useful ASN.1 constants.) +// +// The String type is for parsing. It wraps a []byte slice and provides helper +// functions for consuming structures, value by value. +// +// The Builder type is for constructing messages. It providers helper functions +// for appending values and also for appending length-prefixed submessages – +// without having to worry about calculating the length prefix ahead of time. +// +// See the documentation and examples for the Builder and String types to get +// started. +package cryptobyte // import "golang.org/x/crypto/cryptobyte" + +// String represents a string of bytes. It provides methods for parsing +// fixed-length and length-prefixed values from it. +type String []byte + +// read advances a String by n bytes and returns them. If less than n bytes +// remain, it returns nil. +func (s *String) read(n int) []byte { + if len(*s) < n || n < 0 { + return nil + } + v := (*s)[:n] + *s = (*s)[n:] + return v +} + +// Skip advances the String by n byte and reports whether it was successful. +func (s *String) Skip(n int) bool { + return s.read(n) != nil +} + +// ReadUint8 decodes an 8-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint8(out *uint8) bool { + v := s.read(1) + if v == nil { + return false + } + *out = uint8(v[0]) + return true +} + +// ReadUint16 decodes a big-endian, 16-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint16(out *uint16) bool { + v := s.read(2) + if v == nil { + return false + } + *out = uint16(v[0])<<8 | uint16(v[1]) + return true +} + +// ReadUint24 decodes a big-endian, 24-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint24(out *uint32) bool { + v := s.read(3) + if v == nil { + return false + } + *out = uint32(v[0])<<16 | uint32(v[1])<<8 | uint32(v[2]) + return true +} + +// ReadUint32 decodes a big-endian, 32-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint32(out *uint32) bool { + v := s.read(4) + if v == nil { + return false + } + *out = uint32(v[0])<<24 | uint32(v[1])<<16 | uint32(v[2])<<8 | uint32(v[3]) + return true +} + +// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint64(out *uint64) bool { + v := s.read(8) + if v == nil { + return false + } + *out = uint64(v[0])<<56 | uint64(v[1])<<48 | uint64(v[2])<<40 | uint64(v[3])<<32 | uint64(v[4])<<24 | uint64(v[5])<<16 | uint64(v[6])<<8 | uint64(v[7]) + return true +} + +func (s *String) readUnsigned(out *uint32, length int) bool { + v := s.read(length) + if v == nil { + return false + } + var result uint32 + for i := 0; i < length; i++ { + result <<= 8 + result |= uint32(v[i]) + } + *out = result + return true +} + +func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool { + lenBytes := s.read(lenLen) + if lenBytes == nil { + return false + } + var length uint32 + for _, b := range lenBytes { + length = length << 8 + length = length | uint32(b) + } + v := s.read(int(length)) + if v == nil { + return false + } + *outChild = v + return true +} + +// ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value +// into out and advances over it. It reports whether the read was successful. +func (s *String) ReadUint8LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(1, out) +} + +// ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit +// length-prefixed value into out and advances over it. It reports whether the +// read was successful. +func (s *String) ReadUint16LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(2, out) +} + +// ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit +// length-prefixed value into out and advances over it. It reports whether +// the read was successful. +func (s *String) ReadUint24LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(3, out) +} + +// ReadBytes reads n bytes into out and advances over them. It reports +// whether the read was successful. +func (s *String) ReadBytes(out *[]byte, n int) bool { + v := s.read(n) + if v == nil { + return false + } + *out = v + return true +} + +// CopyBytes copies len(out) bytes into out and advances over them. It reports +// whether the copy operation was successful +func (s *String) CopyBytes(out []byte) bool { + n := len(out) + v := s.read(n) + if v == nil { + return false + } + return copy(out, v) == n +} + +// Empty reports whether the string does not contain any bytes. +func (s String) Empty() bool { + return len(s) == 0 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4f78dba49..7e50c7048 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1183,7 +1183,7 @@ github.com/justinas/alice # github.com/kevinburke/ssh_config v1.2.0 ## explicit github.com/kevinburke/ssh_config -# github.com/klauspost/compress v1.16.5 +# github.com/klauspost/compress v1.16.7 ## explicit; go 1.18 github.com/klauspost/compress/flate github.com/klauspost/compress/s2 @@ -1332,15 +1332,17 @@ github.com/mschoch/smat # github.com/nats-io/jwt/v2 v2.4.1 ## explicit; go 1.18 github.com/nats-io/jwt/v2 -# github.com/nats-io/nats-server/v2 v2.9.19 +# github.com/nats-io/nats-server/v2 v2.9.21 ## explicit; go 1.19 github.com/nats-io/nats-server/v2/conf github.com/nats-io/nats-server/v2/internal/ldap github.com/nats-io/nats-server/v2/logger github.com/nats-io/nats-server/v2/server +github.com/nats-io/nats-server/v2/server/certidp +github.com/nats-io/nats-server/v2/server/certstore github.com/nats-io/nats-server/v2/server/pse github.com/nats-io/nats-server/v2/server/sysmem -# github.com/nats-io/nats.go v1.27.0 +# github.com/nats-io/nats.go v1.28.0 ## explicit; go 1.19 github.com/nats-io/nats.go github.com/nats-io/nats.go/encoders/builtin @@ -1916,6 +1918,8 @@ golang.org/x/crypto/blowfish golang.org/x/crypto/cast5 golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20poly1305 +golang.org/x/crypto/cryptobyte +golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/curve25519 golang.org/x/crypto/curve25519/internal/field golang.org/x/crypto/ed25519