Skip to content

Commit

Permalink
reworked example
Browse files Browse the repository at this point in the history
This will probably replace the existing exampler
  • Loading branch information
jpgoldberg committed Nov 13, 2017
1 parent e29d473 commit cab7e3e
Showing 1 changed file with 154 additions and 0 deletions.
154 changes: 154 additions & 0 deletions example_sharedkey_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package srp

import (
"crypto/aes"
"crypto/cipher"
rand "crypto/rand"
"fmt"
"log"
"math/big"
)

// ExampleServerClientKey is an example
func ExampleServerClientKey() {

// This example has both a server and the corresponding client live
// in the same function. That is not something you would normally do.
// Normally, you would be running one side (client or server) only.
// If I understand channels better, I could probably set up a more
// realistic example.

var err error
var A, B *big.Int

// On first encounter between client and server, they will negotatiate
// an SRP group to use. We will assume that they have settled on
// RFC5054Group4096

group := RFC5054Group4096

// The client will need a password from the user and will also need
// a salt.

pw := "Fido1961!" // It's the "!" that makes this password super secure

// Generate 8 bytes of random salt. Be sure to use crypto/rand for all
// of your random number needs
salt := make([]byte, 8)
rand.Read(salt)

username := "fred@fred.example"

// You would use a better Key Derivation Function than this one
x := KDFRFC5054(salt, username, pw) // Really. Don't use this KDF

// this is still our first use scenario, but the client needs to create
// an SRP client to generate the verifier.
firstClient := NewSRPClient(KnownGroups[group], x, nil)
if firstClient == nil {
fmt.Println("couldn't setup client")
}
v, err := firstClient.Verifier()
if err != nil {
fmt.Println(err)
}

// Now the client has all it needs to enroll with the server.
// Client sends salt, username, and v to the server

// Server will store long term the salt, username, an identifier for the SRP group
// used and v. It should store v securely.

// Some time later, we actually want to authenticate with this stuff

// Client and server may talk. Depending on what the client has locally,
// The client may need to be told it's salt, and the SRP group to use
// But here we will assume that that the client knows this, and already has
// computed x.

client := NewSRPClient(KnownGroups[group], x, nil)

// The client will need to send its ephemeral public key to the server
// so we fetch that now.
A = client.EphemeralPublic()

// Now it is time for some stuff (though not much) on the server.
server := NewSRPServer(KnownGroups[group], v, nil)
if server == nil {
fmt.Println("Couldn't set up server")
}

// The server will get A (clients ephemeral public key) from the client
// which the server will set using SetOthersPublic

// Server MUST check error status here as defense against
// a malicious A sent by client.
if err = server.SetOthersPublic(A); err != nil {
fmt.Println(err)
log.Fatal(err)
}

// server sends its ephemeral public key, B, to client
// client sets it as others public key.
if B = server.EphemeralPublic(); B == nil {
fmt.Println("server couldn't make B")
}

// server can now make the key.
serverKey, err := server.Key()
if err != nil || serverKey == nil {
fmt.Printf("something went wrong making server key: %s\n", err)
}

// Once the client receives B from the server it can set it.
// Client should check error status here as defense against
// a malicious B sent from server
if err = client.SetOthersPublic(B); err != nil {
fmt.Println(err)
log.Fatal(err)
}

// client can now make the session key
clientKey, err := client.Key()
if err != nil || clientKey == nil {
fmt.Printf("something went wrong making server key: %s", err)
}

// In normal usage, server and client would send challenges to prove
// that each know the same key.

// Once you have confirmed that client and server are using the same key
// (thus proving that x and v have the right relation to each other)
// we can use that key to encrypt stuff.

// Let's have it be a missive from the server to the client

// server sets up a block cipher with the key
serverBlock, _ := aes.NewCipher(serverKey) // set with server's key
serverCryptor, _ := cipher.NewGCM(serverBlock)

// We will use GCM with a 12 byte nonce for this example
// NEVER use the same nonce twice.
nonce := make([]byte, 12)
rand.Read(nonce)

plaintext := []byte("Hi client! Will you be my Valintine?")
ciphertext := serverCryptor.Seal(nil, nonce, plaintext, nil)

// Server sends the the ciphertext and the nonce to the client
// Client sets up its cryptor with its key
clientBlock, _ := aes.NewCipher(clientKey) // with the Client's key
clientCryptor, _ := cipher.NewGCM(clientBlock)

message, err := clientCryptor.Open(nil, nonce, ciphertext, nil)
if err != nil {
fmt.Printf("Decryption failed: %s", err)
log.Fatal(err)
// if decryption fails, do not trust anything about ciphertext
}

// If the message is successfully decrypted, then client and server
// can talk to each other using the key they derived
fmt.Printf("%s\n", message)
// Output: Hi client! Will you be my Valintine?
}

0 comments on commit cab7e3e

Please sign in to comment.