srp

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2019 License: MIT Imports: 6 Imported by: 0

README

Build Status Report Card codecov

srp

A Go implementation of the Secure Remote Password Protocol (SRP)

Overview

This package implements SRP as defined in RFC 2945 and RFC 5054.

RFC 2945: The SRP Authentication and Key Exchange System

RFC 5054: Using the Secure Remote Password (SRP) Protocol for TLS Authentication

Usage

This package exposes several methods defined in AuthClient and AuthServer interfaces to complete the authentication flow. It offers a default Client and Server set to use SHA256 as the hashing algorithm and the 4096 prime value group from RFC 5054 Section 3.2.

c, _ := NewDefaultClient("username", "password")
s, _ := NewDefaultServer()

You can pass your own hashing algorithm or prime value group as well.

g, _ := NewGroup(Group8192)
c, _ := NewClient(crypto.SHA512, g, "username", "password")
s, _ := NewServer(crypto.SHA512, g)

This package provides the tooling to enroll and validate a user. Using this library however will still require you to:

  • Securely store client submitted credentials during registration. This includes username, salt, verifier
  • Retreive user salt and verifier during authentication.
Authentication Overview

A detailed overview on authentication can be found on RFC 2945 Page 3. In general, we implement the following flow where:

  • a, A: Client ephemeral private and public key (*big.Int)
  • b, B: Server ephemeral private and public key (*big.Int)
  • K: PremasterKey (A shared key generated by both Client and Server (*big.Int)
  • M1: Client proof of K generation (*big.Int)
  • M2: Server proof of K generation (*big.Int)
Client                        Server
----------                    ----------
Calculate a, A
I, A              --------->
                              Calculate b, B
                  <---------  B, s
Calculate K, M1
M1                --------->  Calculate K, M2
                              Confirm M2
                  <---------  M2
Confirm M2

At each stage of the auth flow, client/server will receive/return several credentails (ex. salt, verifier, proof, public keys) to move forward with the premasterkey calculation.

Registration
  • Client generates username, salt, verifier
uname, salt, verifier, err := c.Enroll()
  • Server accepts credentials. Persisting this data is outside the scope of this package. The verifier value acts as the server's long term secret value for the user. It is never transmitted back after this.
isEnrolled := s.ProcessEnroll(uname, salt, verifier)
Authentication
  • Client generates a new public key and username used during enrollment
uname, cPubKey := c.Auth()
  • Server receives credentials from the client. On success, it will generate its own public key and return the salt used during registration.
# You will need to implement this
salt, verifier := RetrieveThisFromSomeStorage()

# On success we will receive the salt and ephemeral public key for the client
sPubKey, salt, err := s.ProcessAuth(uname, salt, cPubKey, verifier)
  • Client receives salt and ephemeral public key from server. It then generates proof of its identity to send back to the server.
cProof, err := c.ProveIdentity(sPubKey, salt)
  • Server receives the client's proof of identity. If valid, it will return it's own proof for the client.
sProof, err := s.ProcessProof(cProof)
  • If the server successfully validated the client proof, it will generate it's own proof that the client may validate.
isServerValid := c.IsProofValid(sProof)

Validation of both the server and client proof ensures that they both calculated the same PremasterKey. At this point you may authenticate the user or use the shared key as part of your authentication protocol.

Test

Tests rely on testify's assert library. It should install automatically if this project is stored outside of your GOPATH. If it is inside GOPATH, you first need to enable module support.

export GO111MODULE=on

Run tests

make test

Lint

golangci-lint is used for linting. To install (OSX)

brew install golangci/tap/golangci-lint

Run linter

make lint

Documentation

Overview

Package srp implements the Secure Remote Password Protocol (Version 6a)

SRP-6a is a password-authenticated key agreement (PAKE) protocol where a client/user demonstrates to a server that they know the password without sending the password or any other information from which a password can be inferred.

The goal of SRP is for both client and server to generate the same session key (K), which they prove by sharing a hash (M) of several known parameters and attempting to replicate the value. Validating the value of M proves that the server and client are each aware of their long term secrets x (client secret) and v (server secret)

RFC 2945: The SRP Authentication and Key Exchange System https://tools.ietf.org/html/rfc2945

RFC 5054: Using the Secure Remote Password (SRP) Protocol for TLS Authentication https://tools.ietf.org/html/rfc5054

Operations

^: Denotes exponentiation operation
|: Denotes concatenation
%: Denotes modulo operation
H(): Hash Function (eg. SHA256)

Roles

C: Client/User attempting authentication
S: Server authenticating the client

Key

N, g: Group parameters (a large prime N, and a primitive root of N)
I: An identifying username belonging to C
p: A password belonging to C
s: A salt belonging to C
x: Private key derived from p and s; x = H(s|H(I|":"|p))
k: A multiplier parameter derived by both C and S; k = H(N, g)
u: A scrambling parameter derived by both C and S; u = H(A, B)
v: The password verifier belonging to S and derived from x; v = g^x % N
a,A: Secret/Public ephemeral values belonging to C
b,B: Secret/Public ephemeral values belonging to S
M: Calculated proof of key generation
K: Calculated shared key

Scenario: Client (C) establishes a password with Server (S)

  1. C selects a password p, salt s and computes x = H(s|H(I|":"|p)), v = g^x % N
  2. C submits v (password verifier), s, I (username) to S
  3. S stores v and s, indexed by I

Scenario: Client (C) demonstrates proof of password to Server (S)

Initial hash of shared public keys

  1. C generates secret/public ephemeral values a/A where A = g^a % N
  2. C submits I and A to S
  3. S generates secret/public ephemeral values b/B where B = (kv + g^b) % N
  4. S submits B and s to C
  5. C and S both calculate u = H(A, B)

Calculation of keys:

  1. C calculates Premaster Secret cPS = ((B - k (g^x)) ^ (a + ux)) % N
  2. S calculates Premaster Secret sPS = ((A * v^u) ^ b) % N
  3. C calculates M1 = H(H(N) XOR H(g), H(I), s, A, B, H(cPS))
  4. S calculates M2 = H(A, M1, H(sPS))

Confirmation of proof:

  1. C submits M1 and S confirms M1
  2. S submits M2 and C onfirms M2

Full authentication is as follows:

Client                        Server
----------                    ----------
Calculate a, A
I, A              --------->
							  Calculate b, B
				  <---------  B, s
Calculate K, M1
M1                --------->  Calculate K, M2
							  Confirm M2
				  <---------  M2
Confirm M2

Index

Examples

Constants

View Source
const (
	// ErrNoGroupParams is returned when SRP group parameters N and G
	// are not initialized.
	ErrNoGroupParams = Error("srp.Group not initialized")
	// ErrNoPremasterKey is returned when a calculation is attempted
	// that requires the key.
	ErrNoPremasterKey = Error("premaster key required for calculation")
	// ErrBadClientProof is returned when the client submits invalid
	// proof.
	ErrBadClientProof = Error("invalid client proof received")
	// ErrNoHash is returned when a client or server attempts a calculation
	// with no hashing function set.
	ErrNoHash = Error("hash not initialized")
	// ErrCalcVerifier is returned when the client fails to calculate a
	// verifier value.
	ErrCalcVerifier = Error("failed to generate verifier")
	// ErrNoEphemeralKeys is returned when a calculation is done with missing
	// ephemeral keys A or B.
	ErrNoEphemeralKeys = Error("shared keys A/B not calculated")
	// ErrPublicKeyModuloZero is returned when a public key % N is 0.
	ErrPublicKeyModuloZero = Error("key % N cannot be 0")
	// ErrInvalidPrime is returned when a Group is created with an invalid prime
	// value.
	ErrInvalidPrime = Error("invalid prime value provided")
	// ErrInvalidPrimitiveRoot is returned when a Group is created with an invalid
	// primitive root.
	ErrInvalidPrimitiveRoot = Error("invalid primitive root provided")
)
View Source
const (
	// Group1024 is a primitive root and hexadecimal prime number.
	Group1024 = "2:0xEEAF0AB9ADB38DD69C33F80AFA8FC5E860726187" +
		"75FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692" +
		"C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6" +
		"CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68ED" +
		"BC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2F" +
		"C0EB06E3"
	// Group2048 is a primitive root and hexadecimal prime number.
	Group2048 = "2:0xAC6BDB41324A9A9BF166DE5E1389582FAF72B665" +
		"1987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767" +
		"A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0" +
		"CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F9" +
		"7993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E44" +
		"6B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717" +
		"461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA" +
		"032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC" +
		"041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803" +
		"D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4A" +
		"FF73"
	// Group4096 is a primitive root and hexadecimal prime number.
	Group4096 = "5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B" +
		"80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E34" +
		"04DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
		"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE38" +
		"6BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8" +
		"A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3" +
		"AD961C62F356208552BB9ED529077096966D670C354E4ABC9804" +
		"F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B27" +
		"83A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497C" +
		"EA956AE515D2261898FA051015728E5A8AAAC42DAD33170D0450" +
		"7A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
		"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3" +
		"D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18" +
		"177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5" +
		"AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" +
		"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583" +
		"E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C5947" +
		"4E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2" +
		"D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC1" +
		"86FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF"
	// Group8192 is a primitive root and hexadecimal prime number.
	Group8192 = "19:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628" +
		"B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3" +
		"404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C24" +
		"5E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE3" +
		"86BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB" +
		"8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA" +
		"3AD961C62F356208552BB9ED529077096966D670C354E4ABC980" +
		"4F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2" +
		"783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497" +
		"CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D045" +
		"07A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7" +
		"DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE" +
		"3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B1" +
		"8177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E" +
		"5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D" +
		"788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA258" +
		"3E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C594" +
		"74E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE" +
		"2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC" +
		"186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D" +
		"4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382" +
		"F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9" +
		"B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF4" +
		"26FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F48" +
		"2D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97" +
		"FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4" +
		"154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32" +
		"806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA5" +
		"6C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5" +
		"B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A93" +
		"2DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47E" +
		"D2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238" +
		"F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A" +
		"81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C" +
		"087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82" +
		"DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9A" +
		"B48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F" +
		"4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC0" +
		"26E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7" +
		"160C980DD98EDD3DFFFFFFFFFFFFFFFFF"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthClient

type AuthClient interface {
	Enroll() (string, string, *big.Int, error)
	Auth() (string, *big.Int)
	ProveIdentity(*big.Int, string) (*big.Int, error)
}

AuthClient is an interface to support client related requests for enrollment and authentication.

type AuthServer

type AuthServer interface {
	ProcessEnroll(u, s string, v *big.Int) bool
	ProcessAuth(u, sa string, A, v *big.Int) (*big.Int, string, error)
	ProcessProof(cp *big.Int) (*big.Int, error)
}

AuthServer is an interface to support client request validation.

type Client

type Client struct {
	SRP
	// contains filtered or unexported fields
}

Client represents an SRP Client. Client possesses a password and initiates enrollment and authentication against an SRP Server.

func NewClient

func NewClient(h crypto.Hash, g *Group, u, p string) (*Client, error)

NewClient returns an SRP Clientwith user defined hash and group.

func NewDefaultClient

func NewDefaultClient(u, p string) (*Client, error)

NewDefaultClient returns an SRP Client preconfigured for parameters Group4096 and SHA256 hashing function.

func (*Client) Auth

func (c *Client) Auth() (string, *big.Int)

Auth prepares an authentication payload for an SRP server. We expect a Client to have already completed a RequestEnrollment prior to submitting an authentication request.

func (*Client) Enroll

func (c *Client) Enroll() (string, string, *big.Int, error)

Enroll prepares an enrollment payload for an SRP server. We expect enrollment payload to be persisted on the server for future authentication.

func (*Client) IsProofValid

func (c *Client) IsProofValid(i *big.Int) bool

IsProofValid validates a SRP Server's proof of session key.

func (*Client) ProveIdentity

func (c *Client) ProveIdentity(A *big.Int, s string) (*big.Int, error)

ProveIdentity accepts a SRP Server's authentication response and attempts to prove Client authentication with the Client's proof of key.

type Error

type Error string

Error represents an SRP error

func (Error) Error

func (e Error) Error() string

type Group

type Group struct {
	// Hex is the hexadecimal value for prime N
	Hex string
	// G is a primitive root of prime N
	G *big.Int
	// N is a large prime value
	N *big.Int
}

Group represents constant parameters in the SRP protocol.

It is a combination of large prime value, N and a primitive root of N, known as G, used by both the client and server.

RFC 5054 Section 3.2 warns that due to the difficulty of calculating eligible prime values in real time, implementations of SRP should use pre-defined values from a trusted source. The values provided in this package come from RFC 5054 Appendix A.

Ref:

https://tools.ietf.org/html/rfc5054#section-3.2 https://tools.ietf.org/html/rfc5054#appendix-A

func NewGroup

func NewGroup(s string) (*Group, error)

NewGroup returns a new Group. It expects a string parameter in the format of <primitive_root>:<prime_as_hexadecimal_string> or <G>:<Hex>

func (*Group) CalcN

func (g *Group) CalcN() (*big.Int, error)

CalcN returns the integer value of a Group's Hex value.

type SRP

type SRP struct {
	// N is a large prime, referred to in RFC 5054 as N.
	N *big.Int
	// G is a primitive root of N, referred to in RFC 5054 as g.
	G *big.Int

	// H is a cryptographic hash. RFC 5054 defaults to SHA1.
	H crypto.Hash
	// I is a username, referred to in RFC 5054 as I.
	I string
	// S is a user's salt referred to in RFC 5054 as s.
	S string
	// Long term secret, RFC 5054 refers to this value as x
	// for the client and v for the server.
	Secret       *big.Int
	PremasterKey *big.Int
	// contains filtered or unexported fields
}

SRP represents the main parameters used in calculating a server/client shared key.

The srp/client and srp/server packages extend SRP from a client and server use-case perspective.

Example (Default)
c, _ := NewDefaultClient("jane_doe_123", "my-password")
s, _ := NewDefaultServer()

// Client salt and verifier and returns public key.
_, salt, verifier, _ := c.Enroll()
username, pubKeyA := c.Auth()

// Server processes auth credentials
pubKeyB, saltB, _ := s.ProcessAuth(username, salt, pubKeyA, verifier)

clientProof, err := c.ProveIdentity(pubKeyB, saltB)
fmt.Println(err)

serverProof, err := s.ProcessProof(clientProof)
fmt.Println(err)

ok := c.IsProofValid(serverProof)
fmt.Println(ok)
Output:

<nil>
<nil>
true
Example (SpecifyGroupAndHash)
g, _ := NewGroup(Group8192)
c, _ := NewClient(crypto.SHA512, g, "jane_doe_123", "my-password")
s, _ := NewServer(crypto.SHA512, g)

// Client salt and verifier and returns public key.
_, salt, verifier, _ := c.Enroll()
username, pubKeyA := c.Auth()

// Server processes auth credentials
pubKeyB, saltB, _ := s.ProcessAuth(username, salt, pubKeyA, verifier)

clientProof, err := c.ProveIdentity(pubKeyB, saltB)
fmt.Println(err)

serverProof, err := s.ProcessProof(clientProof)
fmt.Println(err)

ok := c.IsProofValid(serverProof)
fmt.Println(ok)
Output:

<nil>
<nil>
true

func NewDefaultSRP

func NewDefaultSRP() (*SRP, error)

NewDefaultSRP returns an SRP environment preconfigured for parameters Group4096 and SHA256 for a hashing function.

func NewSRP

func NewSRP(h crypto.Hash, g *Group) (*SRP, error)

NewSRP returns an SRP environment with configurable hashing function and group parameters.

type Server

type Server struct {
	SRP
}

Server represents an SRP Server. Server validates a Client's enrollment and authentication.

func NewDefaultServer

func NewDefaultServer() (*Server, error)

NewDefaultServer returns an SRP server preconfigured for parameters Group4096 and SHA256 hashing function.

func NewServer

func NewServer(h crypto.Hash, g *Group) (*Server, error)

NewServer returns an SRP Server with user defined hash and group.

func (*Server) IsProofValid

func (s *Server) IsProofValid(i *big.Int) bool

IsProofValid validates a SRP Client's proof of session key.

func (*Server) ProcessAuth

func (s *Server) ProcessAuth(u, sa string, A, v *big.Int) (*big.Int, string, error)

ProcessAuth ackwnowledges an authentication request from a pre-enrolled SRP Client. Credentials should be received from the SRP client. Salt and verifier should be retrieved from some secure persisted storage prior to this call. Retreival of cr2 is outside the scope of this package.

func (*Server) ProcessEnroll

func (s *Server) ProcessEnroll(u, sa string, v *big.Int) bool

ProcessEnroll acknowledges an enrollment payload from an SRP Client. It returns true if valid credentials have been submitted.

func (*Server) ProcessProof

func (s *Server) ProcessProof(cp *big.Int) (*big.Int, error)

ProcessProof receives and validates proof from an SRP Client.

Jump to

Keyboard shortcuts

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