{"id":13471941,"url":"https://github.com/opencoff/go-srp","last_synced_at":"2025-04-04T12:06:41.445Z","repository":{"id":31039773,"uuid":"34598396","full_name":"opencoff/go-srp","owner":"opencoff","description":"SRP implementation in pure golang","archived":false,"fork":false,"pushed_at":"2025-03-25T12:28:10.000Z","size":79,"stargazers_count":170,"open_issues_count":2,"forks_count":21,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-28T11:08:11.930Z","etag":null,"topics":["go","golang","secure-authentication","srp","srp-6a"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opencoff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-04-26T04:52:34.000Z","updated_at":"2025-03-27T16:56:26.000Z","dependencies_parsed_at":"2022-08-28T13:40:20.981Z","dependency_job_id":"442a63c5-bb67-4ae1-98e0-87f7ff3b7528","html_url":"https://github.com/opencoff/go-srp","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opencoff%2Fgo-srp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opencoff%2Fgo-srp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opencoff%2Fgo-srp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opencoff%2Fgo-srp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opencoff","download_url":"https://codeload.github.com/opencoff/go-srp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247174415,"owners_count":20896078,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","secure-authentication","srp","srp-6a"],"created_at":"2024-07-31T16:00:50.548Z","updated_at":"2025-04-04T12:06:41.426Z","avatar_url":"https://github.com/opencoff.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Standalone SRP-6a implementation in go-lang\n\n[![GoDoc](https://godoc.org/github.com/opencoff/go-srp?status.svg)](https://godoc.org/github.com/opencoff/go-srp)\n[![Go Report Card](https://goreportcard.com/badge/github.com/opencoff/go-srp)](https://goreportcard.com/report/github.com/opencoff/go-srp)\n\nThis is a standalone implementation of SRP in golang. It uses the go\nstandard libraries and has no other external dependencies. This library\ncan be used by SRP clients or servers.\n\nSRP is a protocol to authenticate a user and derive safe session keys.\nIt is the latest in the category of \\\"strong authentication protocols\\\".\n\nSRP is documented here: \u003chttp://srp.stanford.edu/doc.html\u003e. Briefly,\n\n## Design Overview\n\n### Conventions\n\n    N    A large safe prime (N = 2q+1, where q is prime)\n       All arithmetic is done modulo N.\n    g    A generator modulo N\n    k    Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)\n    s    User's salt\n    I    Username\n    p    Cleartext Password\n    H()  One-way hash function\n    ^    (Modular) Exponentiation\n    u    Random scrambling parameter\n    a,b  Secret ephemeral values\n    A,B  Public ephemeral values\n    x    Private key (derived from p and s)\n    v    Password verifier\n\n### Differences from SRP-6a and RFC 5054\nWe differ from the SRP-6a spec and RFC 5054 in a couple of key ways:\n\n* We hash the identity `I`; this provides some (minimal) protection against\n  dictionary attacks on the username.\n* We hash the user passphrase `p`; this expands shorter passphrase\n  into longer ones and extends the alphabet used in the passphrase.\n* We differ from RFC 5054 in our choice of hash function; we use Blake-2b.\n  SHA-1 is getting long in the tooth, Blake2b is the current\n  state-of-the art. Equivalently, one may use SHA3 (see below for\n  using a user supplied hash function).\n\n### Generating and Storing the Password Verifier\nThe host calculates the password verifier using the following formula:\n\n    s = randomsalt()          (same length as N)\n    I = H(I)\n    p = H(p)                  (hash/expand I \u0026 p)\n    t = H(I, \":\", p)\n    x = H(s, t)\n    v = g^x                   (computes password verifier)\n\nThe host then stores {I, s, v} in its password database - such that\nthe triple can be retrieved by using `I` as the index/key.\n\n### Authentication Protocol\nThe authentication protocol itself goes as follows:\n\n    Client                       Server\n    --------------               ----------------\n    un, pw = \u003c user input \u003e\n    I = H(un)\n    p = H(pw)\n    a = random()\n    A = g^a % N\n                I, A --\u003e\n                              s, v = lookup(I)\n                              b = random()\n                              B = (kv + g^b) % N\n                              u = H(A, B)\n                              S = ((A * v^u) ^ b) % N\n                              K = H(S)\n                              M' = H(K, A, B, I, s, N, g)\n                 \u003c-- s, B\n    u = H(A, B)\n    x = H(s, p)\n    S = ((B - k (g^x)) ^ (a + ux)) % N\n    K = H(S)\n    M = H(K, A, B, I, s, N, g)\n\n                M --\u003e\n                              M must be equal to M'\n                              Z = H(M, K)\n                \u003c-- Z\n\n    Z' = H(M, K)\n    Z' must equal Z\n\nWhen the server receives `\u003cI, A\u003e`, it can compute everything: shared key\nand proof-of-generation `M'`. The shared key is `K`.\n\nTo verify that the client has generated the same key `K`, the client sends\n`M` -- a hash of all the data it has and it received from the server. To\nvalidate that the server also has the same value, it requires the server to send\nits own proof. In the SRP paper, the authors use:\n\n    M = H(H(N) xor H(g), H(I), s, A, B, K)\n    M' = H(A, M, K)\n\nWe use a simpler construction:\n\n    M = H(K, A, B, I, s, N, g)\n    M' = H(M, K)\n\nThe two parties also employ the following safeguards:\n\n 1. The user will abort if he receives `B == 0 (mod N) or u == 0`.\n 2. The host will abort if it detects that `A == 0 (mod N)`.\n 3. The user must show his proof of K first. If the server detects that the\n    user\\'s proof is incorrect, it must abort without showing its own proof of K.\n\n\n## Implementation Notes\nIn our implementation:\n\n- The standard hash function is Blake2b-256; this can be changed by choosing an\n  appropriate hash from `crypto`:\n  ```go\n\n       s, err := srp.NewWithHash(crypto.SHA256, 4096)\n  ```\n\n\n### Setting up the Verifiers on the Server\nIn order to authenticate and derive session keys, verifiers must be\nstored in a non-volatile medium on the server. The client provides the\nprime-field size, username and password when creating the verifier. The\nserver stores the triple in a non-volatile medium. The verifiers are\ngenerated *once* when a user is created on the server.\n\nThe Client is the entity where the user enters their password and wishes\nto be authenticated with a SRP server. The communication between client\nand server can happen in clear text - SRP is immune to man in the middle\nattacks.\n\nDepending on the resources available on a given client, it can choose a\nsmall or large prime-field; but once chosen it is recorded on the server\nuntil a new verifier is generated.\n\nFor example, a client will do:\n\n```go\n\n    s, err := srp.New(n_bits)\n\n    v, err := s.Verifier(username, password)\n    id, verif := v.Encode()\n\n    // Now, store 'id', 'verif' in non-volatile storage such that 'verif' can be\n    // retrieved by providing 'id'.\n```\nNote that `id` is the hashed identity string for username. The server should store\nthe encoded verifier string `verif` in a DB such that it can be looked up using `id`\nas the key.\n\n### Changing the default hash function\nA client may wish to change the default hash function to something else. e.g.,::\n\n```go\n\n    s, err := srp.NewWithHash(crypto.SHA256, n_bits)\n\n    v, err := s.Verifier(username, password)\n    id, verif := v.Encode()\n```\n\n### Authentication attempt from the Client\nThe client performs the following sequence of steps to authenticate and\nderive session keys:\n\n```go\n\n    s, err := srp.New(n_bits)\n\n    c, err := s.NewClient(user, pass)\n    creds := c.Credentials()\n\n    // 1. send the credentials to the server. It is already in ASCII string form; this\n    //    is essentially the encoded form of identity and a random public key.\n\n    // 2. Receive the server credentials into 'server_creds'; this is the server\n    //    public key and random salt generated when the verifier was created.\n\n    // It is assumed that there is some network communication that happens\n    // to get this string from the server.\n\n    // Now, generate a mutual authenticator to be sent to the server\n    auth, err := c.Generate(server_creds)\n\n    // 3. Send the mutual authenticator to the server\n    // 4. receive \"proof\" that the server too computed the same result.\n\n    // Verify that the server actually did what it claims\n    if !c.ServerOk(proof) {\n        panic(\"authentication failed\")\n    }\n\n    // Generate session key\n    rawkey := c.RawKey()\n```\n\n### Authenticating a Client on the Server\nOn the server, the authentication attempt begins after receiving the\ninitial user credentials. This is used to lookup the stored verifier and\nother bits.:\n\n```go\n\n    // Assume that we received the user credentials via the network into 'creds'\n\n\n    // Parse the user info and authenticator from the 'creds' string\n    id, A, err := srp.ServerBegin(creds)\n\n    // Use 'id' to lookup the user in some non-volatile DB and obtain\n    // previously stored *encoded* verifier 'v'.\n    verifier := db.Lookup(id)\n\n\n    // Create an SRP instance and Verifier instance from the stored data.\n    s, v, err := srp.MakeSRPVerifier(verifier)\n\n    // Begin a new client-server SRP session using the verifier and received\n    // public key.\n    srv, err := s.NewServer(v, A)\n\n    // Generate server credentials to send to the user\n    s_creds := srv.Credentials()\n\n    // 1. send 's_creds' to the client\n    // 2. receive 'm_auth' from the client\n\n    // Authenticate user and generate mutual proof of authentication\n    proof, ok := srv.ClientOk(m_auth)\n    if ok != nil {\n         panic(\"Authentication failed\")\n    }\n\n    // 3. Send proof to client\n\n    // Auth succeeded, derive session key\n    rawkey := s.RawKey()\n```\n\n### Generating new Safe Primes \u0026 Prime Field Generators\nThe SRP library uses a pre-calculated list of large safe prime for common widths\nalong wit their field generators. But, this is not advisable for large scale \nproduction use. It is best that a separate background process be used to generate\nsafe primes \u0026 the corresponding field generators - and store them in some cache.\nThe function `findPrimeField()` can be modified to fetch from this cache. Depending \non the security stance, the cache can decide on a \"use once\" policy or\n\"use N times\" policy.  \n\nThe function `srp.NewPrimeField()` generates and returns a new large safe prime\nand its field generator.\n\n### Building SRP\n\nThere is an example program that shows you the API usage (documented\nabove).:\n\n```sh\n    $ git clone https://github.com/opencoff/go-srp\n    $ cd go-srp\n    $ go test -v\n```\n\nFinally, build the example program:\n\n```sh\n    $ go build -o ex example/example.go\n    $ ./ex\n```\n\nThe example program outputs the raw-key from the client \u0026 server\\'s\nperspective (they should be identical).\n\nThere is also a companion program in the example directory that generates prime fields\nof a given size:\n\n```sh\n    $ go build -o pf example/primefield.go\n    $ ./pf 1024\n```\n\nThe above program can be run to generate multiple fields on the command line:\n```sh\n    $ ./pf 1024 2048 4096 8192\n```\n\nThe library uses `go modules`; so, it should be straight forward to import and use.\n\n\n### Using the SRP Raw key to derive session keys\nThe client and server both derive the same value for RawKey(). This\nis the crux of the SRP protocol. Treat this as a \\\"master key\\\".\nIt is not advisable to use the RawKey() for encryption purposes. It\nis better to derive a separate key for each direction\n(client-\\\u003eserver and server-\\\u003eclient). e.g.,:\n\n```go\n    c2s_k = KDF(rawkey, counter, \"C2S\")\n    s2s_k = KDF(rawkey, counter, \"S2C\")\n```\n\nKDF above can be a reputable key derivation function such as PBKDF2\nor Scrypt. The \\\"counter\\\" is incremented every time you derive a\nnew key.\n\n*I am not a cryptographer*. Please consult your favorite crypto book\nfor deriving encryption keys from a master key.\n\n#### Using `scrypt` as the KDF\nHere is a example KDF using `scrypt`:\n\n```go\n    import \"golang.org/x/crypto/scrypt\"\n\n    // Safe values for Scrypt() parameters\n    const _N int = 65536\n    const _r int = 1024\n    const _p int = 64\n\n    // Kdf derives a 'sz' byte key for use 'usage'\n    func Kdf(key []byte, salt []byte, usage string, sz int) []byte {\n\n        u0 := []byte(usage)\n        pw := append(key, u0...)\n        k, _ := scrypt.Key(pw, salt, _N, _r, _p, sz)\n        return k\n    }\n```\n\n#### Using `argon2` as the KDF\n[Argon](https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03) is\nthe new state of the art (2018) key derivation algorithm. The\n`Argon2id` variant is resistant to timing, side-channel and\nTime-memory tradeoff attacks. Here is an example using the `Argon2id`\nvariant:\n\n```go\n    import (\n        \"runtime\"\n        \"golang.org/x/crypto/argon2\"\n    )\n\n    // safe values for IDKey() borrowed from libsodium\n    const _Time uint32 = 3\n    const _Mem  uint32 = 256 * 1048576  // 256 MB\n\n    // Kdf derives a 'sz' byte key for use 'usage'\n    func Kdf(key, salt []byte, usage string, sz int) []byte {\n        u0 := []byte(usage)\n        pw := append(key, u0...)\n\n        return argon2.IDKey(pw, salt, _Time, _Mem, runtime.NumCPU(), uint32(sz))\n    }\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopencoff%2Fgo-srp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopencoff%2Fgo-srp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopencoff%2Fgo-srp/lists"}