Files
athens/vendor/github.com/xdg/scram/client_conv.go
Arpit Gogia 974077e73b Mongo go driver beta integration (#1042)
* Changed mongo.go to use new driver

* Modified mongo cataloger

* More new driver related changes

* Change lister.go

* Change saver.go

* Change imports

* Remove unnecessary Count query

* Use IndexView for indexing

* Rename ModuleStore fields

* Use map of key:sorting-order for creating the index

* Minor changes

* Use client options to configure mongo client

* Use method chaining

* gofmt changes

* Change imports

* Fix some build errors

* Use new GridFS API

* Fix more build errors

* Add Go Mongo driver to dependency modules

* Use multierror

* Leave download stream open

* Remove mgo error handling

* Copy zip instead of loading all in memory

* Use context.WithTimeout() wherever possible

* Raise KindNotFound when mod@ver isn't found

* NopCloser not needed

* Fix IndexView error

* Fix build errors

* Remove another mgo error usage

* Fix build error

* Changes according to review

* Formatting changes as per gofmt

* Modify gofmt argument to show the expected formatting (diff)

* Handle ErrNoDocument error and error arising from query execution

* Fix kind of returned error

* Minor changes

* Bug fixes

* gofmt related changes

* Minor change

* Use Insecure from MongoConfig, remove Insecure from global Config

* Remove stray print statement
2019-04-17 19:59:01 +02:00

150 lines
4.0 KiB
Go

// 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
}
// 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 client-final-message-without-proof
c2wop := fmt.Sprintf(
"c=%s,r=%s",
base64.StdEncoding.EncodeToString([]byte(cc.gs2)),
cc.nonce,
)
// Create auth message
authMsg := cc.c1b + "," + s1 + "," + c2wop
// Get derived keys from client cache
dk := cc.client.getDerivedKeys(KeyFactors{Salt: string(msg.salt), Iters: msg.iters})
// Create proof as clientkey XOR clientsignature
clientSignature := computeHMAC(cc.hashGen, dk.StoredKey, []byte(authMsg))
clientProof := xorBytes(dk.ClientKey, clientSignature)
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 {
if cc.client.authzID == "" {
return "n,,"
}
return fmt.Sprintf("n,%s,", encodeName(cc.client.authzID))
}