scram

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 7, 2022 License: Apache-2.0 Imports: 14 Imported by: 110

README

Go Reference Go Report Card Github Actions

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 and RFC-7677.

It includes both client and server side support.

Channel binding and extensions are not (yet) 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 ""
}

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

Documentation

Overview

Package scram provides client and server implementations of the Salted Challenge Response Authentication Mechanism (SCRAM) described in RFC-5802 and RFC-7677.

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.

Example
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 ""
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

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.

func (*Client) GetStoredCredentials

func (c *Client) GetStoredCredentials(kf KeyFactors) StoredCredentials

GetStoredCredentials takes a salt and iteration count structure and provides the values that must be stored by a server to authentication a user. These values are what the Server credential lookup function must return for a given username.

func (*Client) NewConversation

func (c *Client) NewConversation() *ClientConversation

NewConversation constructs a client-side authentication conversation. Conversations cannot be reused, so this must be called for each new authentication attempt.

func (*Client) WithMinIterations

func (c *Client) WithMinIterations(n int) *Client

WithMinIterations changes minimum required PBKDF2 iteration count.

func (*Client) WithNonceGenerator

func (c *Client) WithNonceGenerator(ng NonceGeneratorFcn) *Client

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.

type ClientConversation

type ClientConversation struct {
	// contains filtered or unexported fields
}

ClientConversation implements the client-side of an authentication conversation with a server. A new conversation must be created for each authentication attempt.

func (*ClientConversation) Done

func (cc *ClientConversation) Done() bool

Done returns true if the conversation is completed or has errored.

func (*ClientConversation) Step

func (cc *ClientConversation) Step(challenge string) (response string, err error)

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 (*ClientConversation) Valid

func (cc *ClientConversation) Valid() bool

Valid returns true if the conversation successfully authenticated with the server, including counter-validation that the server actually has the user's stored credentials.

type CredentialLookup

type CredentialLookup func(string) (StoredCredentials, error)

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 HashGeneratorFcn

type HashGeneratorFcn func() hash.Hash

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.

var SHA1 HashGeneratorFcn = func() hash.Hash { return sha1.New() }

SHA1 is a function that returns a crypto/sha1 hasher and should be used to create Client objects configured for SHA-1 hashing.

var SHA256 HashGeneratorFcn = func() hash.Hash { return sha256.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 SHA512 HashGeneratorFcn = func() hash.Hash { return sha512.New() }

SHA512 is a function that returns a crypto/sha512 hasher and should be used to create Client objects configured for SHA-512 hashing.

func (HashGeneratorFcn) NewClient

func (f HashGeneratorFcn) NewClient(username, password, authzID string) (*Client, error)

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 (HashGeneratorFcn) NewClientUnprepped

func (f HashGeneratorFcn) NewClientUnprepped(username, password, authzID string) (*Client, error)

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 (HashGeneratorFcn) NewServer

func (f HashGeneratorFcn) NewServer(cl CredentialLookup) (*Server, error)

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.

type KeyFactors

type KeyFactors struct {
	Salt  string
	Iters int
}

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 NonceGeneratorFcn

type NonceGeneratorFcn func() string

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 Server

type Server struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

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.

func (*Server) NewConversation

func (s *Server) NewConversation() *ServerConversation

NewConversation constructs a server-side authentication conversation. Conversations cannot be reused, so this must be called for each new authentication attempt.

func (*Server) WithNonceGenerator

func (s *Server) WithNonceGenerator(ng NonceGeneratorFcn) *Server

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.

type ServerConversation

type ServerConversation struct {
	// contains filtered or unexported fields
}

ServerConversation implements the server-side of an authentication conversation with a client. A new conversation must be created for each authentication attempt.

func (*ServerConversation) AuthzID

func (sc *ServerConversation) AuthzID() string

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 (*ServerConversation) Done

func (sc *ServerConversation) Done() bool

Done returns true if the conversation is completed or has errored.

func (*ServerConversation) Step

func (sc *ServerConversation) Step(challenge string) (response string, err error)

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 (*ServerConversation) Username

func (sc *ServerConversation) Username() string

Username returns the client-provided username. This is valid to call if the first conversation Step() is successful.

func (*ServerConversation) Valid

func (sc *ServerConversation) Valid() bool

Valid returns true if the conversation successfully authenticated the client.

type StoredCredentials

type StoredCredentials struct {
	KeyFactors
	StoredKey []byte
	ServerKey []byte
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL