diff --git a/.gitignore b/.gitignore index daf913b..ccad70b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ _testmain.go *.exe *.test *.prof +.??*.sw? +src/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..dffd87d --- /dev/null +++ b/README.md @@ -0,0 +1,221 @@ +# Standalone SRP-6a implementation in go-lang +This is a standalone implementation of SRP in golang. It uses the go +standard libraries and has no other external dependencies. This library +can be used by SRP clients or servers. + +SRP is a protocol to authenticate a user and derive safe session keys. +It is the latest in the category of \"strong authentication protocols\". + +SRP is documented here: + +## Setting up the Verifiers on the Server +In order to authenticate and derive session keys, verifiers must be +stored in a non-volatile medium on the server. The client provides the +prime-field size, username and password when creating the verifier. The +server stores the triple in a non-volatile medium. The verifiers are +generated once when a *user* is created on the server. + +The Client is the entity where the user enters their password and wishes +to be authenticated with a SRP server. The communication between client +and server can happen in clear text - SRP is immune to man in the middle +attacks. + +Depending on the resources available on a given client, it can choose a +small or large prime-field; but once chosen it is recorded on the server +- until a new verifier is generated. + +```go + + s, err := srp.New(n_bits) + + v, err := s.Verifier(username, password) + + id, verif := v.Encode() + + // Now, store 'id', 'verif' in non-volatile storage such that 'verif' can be + // retrieved by providing 'id'. +``` + +Note that `id` is the hashed identity string for username. + +## Authentication attempt from the Client +The client performs the following sequence of steps to authenticate and +derive session keys: + +```go + + s, err := srp.New(n_bits) + + c, err := s.NewClient(user, pass) + + creds := c.Credentials() + + // 1. send the credentials to the server. It is already in ASCII string form; this + // is essentially the encoded form of identity and a random public key. + + // 2. Receive the server credentials into 'server_creds'; this is the server + // public key and random salt generated when the verifier was created. + + // It is assumed that there is some network communication that happens + // to get this string from the server. + + // Now, generate a mutual authenticator to be sent to the server + auth, err := c.Generate(server_creds) + if err != nil { + panic(err) + } + + // 3. Send the mutual authenticator to the server + // 4. receive "proof" that the server too computed the same result. + + // Verify that the server actually did what it claims + if !c.ServerOk(proof) { + panic("authentication failed") + } + + // Generate session key + rawkey := c.RawKey() +``` + +## Authenticating a Client on the Server +On the server, the authentication attempt begins after receiving the +initial user credentials. This is used to lookup the stored verifier and +other bits.: + +```go + + // Assume that we received the user credentials via the network into 'creds' + + + // Parse the user info and authenticator from the 'creds' string + id, A, err := srp.ServerBegin(creds) + + // Use 'id' to lookup the user in some non-volatile DB and obtain + // previously stored *encoded* verifier 'v'. + verifier := db.Lookup(id) + + + // Create an SRP instance and Verifier instance from the stored data. + s, v, err := srp.MakeSRPVerifier(verifier) + + // Begin a new client-server SRP session using the verifier and received + // public key. + srv, err := s.NewServer(v, A) + + // Generate server credentials to send to the user + s_creds := srv.Credentials() + + // 1. send 's_creds' to the client + // 2. receive 'm_auth' from the client + + // Authenticate user and generate mutual proof of authentication + proof, ok := srv.ClientOk(m_auth) + if ok != nil { + panic("Authentication failed") + } + + // 3. Send proof to client + + // Auth succeeded, derive session key + rawkey := s.RawKey() +``` + +## Building SRP + +There is an example program that shows you the API usage (documented +above).: + +```sh + $ mkdir srp-example && cd srp-example + $ GOPATH=$PWD go get github.com/opencoff/go-srp + $ GOPATH=$PWD go test github.com/opencoff/go-srp +``` + +Finally, build the example program: + +```sh + $ GOPATH=$PWD go build github.com/opencoff/go-srp/example + $ ./example +``` + +The example program outputs the raw-key from the client & server\'s +perspective (they should be identical). + +Using the library in your program: + +```sh + $ go get github.com/opencoff/go-srp +``` + +And in your program - as the following import path: + +```go + + import "github.com/opencoff/go-srp" +``` + +Other Notes +----------- + +- The client and server both derive the same value for RawKey(). This + is the crux of the SRP protocol. Treat this as a \"master key\". +- It is not advisable to use the RawKey() for encryption purposes. It + is better to derive a separate key for each direction + (client-\>server and server-\>client). e.g.,: + + ```go + c2s_k = KDF(rawkey, counter, "C2S") + s2s_k = KDF(rawkey, counter, "S2C") + ``` + +- KDF above can be a reputable key derivation function such as PBKDF2 + or Scrypt. The \"counter\" is incremented every time you derive a + new key. + +- *I am not a cryptographer*. Please consult your favorite crypto book + for deriving encryption keys from a master key. Here is a example + KDF using `scrypt`: + + ```go + import "golang.org/x/crypto/scrypt" + + // Safe values for Scrypt() parameters + const _N int = 65536 + const _r int = 1024 + const _p int = 64 + + // Kdf derives a 'sz' byte key for use 'usage' + func Kdf(key []byte, salt []byte, usage string, sz int) []byte { + + u0 := []byte(usage) + pw := append(key, u0...) + k, _ := scrypt.Key(pw, salt, _N, _r, _p, sz) + return k + } + ``` + +- [Argon](https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03) is + the new state of the art (2018) key derivation algorithm. The + `Argon2id` variant is resistant to timing, side-channel and + Time-memory tradeoff attacks. Here is an example using the `Argon2id` + variant: + + ```go + import ( + "runtime" + "golang.org/x/crypto/argon2" + ) + + // safe values for IDKey() borrowed from libsodium + const _Time uint32 = 3 + const _Mem uint32 = 256 * 1048576 // 256 MB + + // Kdf derives a 'sz' byte key for use 'usage' + func Kdf(key, salt []byte, usage string, sz int) []byte { + u0 := []byte(usage) + pw := append(key, u0...) + + return argon2.IDKey(pw, salt, _Time, _Mem, runtime.NumCPU(), uint32(sz)) + } + ``` + diff --git a/README.rst b/README.rst deleted file mode 100644 index d1e980d..0000000 --- a/README.rst +++ /dev/null @@ -1,204 +0,0 @@ -Standalone SRP-6a implementation in go-lang -=========================================== - -This is a standalone implementation of SRP in golang. It uses the go -standard libraries and has no other external dependencies. This -library can be used by SRP clients or servers. - -SRP is a protocol to authenticate a user and derive safe session -keys. It is the latest in the category of "strong authentication -protocols". - -SRP is documented here: http://srp.stanford.edu/doc.html - -Setting up the Verifiers on the Server --------------------------------------- -In order to authenticate and derive session keys, verifiers must be -stored in a non-volatile medium on the server. The verifiers are -generated once when a *user* is created on the server. - -The Client is the entity where the user enters their password and -wishes to be authenticated with a SRP server. The communication -between client and server can happen in clear text - SRP is immune -to man in the middle attacks. - -The client and server need to agree a-priori on the number of bits -to be used for the common "safe prime". It is acceptable to use 2048 -or 4096 for this value. Each verifier can have a different number of -safe prime bits - but it must be recorded along with the verifier. -Every authentication attempt must use the same bit-size for the safe -prime previously recorded for that verifier. E.g.,:: - - - Ih, salt, v, err := srp.Verifier(username, password, Safe_prime_bits) - - // Now store Ih, salt, v and possibly Safe_prime_bits in non-volatile storage - - -Note that Ih is the hashed identity string for username. - -Authentication attempt from the Client --------------------------------------- -The client performs the following sequence of steps to authenticate and derive session keys:: - - c, err := srp.NewClient(username, password, Safe_prime_bits) - - if err != nil { - panic(err) - } - - creds := c.Credentials() - - // send the credentials to the server. It is already in ASCII string form. - - // Receive the server credentials into 'server_creds' - // it is assumed that there is some network communication that happens - // to get this string from the server - - // Now, generate a mutual authenticator to be sent to the server - auth, err := c.Generate(server_creds) - if err != nil { - panic(err) - } - - // Send the mutual authenticator to the server and receive "proof" that - // the server too computed the same result. - - // Verify that we too have derived the same proof of authentication and - // session keys - err = c.ServerOk(proof) - if err != nil { - panic(err) - } - - // Generate session key - - rawkey := c.RawKey() - - -Authenticating a Client on the Server -------------------------------------- - -On the server, the authentication attempt begins after receiving the -initial user credentials. This is used to lookup the stored verifier -and other bits.:: - - - // Assume that we received the user credentials via the network into 'creds' - - - // Parse the user info and authenticator from the 'creds' string - I, A, err := srp.ServerBegin(creds) - - - // Use 'I' to lookup the user in some non-volatile DB and obtain - // previously stored verifier 'v' and 'salt' for that user. - - // Begin a new client-server SRP session - s, err := srp.NewServer(I, salt, v, A, Safe_prime_bits) - if err != nil { - panic(err) - } - - // Generate server credentials to send to the user and wait for the - // mutual authenticator to arrive - s_creds := s.Credentials() - - // Now send 's_creds' to the client and receive 'm_auth' from the client - - // Authenticate user and generate proof of authentication - proof, err := s.ClientOk(m_auth) - if err != nil { - panic("Authentication failed") - } - - // Auth succeeded, derive session key - rawkey := s.RawKey() - - -Building SRP ------------- -There is an example program that shows you the API usage (documented -above).:: - - $ mkdir srp-example && cd srp-example - $ GOPATH=$PWD go get github.com/opencoff/go-srp/srp - $ GOPATH=$PWD go test github.com/opencoff/go-srp/srp - -Finally, build the example program:: - - $ GOPATH=$PWD go build github.com/opencoff/go-srp/example - $ ./example - -The example program outputs the raw-key from the client & server's -perspective (they should be identical). - -Using the library in your program:: - - $ go get github.com/opencoff/go-srp/srp - -And in your program - as the following import path:: - - import "github.com/opencoff/go-srp/srp" - - -Other Notes ------------ - -* The client and server both derive the same value for `RawKey()`. This is - the crux of the SRP protocol. Treat this as a "master key". - -* It is not advisable to use the RawKey() for encryption purposes. It is - better to derive a separate key for each direction (client->server - and server->client). e.g., :: - - c2s_k = KDF(rawkey, counter, "C2S") - s2s_k = KDF(rawkey, counter, "S2C") - -* KDF above can be a reputable key derivation function such as PBKDF2 or - Scrypt. The "counter" is incremented every time you derive a new key. - -* *I am not a cryptographer*. Please consult your favorite crypto book for - deriving encryption keys from a master key. Here is a example KDF - using ``scrypt``:: - - import "golang.org/x/crypto/scrypt" - - // Safe values for Scrypt() parameters - const _N int = 65536 - const _r int = 1024 - const _p int = 64 - - // Kdf derives a 'sz' byte key for use 'usage' - func Kdf(key []byte, salt []byte, usage string, sz int) []byte { - - u0 := []byte(usage) - pw := append(key, u0...) - k, _ := scrypt.Key(pw, salt, _N, _r, _p, sz) - return k - } - -* Argon_ is the new state of the art (2018) key derivation algorithm. - The Argon2id variant is resistant to timing, side-channel and Time-memory - tradeoff attacks. Here is an example using the Argon2id variant:: - - import ( - "runtime" - "golang.org/x/crypto/argon2" - ) - - // safe values for IDKey() borrowed from libsodium - const _Time uint32 = 3 - const _Mem uint32 = 256 * 1048576 // 256 MB - - // Kdf derives a 'sz' byte key for use 'usage' - func Kdf(key, salt []byte, usage string, sz int) []byte { - u0 := []byte(usage) - pw := append(key, u0...) - - return argon2.IDKey(pw, salt, _Time, _Mem, runtime.NumCPU(), uint32(sz)) - } - -.. _Argon: https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03 - -.. vim: ft=rst:sw=4:ts=4:tw=72: diff --git a/srp/srp.go b/srp.go similarity index 69% rename from srp/srp.go rename to srp.go index ea3afb4..cfc9957 100644 --- a/srp/srp.go +++ b/srp.go @@ -1,12 +1,14 @@ +// srp.go - golang implementation of SRP-6a // // Copyright 2013-2017 Sudhi Herle // License: MIT // -// Implementation of SRP_. It requires a cryptographically strong -// random number generator. + +// Package srp implements SRP-6a per [1]. It uses the standard library +// and the golang extended library and nothing else. // -// This implementation is accurate as of Aug 2012 edition of the SRP_ -// specification. +// This implementation is accurate as of Aug 2012 edition of the SRP +// specification [1]. // // Conventions // ----------- @@ -106,10 +108,9 @@ // [1] http://srp.stanford.edu/design.html // [2] http://srp.stanford.edu/ // - package srp - import ( + "bytes" "crypto" "crypto/subtle" "encoding/hex" @@ -124,156 +125,177 @@ import ( ) -// Map of bits to tuple -const pflist_str = `1024:2:0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3 -1536:2:0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB -2048:2:0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73 -3072:2:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF -4096:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF -6144:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF -8192:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF` - -type prime_field struct { - g *big.Int - N *big.Int +type SRP struct { + h crypto.Hash + pf *primeField } -// prime field list - mapped by bit size -var pflist map[int]*prime_field -// build the database of prime fields and generators -func init() { +// FieldSize returns this instance's prime-field size in bits +func (s *SRP) FieldSize() int { + return s.pf.n * 8 +} - pflist = make(map[int]*prime_field) - lines := strings.Split(pflist_str, "\n") - for _, s := range lines { - v := strings.Split(s, ":") - b := atoi(v[0]) - pf := &prime_field{ - g: atobi(v[1], 10), - N: atobi(v[2], 0), - } - if 0 == big.NewInt(0).Cmp(pf.N) { - panic(fmt.Sprintf("srp init: N (%s) is zero", v[2])) - } - pflist[b] = pf - } +// New creates a new SRP environment using a 'bits' sized prime-field for +// use by SRP clients and Servers.The default hash function is Blake-2b-256. +func New(bits int) (*SRP, error) { + return NewWithHash(crypto.BLAKE2b_256, bits) } -func atoi(s string) int { - i, err := strconv.Atoi(s) - if err != nil { - panic(fmt.Sprintf("srp init: can't parse int %s", s)) +// NewWitHash creates a new SRP environment using the hash function 'h' and +// 'bits' sized prime-field size. +func NewWithHash(h crypto.Hash, bits int) (*SRP, error) { + pf, ok := pflist[bits] + if !ok { + return nil, fmt.Errorf("srp: invalid prime-field size: %d", bits) } - return i -} -func atobi(s string, base int) *big.Int { - i, ok := big.NewInt(0).SetString(s, base) - if !ok { - panic(fmt.Sprintf("srp init: can't parse bigint |%s|", s)) + s := &SRP{ + h: h, + pf: pf, } - return i + return s, nil } -// hash byte stream and return as bytes -func hashbyte(a ...[]byte) []byte { - h := crypto.BLAKE2b_256.New() - for _, z := range a { - h.Write(z) +// ServerBegin processes the first message from an SRP client and returns a decoded +// identity string and client public key. The caller is expected to use the identity +// to lookup durable storage and find the corresponding encoded Verifier. This verifier +// is given to MakeSRPVerifier() to create an instance of SRP and Verifier. +func ServerBegin(creds string) (string, *big.Int, error) { + v := strings.Split(creds, ":") + if len(v) != 2 { + return "", nil, fmt.Errorf("invalid client public key") } - return h.Sum(nil) -} -// pad x to n bytes if needed -func pad(x *big.Int, n int) []byte { - b := x.Bytes() - if len(b) < n { - z := n - len(b) - p := make([]byte, n, n) - for i := 0; i < z; i++ { - p[i] = 0 - } + //fmt.Printf("v0: %s\nv1: %s\n", v[0], v[1]) - copy(p[z:], b) - b = p + A, ok := big.NewInt(0).SetString(v[1], 16) + if !ok { + return "", nil, fmt.Errorf("Invalid client public key A") } - return b + + return v[0], A, nil } -// hash a number of byte strings and return the resulting hash as -// bigint -func hashint(a ...[]byte) *big.Int { - i := big.NewInt(0) - b := hashbyte(a...) - i.SetBytes(b) - return i +// Verifier represents password verifier that resides on an SRP server. +type Verifier struct { + i []byte // hashed identity + s []byte // random salt (same size as prime field) + v []byte // password verifier + h crypto.Hash // hash algo used for building v + sz int // prime field size } -// hash a number of byte strings and return the result as a human -// readable string -func hashhex(a ...[]byte) string { - h := hashbyte(a...) - s := fmt.Sprintf("%x", h) - return s -} +// Verifier generates a password verifier for user I and passphrase p +// Return tuple containing hashed identity, salt, verifier. Caller +// is expected to store the tuple in some persistent DB. +func (s *SRP) Verifier(I, p []byte) (*Verifier, error) { + ih := s.hashbyte(I) + ph := s.hashbyte(p) + pf := s.pf + salt := randbytes(pf.n) + x := s.hashint(ih, ph, salt) + r := big.NewInt(0).Exp(pf.g, x, pf.N) -// Return n bytes of random bytes. Uses cryptographically strong -// random generator -func randbytes(n int) []byte { - b := make([]byte, n) - _, err := io.ReadFull(CR.Reader, b) - if err != nil { - panic("Random source is broken!") + v := &Verifier{ + i: ih, + s: salt, + v: r.Bytes(), + h: s.h, + sz: pf.n, } - return b -} -// Generate and return a bigInt 'bits' bits in length -func randBigInt(bits int) *big.Int { - n := bits / 8 - if 0 == bits%8 { - n += 1 - } - b := randbytes(n) - r := big.NewInt(0).SetBytes(b) - return r + return v, nil } -// Verifier generates a password verifier for user I and passphrase p -// Return tuple containing hashed identity, salt, verifier. Caller -// is expected to store the tuple in some persistent DB -func Verifier(I, p []byte, bits int) (Ih, salt, v []byte, err error) { + +// MakeVerifier decodes the encoded verifier into an SRP and Verifier +// instance. The caller uses the SRP client provided identity to lookup a DB +// and find the encoded verifier 'b'; this encoded data contains enough +// information to create a valid SRP instance and Verifier instance. +func MakeSRPVerifier(b string) (*SRP, *Verifier, error) { + v := strings.Split(b, ":") + if len(v) != 5 { + return nil, nil, fmt.Errorf("verifier: malformed fields exp 5, saw %d", len(v)) + } + + sz, err := strconv.Atoi(v[0]) + if err != nil || sz <= 0 { + return nil, nil, fmt.Errorf("verifier: malformed field size %s", v[0]) + } + bits := sz * 8 pf, ok := pflist[bits] - if 0 == big.NewInt(0).Cmp(pf.N) || !ok { - err = fmt.Errorf("Invalid bits: %d", bits) - return + if !ok { + return nil, nil, fmt.Errorf("verifier: invalid prime-field size: %d", sz) } - I = hashbyte(I) - p = hashbyte(p) - s := randbytes(bits/8) - i := hashbyte(I, []byte(":"), p) - x := hashint(s, i) - r := big.NewInt(0).Exp(pf.g, x, pf.N) + h, err := strconv.Atoi(v[1]) + if err != nil || h <= 0 { + return nil, nil, fmt.Errorf("verifier: malformed field size %s", v[1]) + } + + hf := crypto.Hash(h) + if !hf.Available() { + return nil, nil, fmt.Errorf("verifier: hash algorithm %d unavailable", h) + } + + i, err := hex.DecodeString(v[2]) + if err != nil { + return nil, nil, fmt.Errorf("verifier: invalid identity: %s", v[2]) + } + s, err := hex.DecodeString(v[3]) + if err != nil { + return nil, nil, fmt.Errorf("verifier: invalid salt: %s", v[3]) + } + vx, err := hex.DecodeString(v[4]) + if err != nil { + return nil, nil, fmt.Errorf("verifier: invalid verifier: %s", v[4]) + } - salt = s - Ih = I - v = r.Bytes() + vf := &Verifier{ + i: i, + s: s, + v: vx, + h: hf, + sz: sz, + } - //fmt.Printf("Verifier %d:\n\tv=%x\n\tx=%x\n", bits, v, x) + sr := &SRP{ + h: hf, + pf: pf, + } - return + return sr, vf, nil +} + +// Encode the verifier into a portable format - returns a tuple +// as portable strings. The caller can store +// the Verifier against the Identity in non-volatile storage. +// An SRP client will supply Identity and its public key - whereupon, +// an SRP server will use the Identity as a key to lookup +// the rest of the encoded verifier data. +func (v *Verifier) Encode() (string, string) { + var b bytes.Buffer + + ih := hex.EncodeToString(v.i) + + b.WriteString(fmt.Sprintf("%d:", v.sz)) + b.WriteString(fmt.Sprintf("%d:", int(v.h))) + b.WriteString(ih) + b.WriteByte(':') + b.WriteString(hex.EncodeToString(v.s)) + b.WriteByte(':') + b.WriteString(hex.EncodeToString(v.v)) + + return ih, b.String() } // Client represents an SRP client instance type Client struct { - g *big.Int - xN *big.Int - n int // number of bytes in N + s *SRP i []byte p []byte a *big.Int @@ -284,41 +306,36 @@ type Client struct { xM []byte } -// NewClient constructs an SRP client instance -func NewClient(I, p []byte, bits int) (*Client, error) { - pf, ok := pflist[bits] - if !ok { - return nil, fmt.Errorf("srp: no support for %d bit field", bits) - } - +// NewClient constructs an SRP client instance. +func (s *SRP) NewClient(I, p []byte) (*Client, error) { + pf := s.pf c := &Client{ - g: pf.g, - xN: pf.N, - n: bits / 8, - i: hashbyte(I), - p: hashbyte(p), - a: randBigInt(bits), + s: s, + i: s.hashbyte(I), + p: s.hashbyte(p), + a: randBigInt(pf.n * 8), + k: s.hashint(pf.N.Bytes(), pad(pf.g, pf.n)), } - c.xA = big.NewInt(0).Exp(pf.g, c.a, pf.N) - c.k = hashint(c.xN.Bytes(), pad(c.g, c.n)) //fmt.Printf("Client %d:\n\tA=%x\n\tk=%x", bits, c.xA, c.k) return c, nil } + // Credentials returns client public credentials to send to server // Send to server func (c *Client) Credentials() string { - s0 := hex.EncodeToString(c.i) - s1 := hex.EncodeToString(c.xA.Bytes()) - return s0 + ":" + s1 + var b bytes.Buffer + + b.WriteString(hex.EncodeToString(c.i)) + b.WriteByte(':') + b.WriteString(hex.EncodeToString(c.xA.Bytes())) + return b.String() } // Generate validates the server public credentials and generate session key -// Return the mutual authenticator -// - Get from server -// - calculate S from a, s, B +// Return the mutual authenticator. // NB: We don't send leak any information in error messages. func (c *Client) Generate(srv string) (string, error) { v := strings.Split(srv, ":") @@ -326,7 +343,7 @@ func (c *Client) Generate(srv string) (string, error) { return "", fmt.Errorf("invalid server public key") } - s, err := hex.DecodeString(v[0]) + salt, err := hex.DecodeString(v[0]) if err != nil { return "", fmt.Errorf("invalid server public key") } @@ -336,31 +353,30 @@ func (c *Client) Generate(srv string) (string, error) { return "", fmt.Errorf("invalid server public key") } + pf := c.s.pf zero := big.NewInt(0) - z := big.NewInt(0).Mod(B, c.xN) + z := big.NewInt(0).Mod(B, pf.N) if zero.Cmp(z) == 0 { return "", fmt.Errorf("invalid server public key") } - u := hashint(pad(c.xA, c.n), pad(B, c.n)) + u := c.s.hashint(pad(c.xA, pf.n), pad(B, pf.n)) if u.Cmp(zero) == 0 { return "", fmt.Errorf("invalid server public key") } // S := ((B - kg^x) ^ (a + ux)) % N - i := hashbyte(c.i, []byte(":"), c.p) - x := hashint(s, i) - - t0 := big.NewInt(0).Exp(c.g, x, c.xN) + x := c.s.hashint(c.i, c.p, salt) + t0 := big.NewInt(0).Exp(pf.g, x, pf.N) t0 = t0.Mul(t0, c.k) t1 := big.NewInt(0).Sub(B, t0) t2 := big.NewInt(0).Add(c.a, big.NewInt(0).Mul(u, x)) - S := big.NewInt(0).Exp(t1, t2, c.xN) + S := big.NewInt(0).Exp(t1, t2, pf.N) - c.xK = hashbyte(S.Bytes()) - c.xM = hashbyte(c.xK, c.xA.Bytes(), B.Bytes(), c.i, s, c.xN.Bytes(), c.g.Bytes()) + c.xK = c.s.hashbyte(S.Bytes()) + c.xM = c.s.hashbyte(c.xK, c.xA.Bytes(), B.Bytes(), c.i, salt, pf.N.Bytes(), pf.g.Bytes()) //fmt.Printf("Client %d:\n\tx=%x\n\tS=%x\n\tK=%x\n\tM=%x\n", c.n *8, x, S, c.xK, c.xM) @@ -369,15 +385,11 @@ func (c *Client) Generate(srv string) (string, error) { // ServerOk takes a 'proof' offered by the server and verifies that it is valid. // i.e., we should compute the same hash() on M that the server did. -func (c *Client) ServerOk(proof string) error { - h := hashbyte(c.xK, c.xM) +func (c *Client) ServerOk(proof string) bool { + h := c.s.hashbyte(c.xK, c.xM) myh := hex.EncodeToString(h) - if subtle.ConstantTimeCompare([]byte(myh), []byte(proof)) != 1 { - return fmt.Errorf("Server failed to generate same password") - } - - return nil + return subtle.ConstantTimeCompare([]byte(myh), []byte(proof)) == 1 } // RawKey returns the raw key computed as part of the protocol @@ -387,29 +399,27 @@ func (c *Client) RawKey() []byte { // String represents the client parameters as a string value func (c *Client) String() string { + pf := c.s.pf return fmt.Sprintf(" g=%d, N=%x\n I=%x\n A=%x\n K=%x\n", - c.g, c.xN, c.i, c.xA, c.xK) + pf.g, pf.N, c.i, c.xA, c.xK) } // Server represents an SRP server instance type Server struct { - g *big.Int - xN *big.Int - n int // number of bytes in N - i []byte - s []byte - v *big.Int - xB *big.Int - - xK []byte - xM []byte + s *SRP + i []byte + salt []byte + v *big.Int + xB *big.Int + xK []byte + xM []byte } // ServerBegin begins the server processing by parsing the credentials sent by // the client. // The caller is expected to use 'I' to lookup some database and // find the verifier, salt and other user specific parameters. -func ServerBegin(creds string) (I []byte, A *big.Int, err error) { +func (s *SRP) ServerBegin(creds string) (I []byte, A *big.Int, err error) { v := strings.Split(creds, ":") if len(v) != 2 { err = fmt.Errorf("Invalid client public key") @@ -428,20 +438,15 @@ func ServerBegin(creds string) (I []byte, A *big.Int, err error) { return } -// NewServer constructs a Server instance -func NewServer(I, salt, v []byte, A *big.Int, bits int) (*Server, error) { - pf, ok := pflist[bits] - if !ok { - return nil, fmt.Errorf("srp: no support for %d bit field", bits) - } +// NewServer constructs a Server instance for computing a shared secret. +func (s *SRP) NewServer(v *Verifier, A *big.Int) (*Server, error) { - s := &Server{ - g: pf.g, - xN: pf.N, - s: salt, - i: I, - v: big.NewInt(0).SetBytes(v), - n: bits / 8, + pf := s.pf + sx := &Server{ + s: s, + salt: v.s, + i: v.i, + v: big.NewInt(0).SetBytes(v.v), } // g, N := field(bits) @@ -454,48 +459,47 @@ func NewServer(I, salt, v []byte, A *big.Int, bits int) (*Server, error) { zero := big.NewInt(0) - b := randBigInt(bits) - k := hashint(s.xN.Bytes(), pad(s.g, s.n)) - t0 := big.NewInt(0).Mul(k, s.v) - B := big.NewInt(0).Add(t0, big.NewInt(0).Exp(s.g, b, s.xN)) + b := randBigInt(pf.n * 8) + k := s.hashint(pf.N.Bytes(), pad(pf.g, pf.n)) + t0 := big.NewInt(0).Mul(k, sx.v) + B := big.NewInt(0).Add(t0, big.NewInt(0).Exp(pf.g, b, pf.N)) - u := hashint(pad(A, s.n), pad(B, s.n)) + u := s.hashint(pad(A, pf.n), pad(B, pf.n)) if u.Cmp(zero) == 0 { return nil, fmt.Errorf("Invalid server public key u") } - t0 = big.NewInt(0).Mul(A, big.NewInt(0).Exp(s.v, u, s.xN)) - S := big.NewInt(0).Exp(t0, b, s.xN) + t0 = big.NewInt(0).Mul(A, big.NewInt(0).Exp(sx.v, u, pf.N)) + S := big.NewInt(0).Exp(t0, b, pf.N) - s.xB = B - s.xK = hashbyte(S.Bytes()) - s.xM = hashbyte(s.xK, A.Bytes(), B.Bytes(), I, salt, s.xN.Bytes(), s.g.Bytes()) + sx.xB = B + sx.xK = s.hashbyte(S.Bytes()) + sx.xM = s.hashbyte(sx.xK, A.Bytes(), B.Bytes(), v.i, v.s, pf.N.Bytes(), pf.g.Bytes()) //fmt.Printf("Server %d:\n\tv=%x\n\tk=%x\n\tA=%x\n\tS=%x\n\tK=%x\n\tM=%x\n", bits, v, k, A.Bytes(), S, s.xK, s.xM) - return s, nil + return sx, nil } // Credentials returns the server credentials (s,B) in a network portable // format. func (s *Server) Credentials() string { - s0 := hex.EncodeToString(s.s) + s0 := hex.EncodeToString(s.salt) s1 := hex.EncodeToString(s.xB.Bytes()) return s0 + ":" + s1 } // ClientOk verifies that the client has generated the same password as the // server and return proof that the server too has done the same. -func (s *Server) ClientOk(m string) (proof string, err error) { +func (s *Server) ClientOk(m string) (proof string, ok bool) { mym := hex.EncodeToString(s.xM) - if subtle.ConstantTimeCompare([]byte(mym), []byte(m)) != 1 { - return "", fmt.Errorf("Client failed to generate same password") + return "", false } - h := hashbyte(s.xK, s.xM) - return hex.EncodeToString(h), nil + h := s.s.hashbyte(s.xK, s.xM) + return hex.EncodeToString(h), true } // RawKey returns the raw key negotiated as part of the SRP @@ -505,8 +509,125 @@ func (s *Server) RawKey() []byte { // String represents the Server parameters as a string value func (s *Server) String() string { + pf := s.s.pf return fmt.Sprintf(" g=%d, N=%x\n I=%x\n s=%x\n B=%x\n K=%x\n", - s.g, s.xN, s.i, s.s, s.xB, s.xK) + pf.g, pf.N, s.i, s.salt, s.xB, s.xK) } -// - EOF - + +// hash byte stream and return as bytes +func (s *SRP) hashbyte(a ...[]byte) []byte { + h := s.h.New() + for _, z := range a { + h.Write(z) + } + return h.Sum(nil) +} + + +// hash a number of byte strings and return the resulting hash as +// bigint +func (s *SRP) hashint(a ...[]byte) *big.Int { + i := big.NewInt(0) + b := s.hashbyte(a...) + i.SetBytes(b) + return i +} + +func atoi(s string) int { + i, err := strconv.Atoi(s) + if err != nil { + panic(fmt.Sprintf("srp init: can't parse int %s", s)) + } + return i +} + +func atobi(s string, base int) *big.Int { + i, ok := big.NewInt(0).SetString(s, base) + if !ok { + panic(fmt.Sprintf("srp init: can't parse bigint |%s|", s)) + } + return i +} + +// pad x to n bytes if needed +func pad(x *big.Int, n int) []byte { + b := x.Bytes() + if len(b) < n { + z := n - len(b) + p := make([]byte, n, n) + for i := 0; i < z; i++ { + p[i] = 0 + } + + copy(p[z:], b) + b = p + } + return b +} + + +// Return n bytes of random bytes. Uses cryptographically strong +// random generator +func randbytes(n int) []byte { + b := make([]byte, n) + _, err := io.ReadFull(CR.Reader, b) + if err != nil { + panic("Random source is broken!") + } + return b +} + +// Generate and return a bigInt 'bits' bits in length +func randBigInt(bits int) *big.Int { + n := bits / 8 + if 0 == bits%8 { + n += 1 + } + b := randbytes(n) + r := big.NewInt(0).SetBytes(b) + return r +} + + +// build the database of prime fields and generators +func init() { + + pflist = make(map[int]*primeField) + lines := strings.Split(pflist_str, "\n") + for _, s := range lines { + v := strings.Split(s, ":") + b := atoi(v[0]) + + pf := &primeField{ + g: atobi(v[1], 10), + N: atobi(v[2], 0), + n: b / 8, + } + if 0 == big.NewInt(0).Cmp(pf.N) { + panic(fmt.Sprintf("srp init: N (%s) is zero", v[2])) + } + pflist[b] = pf + } +} + +// Map of bits to tuple +const pflist_str = `1024:2:0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3 +1536:2:0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB +2048:2:0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73 +3072:2:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF +4096:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF +6144:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF +8192:5:0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF` + +type primeField struct { + g *big.Int + N *big.Int + n int // size of N in bytes +} + +// prime field list - mapped by bit size; initialized via init() above. +var pflist map[int]*primeField + + +// vim: noexpandtab:sw=8:ts=8:tw=92: diff --git a/srp/srp_test.go b/srp_test.go similarity index 55% rename from srp/srp_test.go rename to srp_test.go index ebf8fd1..f29b8c3 100644 --- a/srp/srp_test.go +++ b/srp_test.go @@ -32,79 +32,109 @@ func newAsserter(t *testing.T) func(cond bool, msg string, args ...interface{}) } type user struct { - userhash []byte - salt, verifier []byte - p int + v *Verifier } type userdb struct { - p int - u []*user + s *SRP + u map[string]string } -func newUserDB(uname, pass []byte, p int) (*userdb, error) { +func newUserDB(user, pass []byte, p int) (*userdb, error) { - uh, s, v, err := Verifier(uname, pass, p) + s, err := New(p) if err != nil { return nil, err } - u := &user{userhash: uh, salt: s, verifier: v, p: p} - db := &userdb{p: p} + v, err := s.Verifier(user, pass) + if err != nil { + return nil, err + } + + ih, vh := v.Encode() - db.u = append(db.u, u) + db := &userdb{ + s: s, + u: make(map[string]string), + } + + db.u[ih] = vh return db, nil } // simulated user lookup -func (db *userdb) lookup(uhash []byte) (bool, *user) { - u := db.u[0] - return true, u +func (db *userdb) lookup(ih string) (bool, string) { + u, ok := db.u[ih] + return ok, u } func (db *userdb) verify(t *testing.T, user, pass []byte, goodPw bool) { assert := newAsserter(t) - // setup client session - c, err := NewClient(user, pass, db.p) - assert(err == nil, "expected err to be nil; saw %s", err) + s := db.s // SRP Instance + + // Start an SRP Client instance + c, err := s.NewClient(user, pass) + assert(err == nil, "NewClient: %s", err) + + // Credentials to send to server creds := c.Credentials() // this is what we send to server - ii, aa, err := ServerBegin(creds) - assert(err == nil, "expected err to be nil; saw %s", err) + // client --> sends 'creds' to server - // Begin a new server session by looking up DB using 'ii' as the - // key; fetch verifier (v), salt from the DB. - ok, u := db.lookup(ii) + // In actuality, this is done on the server. + ih, A, err := ServerBegin(creds) + assert(err == nil, "ServerBegin: %s", err) + + // Using identity 'ih', lookup the user-db and fetch the encoded verifier. + ok, vs := db.lookup(ih) assert(ok, "can't find user in db") - s, err := NewServer(ii, u.salt, u.verifier, aa, u.p) - assert(err == nil, "expected err to be nil; saw %s", err) + // On the server, we create the SRP instance afresh from the verifier. + s, v, err := MakeSRPVerifier(vs) + assert(err == nil, "SRPVerifier: %s", err) + + // create a SRP server instance using the verifier and public key + srv, err := s.NewServer(v, A) + assert(err == nil, "NewServer: %s", err) + + // Send the salt and Server public Key to client + s_creds := srv.Credentials() + + // Server --> sends 's_creds' to client - screds := s.Credentials() // send this to client. - mauth, err := c.Generate(screds) // send the mutual authenticator to server - assert(err == nil, "expected err to be nil; saw %s", err) + // Client generates a mutual auth and sends to server + mauth, err := c.Generate(s_creds) + assert(err == nil, "Client.Generate: %s", err) - proof, err := s.ClientOk(mauth) // confirm mutual authenticator and send proof of legit auth + // Client --> sends 'mauth' to server + + // Server validates the mutual authenticator and creates its proof of having derived + // the same key. This proof is sent to the client. + proof, ok := srv.ClientOk(mauth) if goodPw { - assert(err == nil, "server: bad client auth (%s)", err) + assert(ok, "server: bad client proof") } else { - assert(err != nil, "server: expected client to fail") + assert(!ok, "server: validated bad password") return } + + // Server --> sends 'proof' to client + // finally, the client should verify the server's proof - err = c.ServerOk(proof) - assert(err == nil, "client: bad server (%s)", err) + ok = c.ServerOk(proof) + assert(ok, "client: bad client proof") // both client and server are authenticated. Now, we generate a // mutual secret -- which should be identical kc := c.RawKey() - ks := s.RawKey() + ks := srv.RawKey() assert(1 == subtle.ConstantTimeCompare(kc, ks), "key mismatch;\nclient %x, server %x", kc, ks) }