1.修改代码适配阿里云的服务器
This commit is contained in:
12
server/vendor/github.com/xdg-go/pbkdf2/.gitignore
generated
vendored
Normal file
12
server/vendor/github.com/xdg-go/pbkdf2/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
175
server/vendor/github.com/xdg-go/pbkdf2/LICENSE
generated
vendored
Normal file
175
server/vendor/github.com/xdg-go/pbkdf2/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
17
server/vendor/github.com/xdg-go/pbkdf2/README.md
generated
vendored
Normal file
17
server/vendor/github.com/xdg-go/pbkdf2/README.md
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
[](https://pkg.go.dev/github.com/xdg-go/pbkdf2)
|
||||
[](https://goreportcard.com/report/github.com/xdg-go/pbkdf2)
|
||||
[](https://github.com/xdg-go/pbkdf2/actions/workflows/test.yml)
|
||||
|
||||
# pbkdf2 – Go implementation of PBKDF2
|
||||
|
||||
## Description
|
||||
|
||||
Package pbkdf2 provides password-based key derivation based on
|
||||
[RFC 8018](https://tools.ietf.org/html/rfc8018).
|
||||
|
||||
## Copyright and License
|
||||
|
||||
Copyright 2021 by David A. Golden. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"). You may
|
||||
obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
76
server/vendor/github.com/xdg-go/pbkdf2/pbkdf2.go
generated
vendored
Normal file
76
server/vendor/github.com/xdg-go/pbkdf2/pbkdf2.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2021 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
// Package pbkdf2 implements password-based key derivation using the PBKDF2
|
||||
// algorithm described in RFC 2898 and RFC 8018.
|
||||
//
|
||||
// It provides a drop-in replacement for `golang.org/x/crypto/pbkdf2`, with
|
||||
// the following benefits:
|
||||
//
|
||||
// - Released as a module with semantic versioning
|
||||
//
|
||||
// - Does not pull in dependencies for unrelated `x/crypto/*` packages
|
||||
//
|
||||
// - Supports Go 1.9+
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc8018#section-4 for security considerations
|
||||
// in the selection of a salt and iteration count.
|
||||
package pbkdf2
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Key generates a derived key from a password using the PBKDF2 algorithm. The
|
||||
// inputs include salt bytes, the iteration count, desired key length, and a
|
||||
// constructor for a hashing function. For example, for a 32-byte key using
|
||||
// SHA-256:
|
||||
//
|
||||
// key := Key([]byte("trustNo1"), salt, 10000, 32, sha256.New)
|
||||
func Key(password, salt []byte, iterCount, keyLen int, h func() hash.Hash) []byte {
|
||||
prf := hmac.New(h, password)
|
||||
hLen := prf.Size()
|
||||
numBlocks := keyLen / hLen
|
||||
// Get an extra block if keyLen is not an even number of hLen blocks.
|
||||
if keyLen%hLen > 0 {
|
||||
numBlocks++
|
||||
}
|
||||
|
||||
Ti := make([]byte, hLen)
|
||||
Uj := make([]byte, hLen)
|
||||
dk := make([]byte, 0, hLen*numBlocks)
|
||||
buf := make([]byte, 4)
|
||||
|
||||
for i := uint32(1); i <= uint32(numBlocks); i++ {
|
||||
// Initialize Uj for j == 1 from salt and block index.
|
||||
// Initialize Ti = U1.
|
||||
binary.BigEndian.PutUint32(buf, i)
|
||||
prf.Reset()
|
||||
prf.Write(salt)
|
||||
prf.Write(buf)
|
||||
Uj = Uj[:0]
|
||||
Uj = prf.Sum(Uj)
|
||||
|
||||
// Ti = U1 ^ U2 ^ ... ^ Ux
|
||||
copy(Ti, Uj)
|
||||
for j := 2; j <= iterCount; j++ {
|
||||
prf.Reset()
|
||||
prf.Write(Uj)
|
||||
Uj = Uj[:0]
|
||||
Uj = prf.Sum(Uj)
|
||||
for k := range Uj {
|
||||
Ti[k] ^= Uj[k]
|
||||
}
|
||||
}
|
||||
|
||||
// DK = concat(T1, T2, ... Tn)
|
||||
dk = append(dk, Ti...)
|
||||
}
|
||||
|
||||
return dk[0:keyLen]
|
||||
}
|
||||
34
server/vendor/github.com/xdg-go/scram/.gitignore
generated
vendored
Normal file
34
server/vendor/github.com/xdg-go/scram/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Local Claude code settings
|
||||
.claude/settings.local.json
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool
|
||||
*.out
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# Dependency directories
|
||||
vendor/
|
||||
|
||||
# Build output
|
||||
bin/
|
||||
dist/
|
||||
|
||||
# IDE and editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
57
server/vendor/github.com/xdg-go/scram/CHANGELOG.md
generated
vendored
Normal file
57
server/vendor/github.com/xdg-go/scram/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v1.2.0 - 2025-11-24
|
||||
|
||||
### Added
|
||||
|
||||
- **Channel binding support for SCRAM-PLUS variants** (RFC 5929, RFC 9266)
|
||||
- `GetStoredCredentialsWithError()` method that returns errors from PBKDF2
|
||||
key derivation instead of panicking.
|
||||
- Support for Go 1.24+ stdlib `crypto/pbkdf2` package, which provides
|
||||
FIPS 140-3 compliance when using SHA-256 or SHA-512 hash functions.
|
||||
|
||||
### Changed
|
||||
|
||||
- Minimum Go version bumped from 1.11 to 1.18.
|
||||
- Migrated from `github.com/xdg-go/pbkdf2` to stdlib `crypto/pbkdf2` on
|
||||
Go 1.24+. Legacy Go versions (<1.24) continue using the external
|
||||
library via build tags for backward compatibility.
|
||||
- Internal error handling improved for PBKDF2 key derivation failures.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `GetStoredCredentials()` is deprecated in favor of
|
||||
`GetStoredCredentialsWithError()`. The old method panics on PBKDF2
|
||||
errors to maintain backward compatibility but will be removed in a
|
||||
future major version.
|
||||
|
||||
### Notes
|
||||
|
||||
- FIPS 140-3 compliance is available on Go 1.24+ when using SCRAM-SHA-256
|
||||
or SCRAM-SHA-512 with appropriate salt lengths (≥16 bytes). SCRAM-SHA-1
|
||||
is not FIPS-approved.
|
||||
|
||||
## v1.1.2 - 2022-12-07
|
||||
|
||||
- Bump stringprep dependency to v1.0.4 for upstream CVE fix.
|
||||
|
||||
## v1.1.1 - 2022-03-03
|
||||
|
||||
- Bump stringprep dependency to v1.0.3 for upstream CVE fix.
|
||||
|
||||
## v1.1.0 - 2022-01-16
|
||||
|
||||
- Add SHA-512 hash generator function for convenience.
|
||||
|
||||
## v1.0.2 - 2021-03-28
|
||||
|
||||
- Switch PBKDF2 dependency to github.com/xdg-go/pbkdf2 to
|
||||
minimize transitive dependencies and support Go 1.9+.
|
||||
|
||||
## v1.0.1 - 2021-03-27
|
||||
|
||||
- Bump stringprep dependency to v1.0.2 for Go 1.11 support.
|
||||
|
||||
## v1.0.0 - 2021-03-27
|
||||
|
||||
- First release as a Go module
|
||||
75
server/vendor/github.com/xdg-go/scram/CLAUDE.md
generated
vendored
Normal file
75
server/vendor/github.com/xdg-go/scram/CLAUDE.md
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a Go library implementing SCRAM (Salted Challenge Response Authentication Mechanism) per RFC-5802 and RFC-7677. It provides both client and server implementations supporting SHA-1, SHA-256, and SHA-512 hash functions.
|
||||
|
||||
## Development Commands
|
||||
|
||||
**Run tests:**
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
**Run tests with race detection (CI configuration):**
|
||||
```bash
|
||||
go test -race ./...
|
||||
```
|
||||
|
||||
**Build (module-only, no binaries):**
|
||||
```bash
|
||||
go build ./...
|
||||
```
|
||||
|
||||
**Run single test:**
|
||||
```bash
|
||||
go test -run TestName ./...
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
**Hash factory pattern:** The `HashGeneratorFcn` type (scram.go:23) is the entry point for creating clients and servers. Package-level variables `SHA1`, `SHA256`, `SHA512` provide pre-configured hash functions. All client/server creation flows through these hash generators.
|
||||
|
||||
**Client (`client.go`):** Holds authentication configuration for a username/password/authzID tuple. Maintains a cache of derived keys (PBKDF2 results) indexed by `KeyFactors` (salt + iteration count). Thread-safe via RWMutex. Creates `ClientConversation` instances for individual auth attempts.
|
||||
|
||||
**Server (`server.go`):** Holds credential lookup callback and nonce generator. Creates `ServerConversation` instances for individual auth attempts.
|
||||
|
||||
**Conversations:** State machines implementing the SCRAM protocol exchange:
|
||||
- `ClientConversation` (client_conv.go): States are `clientStarting` → `clientFirst` → `clientFinal` → `clientDone`
|
||||
- `ServerConversation` (server_conv.go): States are `serverFirst` → `serverFinal` → `serverDone`
|
||||
|
||||
Both use a `Step(string) (string, error)` method to advance through protocol stages.
|
||||
|
||||
**Message parsing (`parse.go`):** Parses SCRAM protocol messages into structs. Separate parsers for client-first, server-first, client-final, and server-final messages.
|
||||
|
||||
**Shared utilities (`common.go`):**
|
||||
- `NonceGeneratorFcn`: Default uses base64-encoded 24 bytes from crypto/rand
|
||||
- `derivedKeys`: Struct caching ClientKey, StoredKey, ServerKey
|
||||
- `KeyFactors`: Salt + iteration count, used as cache key
|
||||
- `StoredCredentials`: What servers must store for each user
|
||||
- `CredentialLookup`: Callback type servers use to retrieve stored credentials
|
||||
|
||||
### Key Design Patterns
|
||||
|
||||
**Dependency injection:** Server requires a `CredentialLookup` callback, making storage mechanism pluggable.
|
||||
|
||||
**Caching:** Client caches expensive PBKDF2 results in a map keyed by `KeyFactors`. This optimizes reconnection scenarios where salt/iteration count remain constant.
|
||||
|
||||
**Factory methods:** Hash generators provide `.NewClient()` and `.NewServer()` methods that handle SASLprep normalization. Alternative `.NewClientUnprepped()` exists for custom normalization.
|
||||
|
||||
**Configuration via chaining:** Both Client and Server support `.WithNonceGenerator()` for custom nonce generation (primarily for testing).
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- Default minimum PBKDF2 iterations: 4096 (client.go:45)
|
||||
- All string comparisons use `hmac.Equal()` for constant-time comparison
|
||||
- SASLprep normalization applied by default via xdg-go/stringprep dependency
|
||||
- Nonce generation uses crypto/rand
|
||||
|
||||
## Testing
|
||||
|
||||
Tests include conversation state machine tests (client_conv_test.go, server_conv_test.go), integration tests, and examples (doc_test.go). Test data in testdata_test.go.
|
||||
175
server/vendor/github.com/xdg-go/scram/LICENSE
generated
vendored
Normal file
175
server/vendor/github.com/xdg-go/scram/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
114
server/vendor/github.com/xdg-go/scram/README.md
generated
vendored
Normal file
114
server/vendor/github.com/xdg-go/scram/README.md
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
[](https://pkg.go.dev/github.com/xdg-go/scram)
|
||||
[](https://goreportcard.com/report/github.com/xdg-go/scram)
|
||||
[](https://github.com/xdg-go/scram/actions/workflows/test.yml)
|
||||
|
||||
# scram – Go implementation of RFC-5802
|
||||
|
||||
## Description
|
||||
|
||||
Package scram provides client and server implementations of the Salted
|
||||
Challenge Response Authentication Mechanism (SCRAM) described in
|
||||
- [RFC-5802](https://tools.ietf.org/html/rfc5802)
|
||||
- [RFC-5929](https://tools.ietf.org/html/rfc5929)
|
||||
- [RFC-7677](https://tools.ietf.org/html/rfc7677)
|
||||
- [RFC-9266](https://tools.ietf.org/html/rfc9266)
|
||||
|
||||
It includes both client and server side support.
|
||||
|
||||
Channel binding is supported for SCRAM-PLUS variants, including:
|
||||
- `tls-unique` (RFC 5929) - insecure, but required
|
||||
- `tls-server-end-point` (RFC 5929) - works with all TLS versions
|
||||
- `tls-exporter` (RFC 9266) - recommended for TLS 1.3+
|
||||
|
||||
SCRAM message extensions are not supported.
|
||||
|
||||
## Examples
|
||||
|
||||
### Client side
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/xdg-go/scram"
|
||||
|
||||
func main() {
|
||||
// Get Client with username, password and (optional) authorization ID.
|
||||
clientSHA1, err := scram.SHA1.NewClient("mulder", "trustno1", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Prepare the authentication conversation. Use the empty string as the
|
||||
// initial server message argument to start the conversation.
|
||||
conv := clientSHA1.NewConversation()
|
||||
var serverMsg string
|
||||
|
||||
// Get the first message, send it and read the response.
|
||||
firstMsg, err := conv.Step(serverMsg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
serverMsg = sendClientMsg(firstMsg)
|
||||
|
||||
// Get the second message, send it, and read the response.
|
||||
secondMsg, err := conv.Step(serverMsg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
serverMsg = sendClientMsg(secondMsg)
|
||||
|
||||
// Validate the server's final message. We have no further message to
|
||||
// send so ignore that return value.
|
||||
_, err = conv.Step(serverMsg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func sendClientMsg(s string) string {
|
||||
// A real implementation would send this to a server and read a reply.
|
||||
return ""
|
||||
}
|
||||
|
||||
### Client side with channel binding (SCRAM-PLUS)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/xdg-go/scram"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Establish TLS connection
|
||||
tlsConn, err := tls.Dial("tcp", "server:port", &tls.Config{MinVersion: tls.VersionTLS13})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer tlsConn.Close()
|
||||
|
||||
// Get Client with username, password
|
||||
client, err := scram.SHA256.NewClient("mulder", "trustno1", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create channel binding from TLS connection (TLS 1.3 example)
|
||||
// Use NewTLSExporterBinding for TLS 1.3+, NewTLSServerEndpointBinding for all TLS versions
|
||||
channelBinding, err := scram.NewTLSExporterBinding(&tlsConn.ConnectionState())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create conversation with channel binding for SCRAM-SHA-256-PLUS
|
||||
conv := client.NewConversationWithChannelBinding(channelBinding)
|
||||
// ... rest of authentication conversation
|
||||
}
|
||||
|
||||
## Copyright and License
|
||||
|
||||
Copyright 2018 by David A. Golden. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"). You may
|
||||
obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
144
server/vendor/github.com/xdg-go/scram/channel_binding.go
generated
vendored
Normal file
144
server/vendor/github.com/xdg-go/scram/channel_binding.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// ChannelBindingType represents the type of channel binding to use with
|
||||
// SCRAM-PLUS authentication variants. The type must match one of the
|
||||
// channel binding types defined in RFC 5056, RFC 5929, or RFC 9266.
|
||||
type ChannelBindingType string
|
||||
|
||||
const (
|
||||
// ChannelBindingNone indicates no channel binding is used.
|
||||
ChannelBindingNone ChannelBindingType = ""
|
||||
|
||||
// ChannelBindingTLSUnique uses the TLS Finished message from the first
|
||||
// TLS handshake (RFC 5929). This is considered insecure, but is included
|
||||
// as required by RFC 5802.
|
||||
ChannelBindingTLSUnique ChannelBindingType = "tls-unique"
|
||||
|
||||
// ChannelBindingTLSServerEndpoint uses a hash of the server's certificate
|
||||
// (RFC 5929). This works with all TLS versions including TLS 1.3.
|
||||
ChannelBindingTLSServerEndpoint ChannelBindingType = "tls-server-end-point"
|
||||
|
||||
// ChannelBindingTLSExporter uses TLS Exported Keying Material with the
|
||||
// label "EXPORTER-Channel-Binding" (RFC 9266). This is the recommended
|
||||
// channel binding type for TLS 1.3.
|
||||
ChannelBindingTLSExporter ChannelBindingType = "tls-exporter"
|
||||
)
|
||||
|
||||
// ChannelBinding holds the channel binding type and data for SCRAM-PLUS
|
||||
// authentication. Use constructors to create type-specific bindings.
|
||||
type ChannelBinding struct {
|
||||
Type ChannelBindingType
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// IsSupported returns true if the channel binding is configured with a
|
||||
// non-empty type and data.
|
||||
func (cb ChannelBinding) IsSupported() bool {
|
||||
return cb.Type != ChannelBindingNone && len(cb.Data) > 0
|
||||
}
|
||||
|
||||
// Matches returns true if this channel binding matches another channel
|
||||
// binding in both type and data.
|
||||
func (cb ChannelBinding) Matches(other ChannelBinding) bool {
|
||||
if cb.Type != other.Type {
|
||||
return false
|
||||
}
|
||||
return hmac.Equal(cb.Data, other.Data)
|
||||
}
|
||||
|
||||
// NewTLSUniqueBinding creates a ChannelBinding for tls-unique channel binding.
|
||||
// Since Go's standard library doesn't expose the TLS Finished message,
|
||||
// applications must provide the data directly.
|
||||
//
|
||||
// Note: tls-unique is considered insecure and should generally be avoided.
|
||||
func NewTLSUniqueBinding(data []byte) ChannelBinding {
|
||||
// Create a defensive copy to prevent caller from modifying the data
|
||||
cbData := make([]byte, len(data))
|
||||
copy(cbData, data)
|
||||
return ChannelBinding{
|
||||
Type: ChannelBindingTLSUnique,
|
||||
Data: cbData,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTLSServerEndpointBinding creates a ChannelBinding for tls-server-end-point
|
||||
// channel binding per RFC 5929. It extracts the server's certificate from
|
||||
// the TLS connection state and hashes it using the appropriate hash function
|
||||
// based on the certificate's signature algorithm.
|
||||
//
|
||||
// This works with all TLS versions including TLS 1.3.
|
||||
func NewTLSServerEndpointBinding(connState *tls.ConnectionState) (ChannelBinding, error) {
|
||||
if connState == nil {
|
||||
return ChannelBinding{}, errors.New("connection state is nil")
|
||||
}
|
||||
|
||||
if len(connState.PeerCertificates) == 0 {
|
||||
return ChannelBinding{}, errors.New("no peer certificates")
|
||||
}
|
||||
|
||||
cert := connState.PeerCertificates[0]
|
||||
|
||||
// Determine hash algorithm per RFC 5929
|
||||
var h hash.Hash
|
||||
switch cert.SignatureAlgorithm {
|
||||
case x509.MD5WithRSA, x509.SHA1WithRSA, x509.DSAWithSHA1,
|
||||
x509.ECDSAWithSHA1:
|
||||
h = sha256.New() // Use SHA-256 for MD5/SHA-1
|
||||
case x509.SHA256WithRSA, x509.SHA256WithRSAPSS,
|
||||
x509.ECDSAWithSHA256:
|
||||
h = sha256.New()
|
||||
case x509.SHA384WithRSA, x509.SHA384WithRSAPSS,
|
||||
x509.ECDSAWithSHA384:
|
||||
h = sha512.New384()
|
||||
case x509.SHA512WithRSA, x509.SHA512WithRSAPSS,
|
||||
x509.ECDSAWithSHA512:
|
||||
h = sha512.New()
|
||||
default:
|
||||
return ChannelBinding{}, fmt.Errorf("unsupported signature algorithm: %v",
|
||||
cert.SignatureAlgorithm)
|
||||
}
|
||||
|
||||
h.Write(cert.Raw)
|
||||
return ChannelBinding{
|
||||
Type: ChannelBindingTLSServerEndpoint,
|
||||
Data: h.Sum(nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewTLSExporterBinding creates a ChannelBinding for tls-exporter channel binding
|
||||
// per RFC 9266. It uses TLS Exported Keying Material with the label
|
||||
// "EXPORTER-Channel-Binding" and an empty context.
|
||||
//
|
||||
// This is the recommended channel binding type for TLS 1.3+.
|
||||
func NewTLSExporterBinding(connState *tls.ConnectionState) (ChannelBinding, error) {
|
||||
if connState == nil {
|
||||
return ChannelBinding{}, errors.New("connection state is nil")
|
||||
}
|
||||
|
||||
cbData, err := connState.ExportKeyingMaterial("EXPORTER-Channel-Binding", nil, 32)
|
||||
if err != nil {
|
||||
return ChannelBinding{}, fmt.Errorf("failed to export keying material: %w", err)
|
||||
}
|
||||
|
||||
return ChannelBinding{
|
||||
Type: ChannelBindingTLSExporter,
|
||||
Data: cbData,
|
||||
}, nil
|
||||
}
|
||||
198
server/vendor/github.com/xdg-go/scram/client.go
generated
vendored
Normal file
198
server/vendor/github.com/xdg-go/scram/client.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Client implements the client side of SCRAM authentication. It holds
|
||||
// configuration values needed to initialize new client-side conversations for
|
||||
// a specific username, password and authorization ID tuple. Client caches
|
||||
// the computationally-expensive parts of a SCRAM conversation as described in
|
||||
// RFC-5802. If repeated authentication conversations may be required for a
|
||||
// user (e.g. disconnect/reconnect), the user's Client should be preserved.
|
||||
//
|
||||
// For security reasons, Clients have a default minimum PBKDF2 iteration count
|
||||
// of 4096. If a server requests a smaller iteration count, an authentication
|
||||
// conversation will error.
|
||||
//
|
||||
// A Client can also be used by a server application to construct the hashed
|
||||
// authentication values to be stored for a new user. See StoredCredentials()
|
||||
// for more.
|
||||
type Client struct {
|
||||
sync.RWMutex
|
||||
username string
|
||||
password string
|
||||
authzID string
|
||||
minIters int
|
||||
nonceGen NonceGeneratorFcn
|
||||
hashGen HashGeneratorFcn
|
||||
cache map[KeyFactors]derivedKeys
|
||||
}
|
||||
|
||||
func newClient(username, password, authzID string, fcn HashGeneratorFcn) *Client {
|
||||
return &Client{
|
||||
username: username,
|
||||
password: password,
|
||||
authzID: authzID,
|
||||
minIters: 4096,
|
||||
nonceGen: defaultNonceGenerator,
|
||||
hashGen: fcn,
|
||||
cache: make(map[KeyFactors]derivedKeys),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMinIterations changes minimum required PBKDF2 iteration count.
|
||||
func (c *Client) WithMinIterations(n int) *Client {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.minIters = n
|
||||
return c
|
||||
}
|
||||
|
||||
// WithNonceGenerator replaces the default nonce generator (base64 encoding of
|
||||
// 24 bytes from crypto/rand) with a custom generator. This is provided for
|
||||
// testing or for users with custom nonce requirements.
|
||||
func (c *Client) WithNonceGenerator(ng NonceGeneratorFcn) *Client {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.nonceGen = ng
|
||||
return c
|
||||
}
|
||||
|
||||
// NewConversation constructs a client-side authentication conversation.
|
||||
// Conversations cannot be reused, so this must be called for each new
|
||||
// authentication attempt.
|
||||
func (c *Client) NewConversation() *ClientConversation {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return &ClientConversation{
|
||||
client: c,
|
||||
nonceGen: c.nonceGen,
|
||||
hashGen: c.hashGen,
|
||||
minIters: c.minIters,
|
||||
}
|
||||
}
|
||||
|
||||
// NewConversationAdvertisingChannelBinding constructs a client-side
|
||||
// authentication conversation that advertises channel binding support without
|
||||
// using it. This generates the "y" GS2 flag, indicating the client supports
|
||||
// channel binding but the server did not advertise a PLUS variant mechanism.
|
||||
//
|
||||
// This helps detect downgrade attacks where a MITM strips PLUS mechanism
|
||||
// advertisements from the server's mechanism list. If the server actually
|
||||
// advertised PLUS variants, it will reject the "y" flag as a downgrade attack.
|
||||
//
|
||||
// Use this when:
|
||||
// - Your application supports channel binding (has access to TLS connection state)
|
||||
// - SASL mechanism negotiation showed the server does NOT advertise PLUS variants
|
||||
// (e.g., server advertised "SCRAM-SHA-256" but not "SCRAM-SHA-256-PLUS")
|
||||
//
|
||||
// Conversations cannot be reused, so this must be called for each new
|
||||
// authentication attempt.
|
||||
func (c *Client) NewConversationAdvertisingChannelBinding() *ClientConversation {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return &ClientConversation{
|
||||
client: c,
|
||||
nonceGen: c.nonceGen,
|
||||
hashGen: c.hashGen,
|
||||
minIters: c.minIters,
|
||||
advertiseChannelBinding: true,
|
||||
}
|
||||
}
|
||||
|
||||
// NewConversationWithChannelBinding constructs a client-side authentication
|
||||
// conversation with channel binding for SCRAM-PLUS authentication. Channel
|
||||
// binding is connection-specific, so a new conversation should be created for
|
||||
// each connection being authenticated. Conversations cannot be reused, so this
|
||||
// must be called for each new authentication attempt.
|
||||
func (c *Client) NewConversationWithChannelBinding(cb ChannelBinding) *ClientConversation {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return &ClientConversation{
|
||||
client: c,
|
||||
nonceGen: c.nonceGen,
|
||||
hashGen: c.hashGen,
|
||||
minIters: c.minIters,
|
||||
channelBinding: cb,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) getDerivedKeys(kf KeyFactors) (derivedKeys, error) {
|
||||
dk, ok := c.getCache(kf)
|
||||
if !ok {
|
||||
var err error
|
||||
dk, err = c.computeKeys(kf)
|
||||
if err != nil {
|
||||
return derivedKeys{}, err
|
||||
}
|
||||
c.setCache(kf, dk)
|
||||
}
|
||||
return dk, nil
|
||||
}
|
||||
|
||||
// GetStoredCredentials takes a salt and iteration count structure and
|
||||
// provides the values that must be stored by a server to authenticate a
|
||||
// user. These values are what the Server credential lookup function must
|
||||
// return for a given username.
|
||||
//
|
||||
// Deprecated: Use GetStoredCredentialsWithError for proper error handling.
|
||||
// This method panics if PBKDF2 key derivation fails, which should only
|
||||
// occur with invalid KeyFactors parameters.
|
||||
func (c *Client) GetStoredCredentials(kf KeyFactors) StoredCredentials {
|
||||
creds, err := c.GetStoredCredentialsWithError(kf)
|
||||
if err != nil {
|
||||
panic("scram: GetStoredCredentials failed: " + err.Error())
|
||||
}
|
||||
return creds
|
||||
}
|
||||
|
||||
// GetStoredCredentialsWithError takes a salt and iteration count structure and
|
||||
// provides the values that must be stored by a server to authenticate a
|
||||
// user. These values are what the Server credential lookup function must
|
||||
// return for a given username.
|
||||
//
|
||||
// Returns an error if PBKDF2 key derivation fails, which can occur with
|
||||
// invalid parameters in Go 1.24+ (e.g., invalid iteration counts or key lengths).
|
||||
func (c *Client) GetStoredCredentialsWithError(kf KeyFactors) (StoredCredentials, error) {
|
||||
dk, err := c.getDerivedKeys(kf)
|
||||
return StoredCredentials{
|
||||
KeyFactors: kf,
|
||||
StoredKey: dk.StoredKey,
|
||||
ServerKey: dk.ServerKey,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (c *Client) computeKeys(kf KeyFactors) (derivedKeys, error) {
|
||||
h := c.hashGen()
|
||||
saltedPassword, err := pbkdf2Key(c.hashGen, c.password, []byte(kf.Salt), kf.Iters, h.Size())
|
||||
if err != nil {
|
||||
return derivedKeys{}, err
|
||||
}
|
||||
clientKey := computeHMAC(c.hashGen, saltedPassword, []byte("Client Key"))
|
||||
|
||||
return derivedKeys{
|
||||
ClientKey: clientKey,
|
||||
StoredKey: computeHash(c.hashGen, clientKey),
|
||||
ServerKey: computeHMAC(c.hashGen, saltedPassword, []byte("Server Key")),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) getCache(kf KeyFactors) (derivedKeys, bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
dk, ok := c.cache[kf]
|
||||
return dk, ok
|
||||
}
|
||||
|
||||
func (c *Client) setCache(kf KeyFactors, dk derivedKeys) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.cache[kf] = dk
|
||||
}
|
||||
181
server/vendor/github.com/xdg-go/scram/client_conv.go
generated
vendored
Normal file
181
server/vendor/github.com/xdg-go/scram/client_conv.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type clientState int
|
||||
|
||||
const (
|
||||
clientStarting clientState = iota
|
||||
clientFirst
|
||||
clientFinal
|
||||
clientDone
|
||||
)
|
||||
|
||||
// ClientConversation implements the client-side of an authentication
|
||||
// conversation with a server. A new conversation must be created for
|
||||
// each authentication attempt.
|
||||
type ClientConversation struct {
|
||||
client *Client
|
||||
nonceGen NonceGeneratorFcn
|
||||
hashGen HashGeneratorFcn
|
||||
minIters int
|
||||
state clientState
|
||||
valid bool
|
||||
gs2 string
|
||||
nonce string
|
||||
c1b string
|
||||
serveSig []byte
|
||||
channelBinding ChannelBinding
|
||||
advertiseChannelBinding bool // if true, use "y" flag instead of "n" or "p"
|
||||
}
|
||||
|
||||
// Step takes a string provided from a server (or just an empty string for the
|
||||
// very first conversation step) and attempts to move the authentication
|
||||
// conversation forward. It returns a string to be sent to the server or an
|
||||
// error if the server message is invalid. Calling Step after a conversation
|
||||
// completes is also an error.
|
||||
func (cc *ClientConversation) Step(challenge string) (response string, err error) {
|
||||
switch cc.state {
|
||||
case clientStarting:
|
||||
cc.state = clientFirst
|
||||
response, err = cc.firstMsg()
|
||||
case clientFirst:
|
||||
cc.state = clientFinal
|
||||
response, err = cc.finalMsg(challenge)
|
||||
case clientFinal:
|
||||
cc.state = clientDone
|
||||
response, err = cc.validateServer(challenge)
|
||||
default:
|
||||
response, err = "", errors.New("Conversation already completed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Done returns true if the conversation is completed or has errored.
|
||||
func (cc *ClientConversation) Done() bool {
|
||||
return cc.state == clientDone
|
||||
}
|
||||
|
||||
// Valid returns true if the conversation successfully authenticated with the
|
||||
// server, including counter-validation that the server actually has the
|
||||
// user's stored credentials.
|
||||
func (cc *ClientConversation) Valid() bool {
|
||||
return cc.valid
|
||||
}
|
||||
|
||||
func (cc *ClientConversation) firstMsg() (string, error) {
|
||||
// Values are cached for use in final message parameters
|
||||
cc.gs2 = cc.gs2Header()
|
||||
cc.nonce = cc.client.nonceGen()
|
||||
cc.c1b = fmt.Sprintf("n=%s,r=%s", encodeName(cc.client.username), cc.nonce)
|
||||
|
||||
return cc.gs2 + cc.c1b, nil
|
||||
}
|
||||
|
||||
func (cc *ClientConversation) finalMsg(s1 string) (string, error) {
|
||||
msg, err := parseServerFirst(s1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Check nonce prefix and update
|
||||
if !strings.HasPrefix(msg.nonce, cc.nonce) {
|
||||
return "", errors.New("server nonce did not extend client nonce")
|
||||
}
|
||||
cc.nonce = msg.nonce
|
||||
|
||||
// Check iteration count vs minimum
|
||||
if msg.iters < cc.minIters {
|
||||
return "", fmt.Errorf("server requested too few iterations (%d)", msg.iters)
|
||||
}
|
||||
|
||||
// Create channel binding data per RFC 5802:
|
||||
// - For "p" flag: gs2-header + channel-binding-data
|
||||
// - For "n" or "y" flags: gs2-header only (no channel-binding-data)
|
||||
cbindData := []byte(cc.gs2)
|
||||
if cc.channelBinding.IsSupported() {
|
||||
// Only append channel binding data when actually using it (flag "p")
|
||||
cbindData = append(cbindData, cc.channelBinding.Data...)
|
||||
}
|
||||
|
||||
// Create client-final-message-without-proof
|
||||
c2wop := fmt.Sprintf(
|
||||
"c=%s,r=%s",
|
||||
base64.StdEncoding.EncodeToString(cbindData),
|
||||
cc.nonce,
|
||||
)
|
||||
|
||||
// Create auth message
|
||||
authMsg := cc.c1b + "," + s1 + "," + c2wop
|
||||
|
||||
// Get derived keys from client cache
|
||||
dk, err := cc.client.getDerivedKeys(KeyFactors{Salt: string(msg.salt), Iters: msg.iters})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create proof as clientkey XOR clientsignature
|
||||
clientSignature := computeHMAC(cc.hashGen, dk.StoredKey, []byte(authMsg))
|
||||
clientProof, err := xorBytes(dk.ClientKey, clientSignature)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
proof := base64.StdEncoding.EncodeToString(clientProof)
|
||||
|
||||
// Cache ServerSignature for later validation
|
||||
cc.serveSig = computeHMAC(cc.hashGen, dk.ServerKey, []byte(authMsg))
|
||||
|
||||
return fmt.Sprintf("%s,p=%s", c2wop, proof), nil
|
||||
}
|
||||
|
||||
func (cc *ClientConversation) validateServer(s2 string) (string, error) {
|
||||
msg, err := parseServerFinal(s2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(msg.err) > 0 {
|
||||
return "", fmt.Errorf("server error: %s", msg.err)
|
||||
}
|
||||
|
||||
if !hmac.Equal(msg.verifier, cc.serveSig) {
|
||||
return "", errors.New("server validation failed")
|
||||
}
|
||||
|
||||
cc.valid = true
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (cc *ClientConversation) gs2Header() string {
|
||||
var cbFlag string
|
||||
|
||||
if cc.channelBinding.IsSupported() {
|
||||
// Client is using channel binding with specific type
|
||||
cbFlag = fmt.Sprintf("p=%s", cc.channelBinding.Type)
|
||||
} else if cc.advertiseChannelBinding {
|
||||
// Client supports channel binding but server didn't advertise PLUS
|
||||
cbFlag = "y"
|
||||
} else {
|
||||
// Client doesn't support channel binding
|
||||
cbFlag = "n"
|
||||
}
|
||||
|
||||
authzPart := ""
|
||||
if cc.client.authzID != "" {
|
||||
authzPart = "a=" + encodeName(cc.client.authzID)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s,", cbFlag, authzPart)
|
||||
}
|
||||
135
server/vendor/github.com/xdg-go/scram/common.go
generated
vendored
Normal file
135
server/vendor/github.com/xdg-go/scram/common.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NonceGeneratorFcn defines a function that returns a string of high-quality
|
||||
// random printable ASCII characters EXCLUDING the comma (',') character. The
|
||||
// default nonce generator provides Base64 encoding of 24 bytes from
|
||||
// crypto/rand.
|
||||
type NonceGeneratorFcn func() string
|
||||
|
||||
// derivedKeys collects the three cryptographically derived values
|
||||
// into one struct for caching.
|
||||
type derivedKeys struct {
|
||||
ClientKey []byte
|
||||
StoredKey []byte
|
||||
ServerKey []byte
|
||||
}
|
||||
|
||||
// KeyFactors represent the two server-provided factors needed to compute
|
||||
// client credentials for authentication. Salt is decoded bytes (i.e. not
|
||||
// base64), but in string form so that KeyFactors can be used as a map key for
|
||||
// cached credentials.
|
||||
type KeyFactors struct {
|
||||
Salt string
|
||||
Iters int
|
||||
}
|
||||
|
||||
// StoredCredentials are the values that a server must store for a given
|
||||
// username to allow authentication. They include the salt and iteration
|
||||
// count, plus the derived values to authenticate a client and for the server
|
||||
// to authenticate itself back to the client.
|
||||
//
|
||||
// NOTE: these are specific to a given hash function. To allow a user to
|
||||
// authenticate with either SCRAM-SHA-1 or SCRAM-SHA-256, two sets of
|
||||
// StoredCredentials must be created and stored, one for each hash function.
|
||||
type StoredCredentials struct {
|
||||
KeyFactors
|
||||
StoredKey []byte
|
||||
ServerKey []byte
|
||||
}
|
||||
|
||||
// CredentialLookup is a callback to provide StoredCredentials for a given
|
||||
// username. This is used to configure Server objects.
|
||||
//
|
||||
// NOTE: these are specific to a given hash function. The callback provided
|
||||
// to a Server with a given hash function must provide the corresponding
|
||||
// StoredCredentials.
|
||||
type CredentialLookup func(string) (StoredCredentials, error)
|
||||
|
||||
// Server error values as defined in RFC-5802 and RFC-7677. These are returned
|
||||
// by the server in error responses as "e=<value>".
|
||||
const (
|
||||
// ErrInvalidEncoding indicates the client message had invalid encoding
|
||||
ErrInvalidEncoding = "e=invalid-encoding"
|
||||
|
||||
// ErrExtensionsNotSupported indicates unrecognized 'm' value
|
||||
ErrExtensionsNotSupported = "e=extensions-not-supported"
|
||||
|
||||
// ErrInvalidProof indicates the authentication proof from the client was invalid
|
||||
ErrInvalidProof = "e=invalid-proof"
|
||||
|
||||
// ErrChannelBindingsDontMatch indicates channel binding data didn't match expected value
|
||||
ErrChannelBindingsDontMatch = "e=channel-bindings-dont-match"
|
||||
|
||||
// ErrServerDoesSupportChannelBinding indicates server does support channel
|
||||
// binding. This is returned if a downgrade attack is detected or if the
|
||||
// client does not support binding and channel binding is required.
|
||||
ErrServerDoesSupportChannelBinding = "e=server-does-support-channel-binding"
|
||||
|
||||
// ErrChannelBindingNotSupported indicates channel binding is not supported
|
||||
ErrChannelBindingNotSupported = "e=channel-binding-not-supported"
|
||||
|
||||
// ErrUnsupportedChannelBindingType indicates the requested channel binding type is not supported
|
||||
ErrUnsupportedChannelBindingType = "e=unsupported-channel-binding-type"
|
||||
|
||||
// ErrUnknownUser indicates the specified user does not exist
|
||||
ErrUnknownUser = "e=unknown-user"
|
||||
|
||||
// ErrInvalidUsernameEncoding indicates invalid username encoding (invalid UTF-8 or SASLprep failed)
|
||||
ErrInvalidUsernameEncoding = "e=invalid-username-encoding"
|
||||
|
||||
// ErrNoResources indicates the server is out of resources
|
||||
ErrNoResources = "e=no-resources"
|
||||
|
||||
// ErrOtherError is a catch-all for unspecified errors. The server may substitute
|
||||
// the real reason with this error to prevent information disclosure.
|
||||
ErrOtherError = "e=other-error"
|
||||
)
|
||||
|
||||
func defaultNonceGenerator() string {
|
||||
raw := make([]byte, 24)
|
||||
nonce := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
|
||||
_, _ = rand.Read(raw)
|
||||
base64.StdEncoding.Encode(nonce, raw)
|
||||
return string(nonce)
|
||||
}
|
||||
|
||||
func encodeName(s string) string {
|
||||
return strings.Replace(strings.Replace(s, "=", "=3D", -1), ",", "=2C", -1)
|
||||
}
|
||||
|
||||
func computeHash(hg HashGeneratorFcn, b []byte) []byte {
|
||||
h := hg()
|
||||
h.Write(b)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func computeHMAC(hg HashGeneratorFcn, key, data []byte) []byte {
|
||||
mac := hmac.New(hg, key)
|
||||
mac.Write(data)
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
func xorBytes(a, b []byte) ([]byte, error) {
|
||||
if len(a) != len(b) {
|
||||
return nil, errors.New("internal error: xorBytes arguments must have equal length")
|
||||
}
|
||||
xor := make([]byte, len(a))
|
||||
for i := range a {
|
||||
xor[i] = a[i] ^ b[i]
|
||||
}
|
||||
return xor, nil
|
||||
}
|
||||
59
server/vendor/github.com/xdg-go/scram/doc.go
generated
vendored
Normal file
59
server/vendor/github.com/xdg-go/scram/doc.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
// Package scram provides client and server implementations of the Salted
|
||||
// Challenge Response Authentication Mechanism (SCRAM) described in RFC-5802,
|
||||
// RFC-7677, and RFC-9266.
|
||||
//
|
||||
// # Usage
|
||||
//
|
||||
// The scram package provides variables, `SHA1`, `SHA256`, and `SHA512`, that
|
||||
// are used to construct Client or Server objects.
|
||||
//
|
||||
// clientSHA1, err := scram.SHA1.NewClient(username, password, authID)
|
||||
// clientSHA256, err := scram.SHA256.NewClient(username, password, authID)
|
||||
// clientSHA512, err := scram.SHA512.NewClient(username, password, authID)
|
||||
//
|
||||
// serverSHA1, err := scram.SHA1.NewServer(credentialLookupFcn)
|
||||
// serverSHA256, err := scram.SHA256.NewServer(credentialLookupFcn)
|
||||
// serverSHA512, err := scram.SHA512.NewServer(credentialLookupFcn)
|
||||
//
|
||||
// These objects are used to construct ClientConversation or
|
||||
// ServerConversation objects that are used to carry out authentication.
|
||||
//
|
||||
// clientConv := client.NewConversation()
|
||||
// serverConv := server.NewConversation()
|
||||
//
|
||||
// # Channel Binding (SCRAM-PLUS)
|
||||
//
|
||||
// The scram package supports channel binding for SCRAM-PLUS authentication
|
||||
// variants as described in RFC-5802, RFC-5929, and RFC-9266. Channel binding
|
||||
// cryptographically binds the SCRAM authentication to an underlying TLS
|
||||
// connection, preventing man-in-the-middle attacks.
|
||||
//
|
||||
// To use channel binding, create conversations with channel binding data
|
||||
// obtained from the TLS connection:
|
||||
//
|
||||
// // Client example with tls-exporter (TLS 1.3+)
|
||||
// client, _ := scram.SHA256.NewClient(username, password, "")
|
||||
// channelBinding, _ := scram.NewTLSExporterBinding(&tlsConn.ConnectionState())
|
||||
// clientConv := client.NewConversationWithChannelBinding(channelBinding)
|
||||
//
|
||||
// // Server conversation with the same channel binding
|
||||
// server, _ := scram.SHA256.NewServer(credentialLookupFcn)
|
||||
// serverConv := server.NewConversationWithChannelBinding(channelBinding)
|
||||
//
|
||||
// Helper functions are provided to create ChannelBinding values from TLS connections:
|
||||
// - NewTLSServerEndpointBinding: Uses server certificate hash (RFC 5929, all TLS versions)
|
||||
// - NewTLSExporterBinding: Uses exported keying material (RFC 9266, recommended for TLS 1.3+)
|
||||
//
|
||||
// Channel binding is configured on conversations rather than clients or servers
|
||||
// because binding data is connection-specific.
|
||||
//
|
||||
// Channel binding type negotiation is not defined by the SCRAM protocol.
|
||||
// Applications must ensure both client and server agree on the same channel binding
|
||||
// type.
|
||||
package scram
|
||||
216
server/vendor/github.com/xdg-go/scram/parse.go
generated
vendored
Normal file
216
server/vendor/github.com/xdg-go/scram/parse.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type c1Msg struct {
|
||||
gs2Header string
|
||||
gs2BindFlag string // "n", "y", or "p"
|
||||
channelBinding string // channel binding type name if gs2BindFlag is "p"
|
||||
authzID string
|
||||
username string
|
||||
nonce string
|
||||
c1b string
|
||||
}
|
||||
|
||||
type c2Msg struct {
|
||||
cbind []byte
|
||||
nonce string
|
||||
proof []byte
|
||||
c2wop string
|
||||
}
|
||||
|
||||
type s1Msg struct {
|
||||
nonce string
|
||||
salt []byte
|
||||
iters int
|
||||
}
|
||||
|
||||
type s2Msg struct {
|
||||
verifier []byte
|
||||
err string
|
||||
}
|
||||
|
||||
func parseField(s, k string) (string, error) {
|
||||
t := strings.TrimPrefix(s, k+"=")
|
||||
if t == s {
|
||||
return "", fmt.Errorf("error parsing '%s' for field '%s'", s, k)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// parseGS2Flag returns flag, channel binding type, and error.
|
||||
func parseGS2Flag(s string) (string, string, error) {
|
||||
if s == "n" || s == "y" {
|
||||
return s, "", nil
|
||||
}
|
||||
|
||||
// If not "n" or "y", must be "p=..." or error.
|
||||
cbType, err := parseField(s, "p")
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error parsing '%s' for gs2 flag", s)
|
||||
}
|
||||
|
||||
switch ChannelBindingType(cbType) {
|
||||
case ChannelBindingTLSUnique, ChannelBindingTLSServerEndpoint, ChannelBindingTLSExporter:
|
||||
// valid channel binding type
|
||||
default:
|
||||
return "", "", fmt.Errorf("invalid channel binding type: %s", cbType)
|
||||
}
|
||||
return "p", cbType, nil
|
||||
}
|
||||
|
||||
func parseFieldBase64(s, k string) ([]byte, error) {
|
||||
raw, err := parseField(s, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed decoding field '%s': %v", k, err)
|
||||
}
|
||||
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
func parseFieldInt(s, k string) (int, error) {
|
||||
raw, err := parseField(s, k)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
num, err := strconv.Atoi(raw)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error parsing field '%s': %v", k, err)
|
||||
}
|
||||
|
||||
return num, nil
|
||||
}
|
||||
|
||||
func parseClientFirst(c1 string) (msg c1Msg, err error) {
|
||||
fields := strings.Split(c1, ",")
|
||||
if len(fields) < 4 {
|
||||
err = errors.New("not enough fields in first server message")
|
||||
return
|
||||
}
|
||||
|
||||
msg.gs2BindFlag, msg.channelBinding, err = parseGS2Flag(fields[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// authzID content is optional, but the field must be present.
|
||||
if len(fields[1]) > 0 {
|
||||
msg.authzID, err = parseField(fields[1], "a")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unsupported extensions field "m".
|
||||
if strings.HasPrefix(fields[2], "m=") {
|
||||
err = errors.New("SCRAM message extensions are not supported")
|
||||
return
|
||||
}
|
||||
|
||||
msg.username, err = parseField(fields[2], "n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg.nonce, err = parseField(fields[3], "r")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Recombine the gs2Header: gs2-cbind-flag "," [ authzid ] ","
|
||||
msg.gs2Header = fields[0] + "," + fields[1] + ","
|
||||
|
||||
// Recombine the client-first-message-bare: username "," nonce
|
||||
msg.c1b = strings.Join(fields[2:], ",")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseClientFinal(c2 string) (msg c2Msg, err error) {
|
||||
fields := strings.Split(c2, ",")
|
||||
if len(fields) < 3 {
|
||||
err = errors.New("not enough fields in first server message")
|
||||
return
|
||||
}
|
||||
|
||||
msg.cbind, err = parseFieldBase64(fields[0], "c")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg.nonce, err = parseField(fields[1], "r")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Extension fields may come between nonce and proof, so we
|
||||
// grab the *last* fields as proof.
|
||||
msg.proof, err = parseFieldBase64(fields[len(fields)-1], "p")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg.c2wop = c2[:strings.LastIndex(c2, ",")]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseServerFirst(s1 string) (msg s1Msg, err error) {
|
||||
|
||||
// Check for unsupported extensions field "m".
|
||||
if strings.HasPrefix(s1, "m=") {
|
||||
err = errors.New("SCRAM message extensions are not supported")
|
||||
return
|
||||
}
|
||||
|
||||
fields := strings.Split(s1, ",")
|
||||
if len(fields) < 3 {
|
||||
err = errors.New("not enough fields in first server message")
|
||||
return
|
||||
}
|
||||
|
||||
msg.nonce, err = parseField(fields[0], "r")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg.salt, err = parseFieldBase64(fields[1], "s")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg.iters, err = parseFieldInt(fields[2], "i")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseServerFinal(s2 string) (msg s2Msg, err error) {
|
||||
fields := strings.Split(s2, ",")
|
||||
|
||||
msg.verifier, err = parseFieldBase64(fields[0], "v")
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg.err, err = parseField(fields[0], "e")
|
||||
|
||||
return
|
||||
}
|
||||
18
server/vendor/github.com/xdg-go/scram/pbkdf2_go124.go
generated
vendored
Normal file
18
server/vendor/github.com/xdg-go/scram/pbkdf2_go124.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2025 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
//go:build go1.24
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"crypto/pbkdf2"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func pbkdf2Key(h func() hash.Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) {
|
||||
return pbkdf2.Key(h, password, salt, iter, keyLength)
|
||||
}
|
||||
19
server/vendor/github.com/xdg-go/scram/pbkdf2_legacy.go
generated
vendored
Normal file
19
server/vendor/github.com/xdg-go/scram/pbkdf2_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2025 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
//go:build !go1.24
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"hash"
|
||||
|
||||
"github.com/xdg-go/pbkdf2"
|
||||
)
|
||||
|
||||
func pbkdf2Key(h func() hash.Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) {
|
||||
return pbkdf2.Key([]byte(password), salt, iter, keyLength, h), nil
|
||||
}
|
||||
71
server/vendor/github.com/xdg-go/scram/scram.go
generated
vendored
Normal file
71
server/vendor/github.com/xdg-go/scram/scram.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/xdg-go/stringprep"
|
||||
)
|
||||
|
||||
// HashGeneratorFcn abstracts a factory function that returns a hash.Hash
|
||||
// value to be used for SCRAM operations. Generally, one would use the
|
||||
// provided package variables, `scram.SHA1` and `scram.SHA256`, for the most
|
||||
// common forms of SCRAM.
|
||||
type HashGeneratorFcn func() hash.Hash
|
||||
|
||||
// SHA1 is a function that returns a crypto/sha1 hasher and should be used to
|
||||
// create Client objects configured for SHA-1 hashing.
|
||||
var SHA1 HashGeneratorFcn = func() hash.Hash { return sha1.New() }
|
||||
|
||||
// SHA256 is a function that returns a crypto/sha256 hasher and should be used
|
||||
// to create Client objects configured for SHA-256 hashing.
|
||||
var SHA256 HashGeneratorFcn = func() hash.Hash { return sha256.New() }
|
||||
|
||||
// SHA512 is a function that returns a crypto/sha512 hasher and should be used
|
||||
// to create Client objects configured for SHA-512 hashing.
|
||||
var SHA512 HashGeneratorFcn = func() hash.Hash { return sha512.New() }
|
||||
|
||||
// NewClient constructs a SCRAM client component based on a given hash.Hash
|
||||
// factory receiver. This constructor will normalize the username, password
|
||||
// and authzID via the SASLprep algorithm, as recommended by RFC-5802. If
|
||||
// SASLprep fails, the method returns an error.
|
||||
func (f HashGeneratorFcn) NewClient(username, password, authzID string) (*Client, error) {
|
||||
var userprep, passprep, authprep string
|
||||
var err error
|
||||
|
||||
if userprep, err = stringprep.SASLprep.Prepare(username); err != nil {
|
||||
return nil, fmt.Errorf("Error SASLprepping username '%s': %v", username, err)
|
||||
}
|
||||
if passprep, err = stringprep.SASLprep.Prepare(password); err != nil {
|
||||
return nil, fmt.Errorf("Error SASLprepping password '%s': %v", password, err)
|
||||
}
|
||||
if authprep, err = stringprep.SASLprep.Prepare(authzID); err != nil {
|
||||
return nil, fmt.Errorf("Error SASLprepping authzID '%s': %v", authzID, err)
|
||||
}
|
||||
|
||||
return newClient(userprep, passprep, authprep, f), nil
|
||||
}
|
||||
|
||||
// NewClientUnprepped acts like NewClient, except none of the arguments will
|
||||
// be normalized via SASLprep. This is not generally recommended, but is
|
||||
// provided for users that may have custom normalization needs.
|
||||
func (f HashGeneratorFcn) NewClientUnprepped(username, password, authzID string) (*Client, error) {
|
||||
return newClient(username, password, authzID, f), nil
|
||||
}
|
||||
|
||||
// NewServer constructs a SCRAM server component based on a given hash.Hash
|
||||
// factory receiver. To be maximally generic, it uses dependency injection to
|
||||
// handle credential lookup, which is the process of turning a username string
|
||||
// into a struct with stored credentials for authentication.
|
||||
func (f HashGeneratorFcn) NewServer(cl CredentialLookup) (*Server, error) {
|
||||
return newServer(cl, f)
|
||||
}
|
||||
109
server/vendor/github.com/xdg-go/scram/server.go
generated
vendored
Normal file
109
server/vendor/github.com/xdg-go/scram/server.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import "sync"
|
||||
|
||||
// Server implements the server side of SCRAM authentication. It holds
|
||||
// configuration values needed to initialize new server-side conversations.
|
||||
// Generally, this can be persistent within an application.
|
||||
type Server struct {
|
||||
sync.RWMutex
|
||||
credentialCB CredentialLookup
|
||||
nonceGen NonceGeneratorFcn
|
||||
hashGen HashGeneratorFcn
|
||||
}
|
||||
|
||||
func newServer(cl CredentialLookup, fcn HashGeneratorFcn) (*Server, error) {
|
||||
return &Server{
|
||||
credentialCB: cl,
|
||||
nonceGen: defaultNonceGenerator,
|
||||
hashGen: fcn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithNonceGenerator replaces the default nonce generator (base64 encoding of
|
||||
// 24 bytes from crypto/rand) with a custom generator. This is provided for
|
||||
// testing or for users with custom nonce requirements.
|
||||
func (s *Server) WithNonceGenerator(ng NonceGeneratorFcn) *Server {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.nonceGen = ng
|
||||
return s
|
||||
}
|
||||
|
||||
// NewConversation constructs a server-side authentication conversation.
|
||||
// Conversations cannot be reused, so this must be called for each new
|
||||
// authentication attempt.
|
||||
func (s *Server) NewConversation() *ServerConversation {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return &ServerConversation{
|
||||
nonceGen: s.nonceGen,
|
||||
hashGen: s.hashGen,
|
||||
credentialCB: s.credentialCB,
|
||||
}
|
||||
}
|
||||
|
||||
// NewConversationWithChannelBinding constructs a server-side authentication
|
||||
// conversation with channel binding for SCRAM-PLUS authentication.
|
||||
//
|
||||
// This signals that the server advertised PLUS mechanism variants (e.g.,
|
||||
// SCRAM-SHA-256-PLUS) during SASL negotiation, but channel binding is NOT required.
|
||||
// Clients may authenticate using either the base mechanism (e.g., SCRAM-SHA-256)
|
||||
// or the PLUS variant (e.g., SCRAM-SHA-256-PLUS).
|
||||
//
|
||||
// The server will:
|
||||
// - Accept clients without channel binding support (using "n" flag)
|
||||
// - Accept clients with matching channel binding (using "p" flag)
|
||||
// - Reject downgrade attacks (clients using "y" flag when PLUS was advertised)
|
||||
//
|
||||
// Channel binding is connection-specific, so a new conversation should be
|
||||
// created for each connection being authenticated.
|
||||
// Conversations cannot be reused, so this must be called for each new
|
||||
// authentication attempt.
|
||||
func (s *Server) NewConversationWithChannelBinding(cb ChannelBinding) *ServerConversation {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return &ServerConversation{
|
||||
nonceGen: s.nonceGen,
|
||||
hashGen: s.hashGen,
|
||||
credentialCB: s.credentialCB,
|
||||
channelBinding: cb,
|
||||
}
|
||||
}
|
||||
|
||||
// NewConversationWithChannelBindingRequired constructs a server-side authentication
|
||||
// conversation with mandatory channel binding for SCRAM-PLUS authentication.
|
||||
//
|
||||
// This signals that the server advertised ONLY SCRAM-PLUS mechanism variants
|
||||
// (e.g., only SCRAM-SHA-256-PLUS, not the base SCRAM-SHA-256) during SASL negotiation.
|
||||
// Channel binding is required for all authentication attempts.
|
||||
//
|
||||
// The server will:
|
||||
// - Accept only clients with matching channel binding (using "p" flag)
|
||||
// - Reject clients without channel binding support (using "n" flag)
|
||||
// - Reject downgrade attacks (clients using "y" flag when PLUS was advertised)
|
||||
//
|
||||
// This is intended for high-security deployments that advertise only SCRAM-PLUS
|
||||
// variants and want to enforce channel binding as mandatory.
|
||||
//
|
||||
// Channel binding is connection-specific, so a new conversation should be
|
||||
// created for each connection being authenticated.
|
||||
// Conversations cannot be reused, so this must be called for each new
|
||||
// authentication attempt.
|
||||
func (s *Server) NewConversationWithChannelBindingRequired(cb ChannelBinding) *ServerConversation {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return &ServerConversation{
|
||||
nonceGen: s.nonceGen,
|
||||
hashGen: s.hashGen,
|
||||
credentialCB: s.credentialCB,
|
||||
channelBinding: cb,
|
||||
requireChannelBinding: true,
|
||||
}
|
||||
}
|
||||
233
server/vendor/github.com/xdg-go/scram/server_conv.go
generated
vendored
Normal file
233
server/vendor/github.com/xdg-go/scram/server_conv.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package scram
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type serverState int
|
||||
|
||||
const (
|
||||
serverFirst serverState = iota
|
||||
serverFinal
|
||||
serverDone
|
||||
)
|
||||
|
||||
// ServerConversation implements the server-side of an authentication
|
||||
// conversation with a client. A new conversation must be created for
|
||||
// each authentication attempt.
|
||||
type ServerConversation struct {
|
||||
nonceGen NonceGeneratorFcn
|
||||
hashGen HashGeneratorFcn
|
||||
credentialCB CredentialLookup
|
||||
state serverState
|
||||
credential StoredCredentials
|
||||
valid bool
|
||||
gs2Header string
|
||||
username string
|
||||
authzID string
|
||||
nonce string
|
||||
c1b string
|
||||
s1 string
|
||||
channelBinding ChannelBinding
|
||||
requireChannelBinding bool
|
||||
clientCBType string
|
||||
clientCBFlag string
|
||||
}
|
||||
|
||||
// Step takes a string provided from a client and attempts to move the
|
||||
// authentication conversation forward. It returns a string to be sent to the
|
||||
// client or an error if the client message is invalid. Calling Step after a
|
||||
// conversation completes is also an error.
|
||||
func (sc *ServerConversation) Step(challenge string) (response string, err error) {
|
||||
switch sc.state {
|
||||
case serverFirst:
|
||||
sc.state = serverFinal
|
||||
response, err = sc.firstMsg(challenge)
|
||||
case serverFinal:
|
||||
sc.state = serverDone
|
||||
response, err = sc.finalMsg(challenge)
|
||||
default:
|
||||
response, err = "", errors.New("Conversation already completed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Done returns true if the conversation is completed or has errored.
|
||||
func (sc *ServerConversation) Done() bool {
|
||||
return sc.state == serverDone
|
||||
}
|
||||
|
||||
// Valid returns true if the conversation successfully authenticated the
|
||||
// client.
|
||||
func (sc *ServerConversation) Valid() bool {
|
||||
return sc.valid
|
||||
}
|
||||
|
||||
// Username returns the client-provided username. This is valid to call
|
||||
// if the first conversation Step() is successful.
|
||||
func (sc *ServerConversation) Username() string {
|
||||
return sc.username
|
||||
}
|
||||
|
||||
// AuthzID returns the (optional) client-provided authorization identity, if
|
||||
// any. If one was not provided, it returns the empty string. This is valid
|
||||
// to call if the first conversation Step() is successful.
|
||||
func (sc *ServerConversation) AuthzID() string {
|
||||
return sc.authzID
|
||||
}
|
||||
|
||||
// validateChannelBindingFlag validates the client's channel binding flag against
|
||||
// server configuration. The validation logic follows RFC 5802 section 6, but
|
||||
// extends those semantics to cover the case of required channel binding.
|
||||
//
|
||||
// Client flag validation:
|
||||
// - "n": Client doesn't support channel binding
|
||||
// - "y": Client supports channel binding but server didn't advertise PLUS
|
||||
// - "p": Client requires channel binding with specific type
|
||||
//
|
||||
// Returns server error string (empty if validation passes) and error.
|
||||
func (sc *ServerConversation) validateChannelBindingFlag() (string, error) {
|
||||
advertised := sc.channelBinding.IsSupported()
|
||||
|
||||
switch sc.clientCBFlag {
|
||||
case "n":
|
||||
// Client doesn't support channel binding
|
||||
if sc.requireChannelBinding {
|
||||
// Policy violation: server requires channel binding
|
||||
// Use ErrServerDoesSupportChannelBinding (defined for downgrade attacks)
|
||||
// as the best available match to signal that server requires channel binding
|
||||
return ErrServerDoesSupportChannelBinding,
|
||||
errors.New("server requires channel binding but client doesn't support it")
|
||||
}
|
||||
// OK: server either doesn't advertise PLUS or advertises it optionally
|
||||
return "", nil
|
||||
|
||||
case "y":
|
||||
// Client supports channel binding but thinks server doesn't advertise PLUS
|
||||
if advertised {
|
||||
// Downgrade attack: we advertised PLUS but client didn't see it
|
||||
return ErrServerDoesSupportChannelBinding,
|
||||
errors.New("downgrade attack detected: client used 'y' but server advertised PLUS")
|
||||
}
|
||||
// OK: we didn't advertise PLUS, client correctly detected this
|
||||
return "", nil
|
||||
|
||||
case "p":
|
||||
// Client requires channel binding with specific type
|
||||
if !advertised {
|
||||
// Server doesn't support channel binding
|
||||
return ErrChannelBindingNotSupported,
|
||||
errors.New("client requires channel binding but server doesn't support it")
|
||||
}
|
||||
if ChannelBindingType(sc.clientCBType) != sc.channelBinding.Type {
|
||||
// Server supports channel binding but not the requested type
|
||||
return ErrUnsupportedChannelBindingType,
|
||||
fmt.Errorf("client requested %s but server only supports %s",
|
||||
sc.clientCBType, sc.channelBinding.Type)
|
||||
}
|
||||
// OK: channel binding type matches
|
||||
return "", nil
|
||||
|
||||
default:
|
||||
// Invalid flag (should have been caught by parser)
|
||||
return ErrOtherError,
|
||||
fmt.Errorf("invalid channel binding flag: %s", sc.clientCBFlag)
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *ServerConversation) firstMsg(c1 string) (string, error) {
|
||||
msg, err := parseClientFirst(c1)
|
||||
if err != nil {
|
||||
sc.state = serverDone
|
||||
return "", err
|
||||
}
|
||||
|
||||
sc.gs2Header = msg.gs2Header
|
||||
sc.clientCBFlag = msg.gs2BindFlag
|
||||
sc.clientCBType = msg.channelBinding
|
||||
sc.username = msg.username
|
||||
sc.authzID = msg.authzID
|
||||
|
||||
// Validate channel binding flag against server configuration
|
||||
if serverErr, err := sc.validateChannelBindingFlag(); err != nil {
|
||||
sc.state = serverDone
|
||||
return serverErr, err
|
||||
}
|
||||
|
||||
sc.credential, err = sc.credentialCB(msg.username)
|
||||
if err != nil {
|
||||
sc.state = serverDone
|
||||
return ErrUnknownUser, err
|
||||
}
|
||||
|
||||
sc.nonce = msg.nonce + sc.nonceGen()
|
||||
sc.c1b = msg.c1b
|
||||
sc.s1 = fmt.Sprintf("r=%s,s=%s,i=%d",
|
||||
sc.nonce,
|
||||
base64.StdEncoding.EncodeToString([]byte(sc.credential.Salt)),
|
||||
sc.credential.Iters,
|
||||
)
|
||||
|
||||
return sc.s1, nil
|
||||
}
|
||||
|
||||
// For errors, returns server error message as well as non-nil error. Callers
|
||||
// can choose whether to send server error or not.
|
||||
func (sc *ServerConversation) finalMsg(c2 string) (string, error) {
|
||||
msg, err := parseClientFinal(c2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Check channel binding data matches what we expect
|
||||
var expectedCBind []byte
|
||||
if sc.clientCBFlag == "p" {
|
||||
// Client used channel binding - expect gs2 header + channel binding data
|
||||
expectedCBind = append([]byte(sc.gs2Header), sc.channelBinding.Data...)
|
||||
} else {
|
||||
// Client didn't use channel binding - just expect gs2 header
|
||||
expectedCBind = []byte(sc.gs2Header)
|
||||
}
|
||||
|
||||
if !hmac.Equal(msg.cbind, expectedCBind) {
|
||||
return ErrChannelBindingsDontMatch,
|
||||
fmt.Errorf("channel binding mismatch: expected %x, got %x",
|
||||
expectedCBind, msg.cbind)
|
||||
}
|
||||
|
||||
// Check nonce received matches what we sent
|
||||
if msg.nonce != sc.nonce {
|
||||
return ErrOtherError, errors.New("nonce received did not match nonce sent")
|
||||
}
|
||||
|
||||
// Create auth message
|
||||
authMsg := sc.c1b + "," + sc.s1 + "," + msg.c2wop
|
||||
|
||||
// Retrieve ClientKey from proof and verify it
|
||||
clientSignature := computeHMAC(sc.hashGen, sc.credential.StoredKey, []byte(authMsg))
|
||||
clientKey, err := xorBytes([]byte(msg.proof), clientSignature)
|
||||
if err != nil {
|
||||
return ErrOtherError, err
|
||||
}
|
||||
storedKey := computeHash(sc.hashGen, clientKey)
|
||||
|
||||
// Compare with constant-time function
|
||||
if !hmac.Equal(storedKey, sc.credential.StoredKey) {
|
||||
return ErrInvalidProof, errors.New("challenge proof invalid")
|
||||
}
|
||||
|
||||
sc.valid = true
|
||||
|
||||
// Compute and return server verifier
|
||||
serverSignature := computeHMAC(sc.hashGen, sc.credential.ServerKey, []byte(authMsg))
|
||||
return "v=" + base64.StdEncoding.EncodeToString(serverSignature), nil
|
||||
}
|
||||
0
server/vendor/github.com/xdg-go/stringprep/.gitignore
generated
vendored
Normal file
0
server/vendor/github.com/xdg-go/stringprep/.gitignore
generated
vendored
Normal file
36
server/vendor/github.com/xdg-go/stringprep/CHANGELOG.md
generated
vendored
Normal file
36
server/vendor/github.com/xdg-go/stringprep/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# CHANGELOG
|
||||
|
||||
<a name="v1.0.4"></a>
|
||||
## [v1.0.4] - 2022-12-07
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Bump golang.org/x/text to v0.3.8 due to CVE-2022-32149
|
||||
|
||||
<a name="v1.0.3"></a>
|
||||
## [v1.0.3] - 2022-03-01
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Bump golang.org/x/text to v0.3.7 due to CVE-2021-38561
|
||||
|
||||
<a name="v1.0.2"></a>
|
||||
## [v1.0.2] - 2021-03-27
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Change minimum Go version to 1.11
|
||||
|
||||
<a name="v1.0.1"></a>
|
||||
## [v1.0.1] - 2021-03-24
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Add go.mod file
|
||||
|
||||
<a name="v1.0.0"></a>
|
||||
## [v1.0.0] - 2018-02-21
|
||||
|
||||
[v1.0.2]: https://github.com/xdg-go/stringprep/releases/tag/v1.0.2
|
||||
[v1.0.1]: https://github.com/xdg-go/stringprep/releases/tag/v1.0.1
|
||||
[v1.0.0]: https://github.com/xdg-go/stringprep/releases/tag/v1.0.0
|
||||
175
server/vendor/github.com/xdg-go/stringprep/LICENSE
generated
vendored
Normal file
175
server/vendor/github.com/xdg-go/stringprep/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
28
server/vendor/github.com/xdg-go/stringprep/README.md
generated
vendored
Normal file
28
server/vendor/github.com/xdg-go/stringprep/README.md
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
[](https://pkg.go.dev/github.com/xdg-go/stringprep)
|
||||
[](https://goreportcard.com/report/github.com/xdg-go/stringprep)
|
||||
[](https://github.com/xdg-go/stringprep/actions/workflows/test.yml)
|
||||
|
||||
# stringprep – Go implementation of RFC-3454 stringprep and RFC-4013 SASLprep
|
||||
|
||||
## Synopsis
|
||||
|
||||
```
|
||||
import "github.com/xdg-go/stringprep"
|
||||
|
||||
prepped := stringprep.SASLprep.Prepare("TrustNô1")
|
||||
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
This library provides an implementation of the stringprep algorithm
|
||||
(RFC-3454) in Go, including all data tables.
|
||||
|
||||
A pre-built SASLprep (RFC-4013) profile is provided as well.
|
||||
|
||||
## Copyright and License
|
||||
|
||||
Copyright 2018 by David A. Golden. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"). You may
|
||||
obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
73
server/vendor/github.com/xdg-go/stringprep/bidi.go
generated
vendored
Normal file
73
server/vendor/github.com/xdg-go/stringprep/bidi.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package stringprep
|
||||
|
||||
var errHasLCat = "BiDi string can't have runes from category L"
|
||||
var errFirstRune = "BiDi string first rune must have category R or AL"
|
||||
var errLastRune = "BiDi string last rune must have category R or AL"
|
||||
|
||||
// Check for prohibited characters from table C.8
|
||||
func checkBiDiProhibitedRune(s string) error {
|
||||
for _, r := range s {
|
||||
if TableC8.Contains(r) {
|
||||
return Error{Msg: errProhibited, Rune: r}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for LCat characters from table D.2
|
||||
func checkBiDiLCat(s string) error {
|
||||
for _, r := range s {
|
||||
if TableD2.Contains(r) {
|
||||
return Error{Msg: errHasLCat, Rune: r}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check first and last characters are in table D.1; requires non-empty string
|
||||
func checkBadFirstAndLastRandALCat(s string) error {
|
||||
rs := []rune(s)
|
||||
if !TableD1.Contains(rs[0]) {
|
||||
return Error{Msg: errFirstRune, Rune: rs[0]}
|
||||
}
|
||||
n := len(rs) - 1
|
||||
if !TableD1.Contains(rs[n]) {
|
||||
return Error{Msg: errLastRune, Rune: rs[n]}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Look for RandALCat characters from table D.1
|
||||
func hasBiDiRandALCat(s string) bool {
|
||||
for _, r := range s {
|
||||
if TableD1.Contains(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Check that BiDi rules are satisfied ; let empty string pass this rule
|
||||
func passesBiDiRules(s string) error {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := checkBiDiProhibitedRune(s); err != nil {
|
||||
return err
|
||||
}
|
||||
if hasBiDiRandALCat(s) {
|
||||
if err := checkBiDiLCat(s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBadFirstAndLastRandALCat(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
10
server/vendor/github.com/xdg-go/stringprep/doc.go
generated
vendored
Normal file
10
server/vendor/github.com/xdg-go/stringprep/doc.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
// Package stringprep provides data tables and algorithms for RFC-3454,
|
||||
// including errata (as of 2018-02). It also provides a profile for
|
||||
// SASLprep as defined in RFC-4013.
|
||||
package stringprep
|
||||
14
server/vendor/github.com/xdg-go/stringprep/error.go
generated
vendored
Normal file
14
server/vendor/github.com/xdg-go/stringprep/error.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package stringprep
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error describes problems encountered during stringprep, including what rune
|
||||
// was problematic.
|
||||
type Error struct {
|
||||
Msg string
|
||||
Rune rune
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("%s (rune: '\\u%04x')", e.Msg, e.Rune)
|
||||
}
|
||||
21
server/vendor/github.com/xdg-go/stringprep/map.go
generated
vendored
Normal file
21
server/vendor/github.com/xdg-go/stringprep/map.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package stringprep
|
||||
|
||||
// Mapping represents a stringprep mapping, from a single rune to zero or more
|
||||
// runes.
|
||||
type Mapping map[rune][]rune
|
||||
|
||||
// Map maps a rune to a (possibly empty) rune slice via a stringprep Mapping.
|
||||
// The ok return value is false if the rune was not found.
|
||||
func (m Mapping) Map(r rune) (replacement []rune, ok bool) {
|
||||
rs, ok := m[r]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return rs, true
|
||||
}
|
||||
75
server/vendor/github.com/xdg-go/stringprep/profile.go
generated
vendored
Normal file
75
server/vendor/github.com/xdg-go/stringprep/profile.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package stringprep
|
||||
|
||||
import (
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// Profile represents a stringprep profile.
|
||||
type Profile struct {
|
||||
Mappings []Mapping
|
||||
Normalize bool
|
||||
Prohibits []Set
|
||||
CheckBiDi bool
|
||||
}
|
||||
|
||||
var errProhibited = "prohibited character"
|
||||
|
||||
// Prepare transforms an input string to an output string following
|
||||
// the rules defined in the profile as defined by RFC-3454.
|
||||
func (p Profile) Prepare(s string) (string, error) {
|
||||
// Optimistically, assume output will be same length as input
|
||||
temp := make([]rune, 0, len(s))
|
||||
|
||||
// Apply maps
|
||||
for _, r := range s {
|
||||
rs, ok := p.applyMaps(r)
|
||||
if ok {
|
||||
temp = append(temp, rs...)
|
||||
} else {
|
||||
temp = append(temp, r)
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize
|
||||
var out string
|
||||
if p.Normalize {
|
||||
out = norm.NFKC.String(string(temp))
|
||||
} else {
|
||||
out = string(temp)
|
||||
}
|
||||
|
||||
// Check prohibited
|
||||
for _, r := range out {
|
||||
if p.runeIsProhibited(r) {
|
||||
return "", Error{Msg: errProhibited, Rune: r}
|
||||
}
|
||||
}
|
||||
|
||||
// Check BiDi allowed
|
||||
if p.CheckBiDi {
|
||||
if err := passesBiDiRules(out); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p Profile) applyMaps(r rune) ([]rune, bool) {
|
||||
for _, m := range p.Mappings {
|
||||
rs, ok := m.Map(r)
|
||||
if ok {
|
||||
return rs, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p Profile) runeIsProhibited(r rune) bool {
|
||||
for _, s := range p.Prohibits {
|
||||
if s.Contains(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
52
server/vendor/github.com/xdg-go/stringprep/saslprep.go
generated
vendored
Normal file
52
server/vendor/github.com/xdg-go/stringprep/saslprep.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package stringprep
|
||||
|
||||
var mapNonASCIISpaceToASCIISpace = Mapping{
|
||||
0x00A0: []rune{0x0020},
|
||||
0x1680: []rune{0x0020},
|
||||
0x2000: []rune{0x0020},
|
||||
0x2001: []rune{0x0020},
|
||||
0x2002: []rune{0x0020},
|
||||
0x2003: []rune{0x0020},
|
||||
0x2004: []rune{0x0020},
|
||||
0x2005: []rune{0x0020},
|
||||
0x2006: []rune{0x0020},
|
||||
0x2007: []rune{0x0020},
|
||||
0x2008: []rune{0x0020},
|
||||
0x2009: []rune{0x0020},
|
||||
0x200A: []rune{0x0020},
|
||||
0x200B: []rune{0x0020},
|
||||
0x202F: []rune{0x0020},
|
||||
0x205F: []rune{0x0020},
|
||||
0x3000: []rune{0x0020},
|
||||
}
|
||||
|
||||
// SASLprep is a pre-defined stringprep profile for user names and passwords
|
||||
// as described in RFC-4013.
|
||||
//
|
||||
// Because the stringprep distinction between query and stored strings was
|
||||
// intended for compatibility across profile versions, but SASLprep was never
|
||||
// updated and is now deprecated, this profile only operates in stored
|
||||
// strings mode, prohibiting unassigned code points.
|
||||
var SASLprep Profile = saslprep
|
||||
|
||||
var saslprep = Profile{
|
||||
Mappings: []Mapping{
|
||||
TableB1,
|
||||
mapNonASCIISpaceToASCIISpace,
|
||||
},
|
||||
Normalize: true,
|
||||
Prohibits: []Set{
|
||||
TableA1,
|
||||
TableC1_2,
|
||||
TableC2_1,
|
||||
TableC2_2,
|
||||
TableC3,
|
||||
TableC4,
|
||||
TableC5,
|
||||
TableC6,
|
||||
TableC7,
|
||||
TableC8,
|
||||
TableC9,
|
||||
},
|
||||
CheckBiDi: true,
|
||||
}
|
||||
36
server/vendor/github.com/xdg-go/stringprep/set.go
generated
vendored
Normal file
36
server/vendor/github.com/xdg-go/stringprep/set.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2018 by David A. Golden. All rights reserved.
|
||||
//
|
||||
// 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
|
||||
|
||||
package stringprep
|
||||
|
||||
import "sort"
|
||||
|
||||
// RuneRange represents a close-ended range of runes: [N,M]. For a range
|
||||
// consisting of a single rune, N and M will be equal.
|
||||
type RuneRange [2]rune
|
||||
|
||||
// Contains returns true if a rune is within the bounds of the RuneRange.
|
||||
func (rr RuneRange) Contains(r rune) bool {
|
||||
return rr[0] <= r && r <= rr[1]
|
||||
}
|
||||
|
||||
func (rr RuneRange) isAbove(r rune) bool {
|
||||
return r <= rr[0]
|
||||
}
|
||||
|
||||
// Set represents a stringprep data table used to identify runes of a
|
||||
// particular type.
|
||||
type Set []RuneRange
|
||||
|
||||
// Contains returns true if a rune is within any of the RuneRanges in the
|
||||
// Set.
|
||||
func (s Set) Contains(r rune) bool {
|
||||
i := sort.Search(len(s), func(i int) bool { return s[i].Contains(r) || s[i].isAbove(r) })
|
||||
if i < len(s) && s[i].Contains(r) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
3215
server/vendor/github.com/xdg-go/stringprep/tables.go
generated
vendored
Normal file
3215
server/vendor/github.com/xdg-go/stringprep/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user