blob: 9e9836afe71b39c8aaa7ddb50ae269892547cedc [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// Copyright 2018 by David A. Golden. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package scram
8
9import (
10 "crypto/sha1"
11 "crypto/sha256"
12 "fmt"
13 "hash"
14
15 "github.com/xdg/stringprep"
16)
17
18// HashGeneratorFcn abstracts a factory function that returns a hash.Hash
19// value to be used for SCRAM operations. Generally, one would use the
20// provided package variables, `scram.SHA1` and `scram.SHA256`, for the most
21// common forms of SCRAM.
22type HashGeneratorFcn func() hash.Hash
23
24// SHA1 is a function that returns a crypto/sha1 hasher and should be used to
25// create Client objects configured for SHA-1 hashing.
26var SHA1 HashGeneratorFcn = func() hash.Hash { return sha1.New() }
27
28// SHA256 is a function that returns a crypto/sha256 hasher and should be used
29// to create Client objects configured for SHA-256 hashing.
30var SHA256 HashGeneratorFcn = func() hash.Hash { return sha256.New() }
31
32// NewClient constructs a SCRAM client component based on a given hash.Hash
33// factory receiver. This constructor will normalize the username, password
34// and authzID via the SASLprep algorithm, as recommended by RFC-5802. If
35// SASLprep fails, the method returns an error.
36func (f HashGeneratorFcn) NewClient(username, password, authzID string) (*Client, error) {
37 var userprep, passprep, authprep string
38 var err error
39
40 if userprep, err = stringprep.SASLprep.Prepare(username); err != nil {
41 return nil, fmt.Errorf("Error SASLprepping username '%s': %v", username, err)
42 }
43 if passprep, err = stringprep.SASLprep.Prepare(password); err != nil {
44 return nil, fmt.Errorf("Error SASLprepping password '%s': %v", password, err)
45 }
46 if authprep, err = stringprep.SASLprep.Prepare(authzID); err != nil {
47 return nil, fmt.Errorf("Error SASLprepping authzID '%s': %v", authzID, err)
48 }
49
50 return newClient(userprep, passprep, authprep, f), nil
51}
52
53// NewClientUnprepped acts like NewClient, except none of the arguments will
54// be normalized via SASLprep. This is not generally recommended, but is
55// provided for users that may have custom normalization needs.
56func (f HashGeneratorFcn) NewClientUnprepped(username, password, authzID string) (*Client, error) {
57 return newClient(username, password, authzID, f), nil
58}
59
60// NewServer constructs a SCRAM server component based on a given hash.Hash
61// factory receiver. To be maximally generic, it uses dependency injection to
62// handle credential lookup, which is the process of turning a username string
63// into a struct with stored credentials for authentication.
64func (f HashGeneratorFcn) NewServer(cl CredentialLookup) (*Server, error) {
65 return newServer(cl, f)
66}