fix(go.sum): update ResolveSpec dependency to v1.0.87
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
*.sw[op]
|
||||
*.svg
|
||||
*.xml
|
||||
*.out
|
||||
Gopkg.lock
|
||||
vendor/
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
|
||||
## v0.3.1 — 2022-12-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- Sometimes the nonce was not set on the SASL state machine, resulting in
|
||||
authentication failing
|
||||
|
||||
|
||||
## v0.3.0 — 2022-08-15
|
||||
|
||||
### Added
|
||||
|
||||
- Support for tls-exporter channel binding method as defined in [RFC 9266]
|
||||
- Support for fast XOR using SIMD/VSX on more architectures
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Return an error if no tls-unique channel binding (CB) data is present in the
|
||||
TLS connection state (or no connection state exists) and we use SCRAM with CB
|
||||
|
||||
|
||||
[RFC 9266]: https://datatracker.ietf.org/doc/html/rfc9266
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
1 Letterman Drive
|
||||
Suite D4700
|
||||
San Francisco, CA, 94129
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
Copyright © 2014 The Mellium Contributors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# SASL
|
||||
|
||||
[![Issue Tracker][badge]](https://mellium.im/issue)
|
||||
[](https://pkg.go.dev/mellium.im/sasl)
|
||||
[](https://mellium.chat)
|
||||
[](https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
<a href="https://opencollective.com/mellium" alt="Donate on Open Collective"><img src="https://opencollective.com/mellium/donate/button@2x.png?color=blue" width="200"/></a>
|
||||
|
||||
A Go library implementing the Simple Authentication and Security Layer (SASL) as
|
||||
defined by [RFC 4422][rfc4422].
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The package may be used under the terms of the BSD 2-Clause License a copy of
|
||||
which may be found in the file [LICENSE.md][LICENSE].
|
||||
|
||||
[badge]: https://img.shields.io/badge/style-mellium%2fxmpp-green.svg?longCache=true&style=popout-square&label=issues
|
||||
[rfc4422]: https://tools.ietf.org/html/rfc4422
|
||||
[LICENSE]: https://codeberg.org/mellium/xmpp/src/branch/main/LICENSE
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package sasl implements the Simple Authentication and Security Layer (SASL)
|
||||
// as defined by RFC 4422.
|
||||
//
|
||||
// Most users of this package will only need to create a Negotiator using
|
||||
// NewClient or NewServer and call its Step method repeatedly.
|
||||
// Authors implementing SASL mechanisms other than the builtin ones will want to
|
||||
// create a Mechanism struct which will likely use the other methods on the
|
||||
// Negotiator.
|
||||
//
|
||||
// Be advised: This API is still unstable and is subject to change.
|
||||
package sasl // import "mellium.im/sasl"
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
/* #nosec */
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Define common errors used by SASL mechanisms and negotiators.
|
||||
var (
|
||||
ErrInvalidState = errors.New("invalid state")
|
||||
ErrInvalidChallenge = errors.New("invalid or missing challenge")
|
||||
ErrAuthn = errors.New("authentication error")
|
||||
ErrTooManySteps = errors.New("step called too many times")
|
||||
)
|
||||
|
||||
var (
|
||||
// Plain is a Mechanism that implements the PLAIN authentication mechanism
|
||||
// as defined by RFC 4616.
|
||||
Plain Mechanism = plain
|
||||
|
||||
// ScramSha256Plus is a Mechanism that implements the SCRAM-SHA-256-PLUS
|
||||
// authentication mechanism defined in RFC 7677.
|
||||
// The only supported channel binding types are tls-unique as defined in RFC
|
||||
// 5929 and tls-exporter defined in RFC 9266.
|
||||
ScramSha256Plus Mechanism = scram("SCRAM-SHA-256-PLUS", sha256.New)
|
||||
|
||||
// ScramSha256 is a Mechanism that implements the SCRAM-SHA-256
|
||||
// authentication mechanism defined in RFC 7677.
|
||||
ScramSha256 Mechanism = scram("SCRAM-SHA-256", sha256.New)
|
||||
|
||||
// ScramSha1Plus is a Mechanism that implements the SCRAM-SHA-1-PLUS
|
||||
// authentication mechanism defined in RFC 5802.
|
||||
// The only supported channel binding types are tls-unique as defined in RFC
|
||||
// 5929 and tls-exporter defined in RFC 9266.
|
||||
ScramSha1Plus Mechanism = scram("SCRAM-SHA-1-PLUS", sha1.New)
|
||||
|
||||
// ScramSha1 is a Mechanism that implements the SCRAM-SHA-1 authentication
|
||||
// mechanism defined in RFC 5802.
|
||||
ScramSha1 Mechanism = scram("SCRAM-SHA-1", sha1.New)
|
||||
)
|
||||
|
||||
// Mechanism represents a SASL mechanism that can be used by a Client or Server
|
||||
// to perform the actual negotiation. Base64 encoding the final challenges and
|
||||
// responses should not be performed by the mechanism.
|
||||
//
|
||||
// Mechanisms must be stateless and may be shared between goroutines. When a
|
||||
// mechanism needs to store state between the different steps it can return
|
||||
// anything that it needs to store and the value will be cached by the
|
||||
// negotiator and passed in as the data parameter when the next challenge is
|
||||
// received.
|
||||
type Mechanism struct {
|
||||
Name string
|
||||
Start func(n *Negotiator) (more bool, resp []byte, cache interface{}, err error)
|
||||
Next func(n *Negotiator, challenge []byte, data interface{}) (more bool, resp []byte, cache interface{}, err error)
|
||||
}
|
||||
Vendored
+196
@@ -0,0 +1,196 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// State represents the current state of a Negotiator.
|
||||
// The first two bits represent the actual state of the state machine and the
|
||||
// last 3 bits are a bitmask that define the machines behavior.
|
||||
// The remaining bits should not be used.
|
||||
type State uint8
|
||||
|
||||
// The current step of the Server or Client (represented by the first two bits
|
||||
// of the state byte).
|
||||
const (
|
||||
Initial State = iota
|
||||
AuthTextSent
|
||||
ResponseSent
|
||||
ValidServerResponse
|
||||
|
||||
// Bitmask used for extracting the step from the state byte.
|
||||
StepMask = 0x3
|
||||
)
|
||||
|
||||
const (
|
||||
// RemoteCB bit is on if the remote client or server supports channel binding.
|
||||
RemoteCB State = 1 << (iota + 3)
|
||||
|
||||
// Errored bit is on if the machine has errored.
|
||||
Errored
|
||||
|
||||
// Receiving bit is on if the machine is a server.
|
||||
Receiving
|
||||
)
|
||||
|
||||
// NewClient creates a new SASL Negotiator that supports creating authentication
|
||||
// requests using the given mechanism.
|
||||
func NewClient(m Mechanism, opts ...Option) *Negotiator {
|
||||
machine := &Negotiator{
|
||||
mechanism: m,
|
||||
}
|
||||
getOpts(machine, opts...)
|
||||
for _, rname := range machine.remoteMechanisms {
|
||||
lname := m.Name
|
||||
if lname == rname && strings.HasSuffix(lname, "-PLUS") {
|
||||
machine.state |= RemoteCB
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(machine.nonce) == 0 {
|
||||
machine.nonce = nonce(noncerandlen, rand.Reader)
|
||||
}
|
||||
return machine
|
||||
}
|
||||
|
||||
// NewServer creates a new SASL Negotiator that supports receiving
|
||||
// authentication requests using the given mechanism.
|
||||
// A nil permissions function is the same as a function that always returns
|
||||
// false.
|
||||
func NewServer(m Mechanism, permissions func(*Negotiator) bool, opts ...Option) *Negotiator {
|
||||
machine := &Negotiator{
|
||||
mechanism: m,
|
||||
state: AuthTextSent | Receiving,
|
||||
}
|
||||
getOpts(machine, opts...)
|
||||
if permissions != nil {
|
||||
machine.permissions = permissions
|
||||
}
|
||||
for _, rname := range machine.remoteMechanisms {
|
||||
lname := m.Name
|
||||
if lname == rname && strings.HasSuffix(lname, "-PLUS") {
|
||||
machine.state |= RemoteCB
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(machine.nonce) == 0 {
|
||||
machine.nonce = nonce(noncerandlen, rand.Reader)
|
||||
}
|
||||
return machine
|
||||
}
|
||||
|
||||
// A Negotiator represents a SASL client or server state machine that can
|
||||
// attempt to negotiate auth. Negotiators should not be used from multiple
|
||||
// goroutines, and must be reset between negotiation attempts.
|
||||
type Negotiator struct {
|
||||
tlsState *tls.ConnectionState
|
||||
remoteMechanisms []string
|
||||
credentials func() (Username, Password, Identity []byte)
|
||||
permissions func(*Negotiator) bool
|
||||
mechanism Mechanism
|
||||
state State
|
||||
nonce []byte
|
||||
cache interface{}
|
||||
}
|
||||
|
||||
// Nonce returns a unique nonce that is reset for each negotiation attempt. It
|
||||
// is used by SASL Mechanisms and should generally not be called directly.
|
||||
func (c *Negotiator) Nonce() []byte {
|
||||
return c.nonce
|
||||
}
|
||||
|
||||
// Step attempts to transition the state machine to its next state. If Step is
|
||||
// called after a previous invocation generates an error (and the state machine
|
||||
// has not been reset to its initial state), Step panics.
|
||||
func (c *Negotiator) Step(challenge []byte) (more bool, resp []byte, err error) {
|
||||
if c.state&Errored == Errored {
|
||||
panic("sasl: Step called on a SASL state machine that has errored")
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
c.state |= Errored
|
||||
}
|
||||
}()
|
||||
|
||||
switch c.state & StepMask {
|
||||
case Initial:
|
||||
more, resp, c.cache, err = c.mechanism.Start(c)
|
||||
c.state = c.state&^StepMask | AuthTextSent
|
||||
case AuthTextSent:
|
||||
more, resp, c.cache, err = c.mechanism.Next(c, challenge, c.cache)
|
||||
c.state = c.state&^StepMask | ResponseSent
|
||||
case ResponseSent:
|
||||
more, resp, c.cache, err = c.mechanism.Next(c, challenge, c.cache)
|
||||
c.state = c.state&^StepMask | ValidServerResponse
|
||||
case ValidServerResponse:
|
||||
more, resp, c.cache, err = c.mechanism.Next(c, challenge, c.cache)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
return more, resp, err
|
||||
}
|
||||
|
||||
// State returns the internal state of the SASL state machine.
|
||||
func (c *Negotiator) State() State {
|
||||
return c.state
|
||||
}
|
||||
|
||||
// Reset resets the state machine to its initial state so that it can be reused
|
||||
// in another SASL exchange.
|
||||
func (c *Negotiator) Reset() {
|
||||
c.state = c.state & (Receiving | RemoteCB)
|
||||
|
||||
// Skip the start step for servers
|
||||
if c.state&Receiving == Receiving {
|
||||
c.state = c.state&^StepMask | AuthTextSent
|
||||
}
|
||||
|
||||
c.nonce = nonce(noncerandlen, rand.Reader)
|
||||
c.cache = nil
|
||||
}
|
||||
|
||||
// Credentials returns a username, and password for authentication and optional
|
||||
// identity for authorization.
|
||||
func (c *Negotiator) Credentials() (username, password, identity []byte) {
|
||||
if c.credentials != nil {
|
||||
return c.credentials()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Permissions is the callback used by the server to authenticate the user.
|
||||
func (c *Negotiator) Permissions(opts ...Option) bool {
|
||||
if c.permissions != nil {
|
||||
nn := *c
|
||||
getOpts(&nn, opts...)
|
||||
return c.permissions(&nn)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TLSState is the state of any TLS connections being used to negotiate SASL
|
||||
// (it can be used for channel binding).
|
||||
func (c *Negotiator) TLSState() *tls.ConnectionState {
|
||||
if c.tlsState != nil {
|
||||
return c.tlsState
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoteMechanisms is a list of mechanisms as advertised by the other side of a
|
||||
// SASL negotiation.
|
||||
func (c *Negotiator) RemoteMechanisms() []string {
|
||||
if c.remoteMechanisms != nil {
|
||||
return c.remoteMechanisms
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Generates a nonce with n random bytes base64 encoded to ensure that it meets
|
||||
// the criteria for inclusion in a SCRAM message.
|
||||
func nonce(n int, r io.Reader) []byte {
|
||||
if n < 1 {
|
||||
panic("Cannot generate zero or negative length nonce")
|
||||
}
|
||||
b := make([]byte, n)
|
||||
n2, err := r.Read(b)
|
||||
switch {
|
||||
case err != nil:
|
||||
panic(err)
|
||||
case n2 != n:
|
||||
panic("Could not read enough randomness to generate nonce")
|
||||
}
|
||||
val := make([]byte, base64.RawStdEncoding.EncodedLen(n))
|
||||
base64.RawStdEncoding.Encode(val, b)
|
||||
|
||||
return val
|
||||
}
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
// An Option represents an input to a SASL state machine.
|
||||
type Option func(*Negotiator)
|
||||
|
||||
func getOpts(n *Negotiator, o ...Option) {
|
||||
n.credentials = func() (username, password, identity []byte) {
|
||||
return
|
||||
}
|
||||
n.permissions = func(_ *Negotiator) bool {
|
||||
return false
|
||||
}
|
||||
for _, f := range o {
|
||||
f(n)
|
||||
}
|
||||
}
|
||||
|
||||
// TLSState lets the state machine negotiate channel binding with a TLS session
|
||||
// if supported by the underlying mechanism.
|
||||
func TLSState(cs tls.ConnectionState) Option {
|
||||
return func(n *Negotiator) {
|
||||
n.tlsState = &cs
|
||||
}
|
||||
}
|
||||
|
||||
// nonce overrides the nonce used for authentication attempts.
|
||||
// This defaults to a random value and should not be changed.
|
||||
func setNonce(v []byte) Option {
|
||||
return func(n *Negotiator) {
|
||||
n.nonce = v
|
||||
}
|
||||
}
|
||||
|
||||
// RemoteMechanisms sets a list of mechanisms supported by the remote client or
|
||||
// server with which the state machine will be negotiating.
|
||||
// It is used to determine if the server supports channel binding.
|
||||
func RemoteMechanisms(m ...string) Option {
|
||||
return func(n *Negotiator) {
|
||||
n.remoteMechanisms = m
|
||||
}
|
||||
}
|
||||
|
||||
// Credentials provides the negotiator with a username and password to
|
||||
// authenticate with and (optionally) an authorization identity.
|
||||
// Identity will normally be left empty to act as the username.
|
||||
// The Credentials function is called lazily and may be called multiple times by
|
||||
// the mechanism.
|
||||
// It is not memoized by the negotiator.
|
||||
func Credentials(f func() (Username, Password, Identity []byte)) Option {
|
||||
return func(n *Negotiator) {
|
||||
n.credentials = f
|
||||
}
|
||||
}
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
var plainSep = []byte{0}
|
||||
|
||||
var plain = Mechanism{
|
||||
Name: "PLAIN",
|
||||
Start: func(m *Negotiator) (more bool, resp []byte, _ interface{}, err error) {
|
||||
username, password, identity := m.credentials()
|
||||
payload := make([]byte, 0, len(identity)+len(username)+len(password)+2)
|
||||
payload = append(payload, identity...)
|
||||
payload = append(payload, '\x00')
|
||||
payload = append(payload, username...)
|
||||
payload = append(payload, '\x00')
|
||||
payload = append(payload, password...)
|
||||
return false, payload, nil, nil
|
||||
},
|
||||
Next: func(m *Negotiator, challenge []byte, _ interface{}) (more bool, resp []byte, _ interface{}, err error) {
|
||||
// If we're a client, or we're a server that's past the AuthTextSent step,
|
||||
// we should never actually hit this step.
|
||||
if m.State()&Receiving != Receiving || m.State()&StepMask != AuthTextSent {
|
||||
err = ErrTooManySteps
|
||||
return
|
||||
}
|
||||
|
||||
// If we're a server, validate that the challenge looks like:
|
||||
// "Identity\x00Username\x00Password"
|
||||
parts := bytes.Split(challenge, plainSep)
|
||||
if len(parts) != 3 {
|
||||
err = ErrInvalidChallenge
|
||||
return
|
||||
}
|
||||
|
||||
if m.Permissions(Credentials(func() (Username, Password, Identity []byte) {
|
||||
return parts[1], parts[2], parts[0]
|
||||
})) {
|
||||
// Everything checks out as far as we know and the server should continue
|
||||
// to authenticate the user.
|
||||
return
|
||||
}
|
||||
|
||||
err = ErrAuthn
|
||||
return
|
||||
},
|
||||
}
|
||||
Vendored
+286
@@ -0,0 +1,286 @@
|
||||
// Copyright 2016 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"hash"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
const (
|
||||
exporterLen = 32
|
||||
exporterLabel = "EXPORTER-Channel-Binding"
|
||||
gs2HeaderCBSupportUnique = "p=tls-unique,"
|
||||
gs2HeaderCBSupportExporter = "p=tls-exporter,"
|
||||
gs2HeaderNoServerCBSupport = "y,"
|
||||
gs2HeaderNoCBSupport = "n,"
|
||||
)
|
||||
|
||||
var (
|
||||
clientKeyInput = []byte("Client Key")
|
||||
serverKeyInput = []byte("Server Key")
|
||||
)
|
||||
|
||||
// The number of random bytes to generate for a nonce.
|
||||
const noncerandlen = 16
|
||||
|
||||
func getGS2Header(name string, n *Negotiator) (gs2Header []byte) {
|
||||
_, _, identity := n.Credentials()
|
||||
tlsState := n.TLSState()
|
||||
switch {
|
||||
case tlsState == nil || !strings.HasSuffix(name, "-PLUS"):
|
||||
// We do not support channel binding
|
||||
gs2Header = []byte(gs2HeaderNoCBSupport)
|
||||
case n.State()&RemoteCB == RemoteCB:
|
||||
// We support channel binding and the server does too
|
||||
if tlsState.Version >= tls.VersionTLS13 {
|
||||
gs2Header = []byte(gs2HeaderCBSupportExporter)
|
||||
} else {
|
||||
gs2Header = []byte(gs2HeaderCBSupportUnique)
|
||||
}
|
||||
case n.State()&RemoteCB != RemoteCB:
|
||||
// We support channel binding but the server does not
|
||||
gs2Header = []byte(gs2HeaderNoServerCBSupport)
|
||||
}
|
||||
if len(identity) > 0 {
|
||||
gs2Header = append(gs2Header, []byte(`a=`)...)
|
||||
gs2Header = append(gs2Header, identity...)
|
||||
}
|
||||
gs2Header = append(gs2Header, ',')
|
||||
return
|
||||
}
|
||||
|
||||
func scram(name string, fn func() hash.Hash) Mechanism {
|
||||
// BUG(ssw): We need a way to cache the SCRAM client and server key
|
||||
// calculations.
|
||||
return Mechanism{
|
||||
Name: name,
|
||||
Start: func(m *Negotiator) (bool, []byte, interface{}, error) {
|
||||
user, _, _ := m.Credentials()
|
||||
|
||||
// Escape "=" and ",". This is mostly the same as bytes.Replace but
|
||||
// faster because we can do both replacements in a single pass.
|
||||
n := bytes.Count(user, []byte{'='}) + bytes.Count(user, []byte{','})
|
||||
username := make([]byte, len(user)+(n*2))
|
||||
w := 0
|
||||
start := 0
|
||||
for i := 0; i < n; i++ {
|
||||
j := start
|
||||
j += bytes.IndexAny(user[start:], "=,")
|
||||
w += copy(username[w:], user[start:j])
|
||||
switch user[j] {
|
||||
case '=':
|
||||
w += copy(username[w:], "=3D")
|
||||
case ',':
|
||||
w += copy(username[w:], "=2C")
|
||||
}
|
||||
start = j + 1
|
||||
}
|
||||
copy(username[w:], user[start:])
|
||||
|
||||
clientFirstMessage := make([]byte, 5+len(m.Nonce())+len(username))
|
||||
copy(clientFirstMessage, "n=")
|
||||
copy(clientFirstMessage[2:], username)
|
||||
copy(clientFirstMessage[2+len(username):], ",r=")
|
||||
copy(clientFirstMessage[5+len(username):], m.Nonce())
|
||||
|
||||
return true, append(getGS2Header(name, m), clientFirstMessage...), clientFirstMessage, nil
|
||||
},
|
||||
Next: func(m *Negotiator, challenge []byte, data interface{}) (more bool, resp []byte, cache interface{}, err error) {
|
||||
if len(challenge) == 0 {
|
||||
return more, resp, cache, ErrInvalidChallenge
|
||||
}
|
||||
|
||||
if m.State()&Receiving == Receiving {
|
||||
panic("not yet implemented")
|
||||
}
|
||||
return scramClientNext(name, fn, m, challenge, data)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func scramClientNext(name string, fn func() hash.Hash, m *Negotiator, challenge []byte, data interface{}) (more bool, resp []byte, cache interface{}, err error) {
|
||||
_, password, _ := m.Credentials()
|
||||
state := m.State()
|
||||
|
||||
switch state & StepMask {
|
||||
case AuthTextSent:
|
||||
iter := -1
|
||||
var salt, nonce []byte
|
||||
remain := challenge
|
||||
for {
|
||||
var field []byte
|
||||
field, remain = nextParam(remain)
|
||||
if len(field) < 3 || (len(field) >= 2 && field[1] != '=') {
|
||||
continue
|
||||
}
|
||||
switch field[0] {
|
||||
case 'i':
|
||||
ival := string(bytes.TrimRight(field[2:], "\x00"))
|
||||
|
||||
if iter, err = strconv.Atoi(ival); err != nil {
|
||||
return
|
||||
}
|
||||
case 's':
|
||||
salt = make([]byte, base64.StdEncoding.DecodedLen(len(field)-2))
|
||||
var n int
|
||||
n, err = base64.StdEncoding.Decode(salt, field[2:])
|
||||
salt = salt[:n]
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case 'r':
|
||||
nonce = field[2:]
|
||||
case 'm':
|
||||
// RFC 5802:
|
||||
// m: This attribute is reserved for future extensibility. In this
|
||||
// version of SCRAM, its presence in a client or a server message
|
||||
// MUST cause authentication failure when the attribute is parsed by
|
||||
// the other end.
|
||||
err = errors.New("server sent reserved attribute `m'")
|
||||
return
|
||||
}
|
||||
if remain == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case iter < 0:
|
||||
err = errors.New("iteration count is invalid")
|
||||
return
|
||||
case nonce == nil || !bytes.HasPrefix(nonce, m.Nonce()):
|
||||
err = errors.New("server nonce does not match client nonce")
|
||||
return
|
||||
case salt == nil:
|
||||
err = errors.New("server sent empty salt")
|
||||
return
|
||||
}
|
||||
|
||||
gs2Header := getGS2Header(name, m)
|
||||
tlsState := m.TLSState()
|
||||
var channelBinding []byte
|
||||
switch plus := strings.HasSuffix(name, "-PLUS"); {
|
||||
case plus && tlsState == nil:
|
||||
err = errors.New("sasl: SCRAM with channel binding requires a TLS connection")
|
||||
return
|
||||
case bytes.Contains(gs2Header, []byte(gs2HeaderCBSupportExporter)):
|
||||
keying, err := tlsState.ExportKeyingMaterial(exporterLabel, nil, exporterLen)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
if len(keying) == 0 {
|
||||
err = errors.New("sasl: SCRAM with channel binding requires valid TLS keying material")
|
||||
return false, nil, nil, err
|
||||
}
|
||||
channelBinding = make([]byte, 2+base64.StdEncoding.EncodedLen(len(gs2Header)+len(keying)))
|
||||
channelBinding[0] = 'c'
|
||||
channelBinding[1] = '='
|
||||
base64.StdEncoding.Encode(channelBinding[2:], append(gs2Header, keying...))
|
||||
case bytes.Contains(gs2Header, []byte(gs2HeaderCBSupportUnique)):
|
||||
//lint:ignore SA1019 TLS unique must be supported by SCRAM
|
||||
if len(tlsState.TLSUnique) == 0 {
|
||||
err = errors.New("sasl: SCRAM with channel binding requires valid tls-unique data")
|
||||
return false, nil, nil, err
|
||||
}
|
||||
channelBinding = make(
|
||||
[]byte,
|
||||
//lint:ignore SA1019 TLS unique must be supported by SCRAM
|
||||
2+base64.StdEncoding.EncodedLen(len(gs2Header)+len(tlsState.TLSUnique)),
|
||||
)
|
||||
channelBinding[0] = 'c'
|
||||
channelBinding[1] = '='
|
||||
//lint:ignore SA1019 TLS unique must be supported by SCRAM
|
||||
base64.StdEncoding.Encode(channelBinding[2:], append(gs2Header, tlsState.TLSUnique...))
|
||||
default:
|
||||
channelBinding = make(
|
||||
[]byte,
|
||||
2+base64.StdEncoding.EncodedLen(len(gs2Header)),
|
||||
)
|
||||
channelBinding[0] = 'c'
|
||||
channelBinding[1] = '='
|
||||
base64.StdEncoding.Encode(channelBinding[2:], gs2Header)
|
||||
}
|
||||
clientFinalMessageWithoutProof := append(channelBinding, []byte(",r=")...)
|
||||
clientFinalMessageWithoutProof = append(clientFinalMessageWithoutProof, nonce...)
|
||||
|
||||
clientFirstMessage := data.([]byte)
|
||||
authMessage := append(clientFirstMessage, ',')
|
||||
authMessage = append(authMessage, challenge...)
|
||||
authMessage = append(authMessage, ',')
|
||||
authMessage = append(authMessage, clientFinalMessageWithoutProof...)
|
||||
|
||||
saltedPassword := pbkdf2.Key(password, salt, iter, fn().Size(), fn)
|
||||
|
||||
h := hmac.New(fn, saltedPassword)
|
||||
_, err = h.Write(serverKeyInput)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
serverKey := h.Sum(nil)
|
||||
h.Reset()
|
||||
|
||||
_, err = h.Write(clientKeyInput)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
clientKey := h.Sum(nil)
|
||||
|
||||
h = hmac.New(fn, serverKey)
|
||||
_, err = h.Write(authMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
serverSignature := h.Sum(nil)
|
||||
|
||||
h = fn()
|
||||
_, err = h.Write(clientKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
storedKey := h.Sum(nil)
|
||||
h = hmac.New(fn, storedKey)
|
||||
_, err = h.Write(authMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
clientSignature := h.Sum(nil)
|
||||
clientProof := make([]byte, len(clientKey))
|
||||
goXORBytes(clientProof, clientKey, clientSignature)
|
||||
|
||||
encodedClientProof := make([]byte, base64.StdEncoding.EncodedLen(len(clientProof)))
|
||||
base64.StdEncoding.Encode(encodedClientProof, clientProof)
|
||||
clientFinalMessage := append(clientFinalMessageWithoutProof, []byte(",p=")...)
|
||||
clientFinalMessage = append(clientFinalMessage, encodedClientProof...)
|
||||
|
||||
return true, clientFinalMessage, serverSignature, nil
|
||||
case ResponseSent:
|
||||
clientCalculatedServerFinalMessage := "v=" + base64.StdEncoding.EncodeToString(data.([]byte))
|
||||
if clientCalculatedServerFinalMessage != string(challenge) {
|
||||
err = ErrAuthn
|
||||
return
|
||||
}
|
||||
// Success!
|
||||
return false, nil, nil, nil
|
||||
}
|
||||
err = ErrInvalidState
|
||||
return
|
||||
}
|
||||
|
||||
func nextParam(params []byte) ([]byte, []byte) {
|
||||
idx := bytes.IndexByte(params, ',')
|
||||
if idx == -1 {
|
||||
return params, nil
|
||||
}
|
||||
return params[:idx], params[idx+1:]
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.20
|
||||
|
||||
package sasl
|
||||
|
||||
// TODO: remove all the specialized XOR code and use "crypto/subtle".XORBytes
|
||||
// when Go v1.21 comes out. For more information see:
|
||||
// https://mellium.im/issue/338
|
||||
|
||||
func goXORBytes(dst, x, y []byte) int {
|
||||
n := len(x)
|
||||
if len(y) < n {
|
||||
n = len(y)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
if n > len(dst) {
|
||||
panic("subtle.XORBytes: dst too short")
|
||||
}
|
||||
xorBytes(&dst[0], &x[0], &y[0], n) // arch-specific
|
||||
return n
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
package sasl
|
||||
|
||||
//go:noescape
|
||||
func xorBytes(dst, a, b *byte, n int)
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytes(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytes(SB), NOSPLIT, $0
|
||||
MOVQ dst+0(FP), BX
|
||||
MOVQ a+8(FP), SI
|
||||
MOVQ b+16(FP), CX
|
||||
MOVQ n+24(FP), DX
|
||||
TESTQ $15, DX // AND 15 & len, if not zero jump to not_aligned.
|
||||
JNZ not_aligned
|
||||
|
||||
aligned:
|
||||
MOVQ $0, AX // position in slices
|
||||
|
||||
loop16b:
|
||||
MOVOU (SI)(AX*1), X0 // XOR 16byte forwards.
|
||||
MOVOU (CX)(AX*1), X1
|
||||
PXOR X1, X0
|
||||
MOVOU X0, (BX)(AX*1)
|
||||
ADDQ $16, AX
|
||||
CMPQ DX, AX
|
||||
JNE loop16b
|
||||
RET
|
||||
|
||||
loop_1b:
|
||||
SUBQ $1, DX // XOR 1byte backwards.
|
||||
MOVB (SI)(DX*1), DI
|
||||
MOVB (CX)(DX*1), AX
|
||||
XORB AX, DI
|
||||
MOVB DI, (BX)(DX*1)
|
||||
TESTQ $7, DX // AND 7 & len, if not zero jump to loop_1b.
|
||||
JNZ loop_1b
|
||||
CMPQ DX, $0 // if len is 0, ret.
|
||||
JE ret
|
||||
TESTQ $15, DX // AND 15 & len, if zero jump to aligned.
|
||||
JZ aligned
|
||||
|
||||
not_aligned:
|
||||
TESTQ $7, DX // AND $7 & len, if not zero jump to loop_1b.
|
||||
JNE loop_1b
|
||||
SUBQ $8, DX // XOR 8bytes backwards.
|
||||
MOVQ (SI)(DX*1), DI
|
||||
MOVQ (CX)(DX*1), AX
|
||||
XORQ AX, DI
|
||||
MOVQ DI, (BX)(DX*1)
|
||||
CMPQ DX, $16 // if len is greater or equal 16 here, it must be aligned.
|
||||
JGE aligned
|
||||
|
||||
ret:
|
||||
RET
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
package sasl
|
||||
|
||||
//go:noescape
|
||||
func xorBytes(dst, a, b *byte, n int)
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytes(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVD dst+0(FP), R0
|
||||
MOVD a+8(FP), R1
|
||||
MOVD b+16(FP), R2
|
||||
MOVD n+24(FP), R3
|
||||
CMP $64, R3
|
||||
BLT tail
|
||||
loop_64:
|
||||
VLD1.P 64(R1), [V0.B16, V1.B16, V2.B16, V3.B16]
|
||||
VLD1.P 64(R2), [V4.B16, V5.B16, V6.B16, V7.B16]
|
||||
VEOR V0.B16, V4.B16, V4.B16
|
||||
VEOR V1.B16, V5.B16, V5.B16
|
||||
VEOR V2.B16, V6.B16, V6.B16
|
||||
VEOR V3.B16, V7.B16, V7.B16
|
||||
VST1.P [V4.B16, V5.B16, V6.B16, V7.B16], 64(R0)
|
||||
SUBS $64, R3
|
||||
CMP $64, R3
|
||||
BGE loop_64
|
||||
tail:
|
||||
// quick end
|
||||
CBZ R3, end
|
||||
TBZ $5, R3, less_than32
|
||||
VLD1.P 32(R1), [V0.B16, V1.B16]
|
||||
VLD1.P 32(R2), [V2.B16, V3.B16]
|
||||
VEOR V0.B16, V2.B16, V2.B16
|
||||
VEOR V1.B16, V3.B16, V3.B16
|
||||
VST1.P [V2.B16, V3.B16], 32(R0)
|
||||
less_than32:
|
||||
TBZ $4, R3, less_than16
|
||||
LDP.P 16(R1), (R11, R12)
|
||||
LDP.P 16(R2), (R13, R14)
|
||||
EOR R11, R13, R13
|
||||
EOR R12, R14, R14
|
||||
STP.P (R13, R14), 16(R0)
|
||||
less_than16:
|
||||
TBZ $3, R3, less_than8
|
||||
MOVD.P 8(R1), R11
|
||||
MOVD.P 8(R2), R12
|
||||
EOR R11, R12, R12
|
||||
MOVD.P R12, 8(R0)
|
||||
less_than8:
|
||||
TBZ $2, R3, less_than4
|
||||
MOVWU.P 4(R1), R13
|
||||
MOVWU.P 4(R2), R14
|
||||
EORW R13, R14, R14
|
||||
MOVWU.P R14, 4(R0)
|
||||
less_than4:
|
||||
TBZ $1, R3, less_than2
|
||||
MOVHU.P 2(R1), R15
|
||||
MOVHU.P 2(R2), R16
|
||||
EORW R15, R16, R16
|
||||
MOVHU.P R16, 2(R0)
|
||||
less_than2:
|
||||
TBZ $0, R3, end
|
||||
MOVBU (R1), R17
|
||||
MOVBU (R2), R19
|
||||
EORW R17, R19, R19
|
||||
MOVBU R19, (R0)
|
||||
end:
|
||||
RET
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !arm64 && !ppc64 && !ppc64le) || purego
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const wordSize = unsafe.Sizeof(uintptr(0))
|
||||
|
||||
const supportsUnaligned = runtime.GOARCH == "386" ||
|
||||
runtime.GOARCH == "amd64" ||
|
||||
runtime.GOARCH == "ppc64" ||
|
||||
runtime.GOARCH == "ppc64le" ||
|
||||
runtime.GOARCH == "s390x"
|
||||
|
||||
func xorBytes(dstb, xb, yb *byte, n int) {
|
||||
// xorBytes assembly is written using pointers and n. Back to slices.
|
||||
dst := unsafe.Slice(dstb, n)
|
||||
x := unsafe.Slice(xb, n)
|
||||
y := unsafe.Slice(yb, n)
|
||||
|
||||
if supportsUnaligned || aligned(dstb, xb, yb) {
|
||||
xorLoop(words(dst), words(x), words(y))
|
||||
if uintptr(n)%wordSize == 0 {
|
||||
return
|
||||
}
|
||||
done := n &^ int(wordSize-1)
|
||||
dst = dst[done:]
|
||||
x = x[done:]
|
||||
y = y[done:]
|
||||
}
|
||||
xorLoop(dst, x, y)
|
||||
}
|
||||
|
||||
// aligned reports whether dst, x, and y are all word-aligned pointers.
|
||||
func aligned(dst, x, y *byte) bool {
|
||||
return (uintptr(unsafe.Pointer(dst))|uintptr(unsafe.Pointer(x))|uintptr(unsafe.Pointer(y)))&(wordSize-1) == 0
|
||||
}
|
||||
|
||||
// words returns a []uintptr pointing at the same data as x,
|
||||
// with any trailing partial word removed.
|
||||
func words(x []byte) []uintptr {
|
||||
return unsafe.Slice((*uintptr)(unsafe.Pointer(&x[0])), uintptr(len(x))/wordSize)
|
||||
}
|
||||
|
||||
func xorLoop[T byte | uintptr](dst, x, y []T) {
|
||||
x = x[:len(dst)] // remove bounds check in loop
|
||||
y = y[:len(dst)] // remove bounds check in loop
|
||||
for i := range dst {
|
||||
dst[i] = x[i] ^ y[i]
|
||||
}
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright 2022 The Mellium Contributors.
|
||||
// Use of this source code is governed by the BSD 2-clause
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.20
|
||||
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
)
|
||||
|
||||
func goXORBytes(dst, x, y []byte) int {
|
||||
return subtle.XORBytes(dst, x, y)
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (ppc64 || ppc64le) && !purego
|
||||
|
||||
package sasl
|
||||
|
||||
//go:noescape
|
||||
func xorBytes(dst, a, b *byte, n int)
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (ppc64 || ppc64le) && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytes(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytes(SB), NOSPLIT, $0
|
||||
MOVD dst+0(FP), R3 // R3 = dst
|
||||
MOVD a+8(FP), R4 // R4 = a
|
||||
MOVD b+16(FP), R5 // R5 = b
|
||||
MOVD n+24(FP), R6 // R6 = n
|
||||
|
||||
CMPU R6, $32, CR7 // Check if n ≥ 32 bytes
|
||||
MOVD R0, R8 // R8 = index
|
||||
CMPU R6, $8, CR6 // Check if 8 ≤ n < 32 bytes
|
||||
BLT CR6, small // Smaller than 8
|
||||
BLT CR7, xor16 // Case for 16 ≤ n < 32 bytes
|
||||
|
||||
// Case for n ≥ 32 bytes
|
||||
preloop32:
|
||||
SRD $5, R6, R7 // Setup loop counter
|
||||
MOVD R7, CTR
|
||||
MOVD $16, R10
|
||||
ANDCC $31, R6, R9 // Check for tailing bytes for later
|
||||
loop32:
|
||||
LXVD2X (R4)(R8), VS32 // VS32 = a[i,...,i+15]
|
||||
LXVD2X (R4)(R10), VS34
|
||||
LXVD2X (R5)(R8), VS33 // VS33 = b[i,...,i+15]
|
||||
LXVD2X (R5)(R10), VS35
|
||||
XXLXOR VS32, VS33, VS32 // VS34 = a[] ^ b[]
|
||||
XXLXOR VS34, VS35, VS34
|
||||
STXVD2X VS32, (R3)(R8) // Store to dst
|
||||
STXVD2X VS34, (R3)(R10)
|
||||
ADD $32, R8 // Update index
|
||||
ADD $32, R10
|
||||
BC 16, 0, loop32 // bdnz loop16
|
||||
|
||||
BEQ CR0, done
|
||||
|
||||
MOVD R9, R6
|
||||
CMP R6, $8
|
||||
BLT small
|
||||
xor16:
|
||||
CMP R6, $16
|
||||
BLT xor8
|
||||
LXVD2X (R4)(R8), VS32
|
||||
LXVD2X (R5)(R8), VS33
|
||||
XXLXOR VS32, VS33, VS32
|
||||
STXVD2X VS32, (R3)(R8)
|
||||
ADD $16, R8
|
||||
ADD $-16, R6
|
||||
CMP R6, $8
|
||||
BLT small
|
||||
xor8:
|
||||
// Case for 8 ≤ n < 16 bytes
|
||||
MOVD (R4)(R8), R14 // R14 = a[i,...,i+7]
|
||||
MOVD (R5)(R8), R15 // R15 = b[i,...,i+7]
|
||||
XOR R14, R15, R16 // R16 = a[] ^ b[]
|
||||
SUB $8, R6 // n = n - 8
|
||||
MOVD R16, (R3)(R8) // Store to dst
|
||||
ADD $8, R8
|
||||
|
||||
// Check if we're finished
|
||||
CMP R6, R0
|
||||
BGT small
|
||||
RET
|
||||
|
||||
// Case for n < 8 bytes and tailing bytes from the
|
||||
// previous cases.
|
||||
small:
|
||||
CMP R6, R0
|
||||
BEQ done
|
||||
MOVD R6, CTR // Setup loop counter
|
||||
|
||||
loop:
|
||||
MOVBZ (R4)(R8), R14 // R14 = a[i]
|
||||
MOVBZ (R5)(R8), R15 // R15 = b[i]
|
||||
XOR R14, R15, R16 // R16 = a[i] ^ b[i]
|
||||
MOVB R16, (R3)(R8) // Store to dst
|
||||
ADD $1, R8
|
||||
BC 16, 0, loop // bdnz loop
|
||||
|
||||
done:
|
||||
RET
|
||||
Reference in New Issue
Block a user