seba-365 - implemented dep
Change-Id: Ia6226d50e7615935a0c8876809a687427ff88c22
diff --git a/vendor/github.com/xdg/scram/server_conv.go b/vendor/github.com/xdg/scram/server_conv.go
new file mode 100644
index 0000000..9c8838c
--- /dev/null
+++ b/vendor/github.com/xdg/scram/server_conv.go
@@ -0,0 +1,151 @@
+// 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
+}
+
+// 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
+}
+
+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.username = msg.username
+ sc.authzID = msg.authzID
+
+ sc.credential, err = sc.credentialCB(msg.username)
+ if err != nil {
+ sc.state = serverDone
+ return "e=unknown-user", 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 matches what we expect; in this case, we expect
+ // just the gs2 header we received as we don't support channel binding
+ // with a data payload. If we add binding, we need to independently
+ // compute the header to match here.
+ if string(msg.cbind) != sc.gs2Header {
+ return "e=channel-bindings-dont-match", fmt.Errorf("channel binding received '%s' doesn't match expected '%s'", msg.cbind, sc.gs2Header)
+ }
+
+ // Check nonce received matches what we sent
+ if msg.nonce != sc.nonce {
+ return "e=other-error", 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 := xorBytes([]byte(msg.proof), clientSignature)
+ storedKey := computeHash(sc.hashGen, clientKey)
+
+ // Compare with constant-time function
+ if !hmac.Equal(storedKey, sc.credential.StoredKey) {
+ return "e=invalid-proof", 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
+}