1.修改代码适配阿里云的服务器

This commit is contained in:
whm
2026-03-17 14:27:32 +08:00
parent 826617d737
commit 20e7f3a65d
1777 changed files with 775041 additions and 10 deletions

12
server/vendor/github.com/xdg-go/pbkdf2/.gitignore generated vendored Normal file
View 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
View 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
View File

@@ -0,0 +1,17 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/xdg-go/pbkdf2.svg)](https://pkg.go.dev/github.com/xdg-go/pbkdf2)
[![Go Report Card](https://goreportcard.com/badge/github.com/xdg-go/pbkdf2)](https://goreportcard.com/report/github.com/xdg-go/pbkdf2)
[![Github Actions](https://github.com/xdg-go/pbkdf2/actions/workflows/test.yml/badge.svg)](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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,114 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/xdg-go/scram.svg)](https://pkg.go.dev/github.com/xdg-go/scram)
[![Go Report Card](https://goreportcard.com/badge/github.com/xdg-go/scram)](https://goreportcard.com/report/github.com/xdg-go/scram)
[![Github Actions](https://github.com/xdg-go/scram/actions/workflows/test.yml/badge.svg)](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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View File

View 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
View 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
View File

@@ -0,0 +1,28 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/xdg-go/stringprep.svg)](https://pkg.go.dev/github.com/xdg-go/stringprep)
[![Go Report Card](https://goreportcard.com/badge/github.com/xdg-go/stringprep)](https://goreportcard.com/report/github.com/xdg-go/stringprep)
[![Github Actions](https://github.com/xdg-go/stringprep/actions/workflows/test.yml/badge.svg)](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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff