diff options
Diffstat (limited to 'vendor/mellium.im/sasl/scram.go')
-rw-r--r-- | vendor/mellium.im/sasl/scram.go | 227 |
1 files changed, 0 insertions, 227 deletions
diff --git a/vendor/mellium.im/sasl/scram.go b/vendor/mellium.im/sasl/scram.go deleted file mode 100644 index 6e6bdc3c2..000000000 --- a/vendor/mellium.im/sasl/scram.go +++ /dev/null @@ -1,227 +0,0 @@ -// 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" - "encoding/base64" - "errors" - "hash" - "strconv" - "strings" - - "golang.org/x/crypto/pbkdf2" -) - -const ( - gs2HeaderCBSupport = "p=tls-unique," - 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() - switch { - case n.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 - gs2Header = []byte(gs2HeaderCBSupport) - 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 challenge == nil || 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 - for _, field := range bytes.Split(challenge, []byte{','}) { - 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 - } - } - - switch { - case iter < 0: - err = errors.New("Iteration count is missing") - return - 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 - if tlsState != nil && strings.HasSuffix(name, "-PLUS") { - channelBinding = make( - []byte, - 2+base64.StdEncoding.EncodedLen(len(gs2Header)+len(tlsState.TLSUnique)), - ) - base64.StdEncoding.Encode(channelBinding[2:], append(gs2Header, tlsState.TLSUnique...)) - channelBinding[0] = 'c' - channelBinding[1] = '=' - } else { - channelBinding = make( - []byte, - 2+base64.StdEncoding.EncodedLen(len(gs2Header)), - ) - base64.StdEncoding.Encode(channelBinding[2:], gs2Header) - channelBinding[0] = 'c' - channelBinding[1] = '=' - } - 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) - h.Write(serverKeyInput) - serverKey := h.Sum(nil) - h.Reset() - - h.Write(clientKeyInput) - clientKey := h.Sum(nil) - - h = hmac.New(fn, serverKey) - h.Write(authMessage) - serverSignature := h.Sum(nil) - - h = fn() - h.Write(clientKey) - storedKey := h.Sum(nil) - h = hmac.New(fn, storedKey) - h.Write(authMessage) - clientSignature := h.Sum(nil) - clientProof := make([]byte, len(clientKey)) - xorBytes(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 -} |