summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsiddharth ravikumar <s@ricketyspace.net>2022-06-30 21:41:13 -0400
committersiddharth ravikumar <s@ricketyspace.net>2022-06-30 21:41:13 -0400
commita1ef92cc8c029b8380d0b87075d2a4472debca6a (patch)
tree35c438343c251aa0fc02a51995bddae61b22f2d0
parentc8dc45786cfb68434429e96b8b4eeb0d8481dce1 (diff)
challenge: c38: implement simplified srp
-rw-r--r--challenge/c38.go450
-rw-r--r--cryptopals.go2
2 files changed, 452 insertions, 0 deletions
diff --git a/challenge/c38.go b/challenge/c38.go
new file mode 100644
index 0000000..79681da
--- /dev/null
+++ b/challenge/c38.go
@@ -0,0 +1,450 @@
+// Copyright © 2022 siddharth ravikumar <s@ricketyspace.net>
+// SPDX-License-Identifier: ISC
+
+package challenge
+
+import (
+ "bufio"
+ "fmt"
+ "math/big"
+ "net"
+ "os"
+
+ "ricketyspace.net/cryptopals/lib"
+)
+
+func C38(args []string) {
+ if len(args) < 2 {
+ fmt.Println("Usage: cryptopals -c 36 [ client | server ] PORT")
+ return
+ }
+ entity := args[0]
+ port, err := lib.StrToNum(args[1])
+ if err != nil {
+ fmt.Println("port invalid")
+ return
+ }
+ if port < 12000 {
+ fmt.Println("port number must be >= 12000")
+ return
+ }
+
+ // Register user on the server.
+ serverRegisterUser := func(server *lib.SRPServer, info []string) error {
+ if len(info) != 5 {
+ return fmt.Errorf("regiser user: info valid")
+ }
+ n := info[0]
+ g := info[1]
+ k := info[2]
+ ident := info[3]
+ pass := info[4]
+ user, err := lib.NewSRPUser(n, g, k, ident, pass)
+ if err != nil {
+ return fmt.Errorf("register user: %v", err)
+ }
+ if err = server.RegisterUser(user); err != nil {
+ return fmt.Errorf("register user: %v", err)
+ }
+ return nil
+ }
+ // Login user on the server.
+ serverLoginUser := func(server *lib.SRPServer, info []string,
+ conn net.Conn) error {
+ if len(info) != 2 {
+ return fmt.Errorf("login user: info valid")
+ }
+ ident := info[0]
+ user, err := server.GetUser(ident)
+ if err != nil {
+ return fmt.Errorf("get user: %v", err)
+ }
+ if user.LoggedIn() {
+ return fmt.Errorf("user already has a session open")
+ }
+ clientPub := new(big.Int).SetBytes(lib.HexStrToBytes(info[1]))
+ if clientPub.Cmp(big.NewInt(0)) == 0 {
+ return fmt.Errorf("user public key invalid")
+ }
+
+ user.EphemeralKeyGen() // Generate server pub key for user.
+ serverPub, err := user.EphemeralKeyPubSimple()
+ if err != nil {
+ return fmt.Errorf("server pub key: %v", err)
+ }
+
+ // Generate scrambling param.
+ err = user.SetScramblingParamSimple()
+ if err != nil {
+ return fmt.Errorf("setting scrambling param: %v", err)
+ }
+ sParam := user.GetScramblingParam()
+
+ // Make ACK packet
+ packet := fmt.Sprintf("%s+%s+%s", lib.BytesToHexStr(user.Salt()),
+ lib.BytesToHexStr(serverPub.Bytes()),
+ lib.BytesToHexStr(sParam))
+
+ // Send packet to client.
+ _, err = fmt.Fprintf(conn, "%s\n", packet)
+ if err != nil {
+ return fmt.Errorf("sending packet to client: %v", err)
+ }
+
+ // Compute session key.
+ err = user.ComputeSessionKey(clientPub)
+ if err != nil {
+ return fmt.Errorf("computing session key: %v", err)
+ }
+
+ // Wait and try to read hmac from client.
+ cpacket, err := bufio.NewReader(conn).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("hmac recv: %v", err)
+ }
+ hmac := []byte(cpacket[:len(cpacket)-1])
+ if !user.SessionKeyMacVerify(hmac) {
+ return fmt.Errorf("hmac verification failed")
+ }
+ // Login user.
+ user.LogIn()
+
+ return nil
+ }
+ // Logout user on the server.
+ serverLogoutUser := func(server *lib.SRPServer, ident string,
+ conn net.Conn) error {
+ user, err := server.GetUser(ident)
+ if err != nil {
+ return fmt.Errorf("get user: %v", err)
+ }
+ if !user.LoggedIn() {
+ return fmt.Errorf("user not logged in")
+ }
+ // Logout user.
+ user.LogOut()
+ return nil
+ }
+ // Handle connection from a client.
+ serverHandleConn := func(server *lib.SRPServer, conn net.Conn) {
+ defer conn.Close()
+ fmt.Printf("Got connection from %v\n", conn.RemoteAddr())
+
+ // Read packet from client.
+ packet, err := bufio.NewReader(conn).ReadString('\n')
+ if err != nil {
+ fmt.Printf("Unable to read from client %v\n",
+ conn.RemoteAddr())
+ return
+ }
+
+ // Remove newline character from packet.
+ packet = packet[:len(packet)-1]
+
+ parts := lib.StrSplitAt('+', packet)
+ if len(parts) < 2 {
+ fmt.Fprintf(conn, "invalid request\n")
+ return
+ }
+
+ switch {
+ case parts[0] == "register":
+ err = serverRegisterUser(server, parts[1:])
+ if err != nil {
+ fmt.Fprintf(conn, "%v\n", err)
+ return
+ } else {
+ fmt.Fprintf(conn, "OK\n")
+ }
+ return
+ case parts[0] == "login":
+ err = serverLoginUser(server, parts[1:], conn)
+ if err != nil {
+ fmt.Fprintf(conn, "%v\n", err)
+ } else {
+ fmt.Fprintf(conn, "OK\n")
+ }
+ return
+ case parts[0] == "logout":
+ err = serverLogoutUser(server, parts[1], conn)
+ if err != nil {
+ fmt.Fprintf(conn, "%v\n", err)
+ } else {
+ fmt.Fprintf(conn, "OK\n")
+ }
+ return
+ default:
+ fmt.Fprintf(conn, "invalid action")
+ return
+ }
+
+ }
+ // Start SRP server.
+ serverSpawn := func() {
+ server := new(lib.SRPServer)
+
+ p := fmt.Sprintf(":%d", port)
+ ln, err := net.Listen("tcp", p)
+ if err != nil {
+ fmt.Printf("server listen error: %v\n", err)
+ return
+ }
+ for {
+ fmt.Println("Waiting for connection...")
+ conn, err := ln.Accept()
+ if err != nil {
+ fmt.Printf("server accept error: %v\n", err)
+ }
+ go serverHandleConn(server, conn)
+ }
+ }
+ // Register user with server.
+ clientRegisterUser := func(client *lib.SRPClient, ident string) error {
+ n := lib.StripSpaceChars(
+ `ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024
+ e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd
+ 3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec
+ 6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f
+ 24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361
+ c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552
+ bb9ed529077096966d670c354e4abc9804f1746c08ca237327fff
+ fffffffffffff`)
+ g := "2"
+ k := "3"
+
+ // Prompt for password.
+ fmt.Printf("password> ")
+ pass, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("unable to read password: %v", err)
+ }
+
+ // Create session for user.
+ client.Session, err = lib.NewSRPClientSession(n, g, k, ident)
+ if err != nil {
+ return fmt.Errorf("unable to create session: %v", err)
+ }
+
+ // Make SRP registration packet.
+ packet := fmt.Sprintf("%s+%s+%s+%s+%s+%s", "register",
+ n, g, k, ident, pass)
+
+ // Try to connect to server.
+ conn, err := net.Dial("tcp", fmt.Sprintf(":%d", port))
+ if err != nil {
+ return fmt.Errorf("unable connect to server: %v", err)
+ }
+ defer conn.Close()
+
+ // Send packet to server.
+ _, err = fmt.Fprintf(conn, "%s", packet)
+ if err != nil {
+ return fmt.Errorf("unable communicate with server: %v", err)
+ }
+
+ // Wait and try to get registration ACK from server.
+ spacket, err := bufio.NewReader(conn).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("server did not respond: %v", err)
+ }
+ // Remove newline character.
+ spacket = spacket[:len(spacket)-1]
+ if spacket != "OK" {
+ return fmt.Errorf("server registration failed: %s", spacket)
+ }
+ return nil
+ }
+ // Login user into the server.
+ clientLoginUser := func(client *lib.SRPClient, ident string) error {
+ n := lib.StripSpaceChars(
+ `ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024
+ e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd
+ 3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec
+ 6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f
+ 24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361
+ c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552
+ bb9ed529077096966d670c354e4abc9804f1746c08ca237327fff
+ fffffffffffff`)
+ g := "2"
+ k := "3"
+
+ // Prompt for password.
+ fmt.Printf("password> ")
+ pass, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("unable to read password: %v", err)
+ }
+ pass = pass[:len(pass)-1]
+
+ // Create session for user.
+ client.Session, err = lib.NewSRPClientSession(n, g, k, ident)
+ if err != nil {
+ return fmt.Errorf("unable to create session: %v", err)
+ }
+
+ // Get session pub key.
+ pub, err := client.Session.EphemeralKeyPub()
+ if err != nil {
+ return fmt.Errorf("unable to get pub key: %v", err)
+ }
+
+ // Make SRP login packet.
+ packet := fmt.Sprintf("%s+%s+%s", "login",
+ ident, lib.BytesToHexStr(pub.Bytes()))
+
+ // Try to connect to server.
+ conn, err := net.Dial("tcp", fmt.Sprintf(":%d", port))
+ if err != nil {
+ return fmt.Errorf("unable connect to server: %v", err)
+ }
+ defer conn.Close()
+
+ // Send login packet to server.
+ _, err = fmt.Fprintf(conn, "%s\n", packet)
+ if err != nil {
+ return fmt.Errorf("unable communicate with server: %v", err)
+ }
+
+ // Wait and try to get registration ACK from server.
+ spacket, err := bufio.NewReader(conn).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("server did not respond: %v", err)
+ }
+ // Remove newline character.
+ spacket = spacket[:len(spacket)-1]
+
+ if !lib.StrHas(spacket, "+") {
+ return fmt.Errorf("pub exchange: %s", spacket)
+ }
+ parts := lib.StrSplitAt('+', spacket)
+ if len(parts) < 3 {
+ return fmt.Errorf("server login response invalid")
+ }
+ salt := lib.HexStrToBytes(parts[0])
+ serverPub := new(big.Int).SetBytes(lib.HexStrToBytes(parts[1]))
+ sParam := lib.HexStrToBytes(parts[2])
+
+ // Compute session key.
+ err = client.Session.SetScramblingParamSimple(sParam)
+ if err != nil {
+ return fmt.Errorf("setting scrambling param: %v", err)
+ }
+ err = client.Session.ComputeSessionKeySimple(salt, pass, serverPub)
+ if err != nil {
+ return fmt.Errorf("computing session key: %v", err)
+ }
+
+ // Compute session key hmac
+ hmac, err := client.Session.SessionKeyMac(salt)
+ if err != nil {
+ return fmt.Errorf("sesion key hmac: %v", err)
+ }
+
+ // Send hmac to server.
+ _, err = fmt.Fprintf(conn, "%s\n", hmac)
+ if err != nil {
+ return fmt.Errorf("sending hmac: %v", err)
+ }
+
+ // Wait and try to get registration ACK from server.
+ spacket, err = bufio.NewReader(conn).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("server did not respond: %v", err)
+ }
+ // Remove newline character.
+ spacket = spacket[:len(spacket)-1]
+ if spacket != "OK" {
+ return fmt.Errorf("login failed: %s", spacket)
+ }
+ // Login user.
+ client.LogIn()
+ return nil
+ }
+ // Logout user.
+ clientLogoutUser := func(client *lib.SRPClient) error {
+ // Make logout packet.
+ packet := fmt.Sprintf("%s+%s", "logout", client.Ident())
+
+ // Try to connect to server.
+ conn, err := net.Dial("tcp", fmt.Sprintf(":%d", port))
+ if err != nil {
+ return fmt.Errorf("unable connect to server: %v", err)
+ }
+ defer conn.Close()
+
+ // Send login packet to server.
+ _, err = fmt.Fprintf(conn, "%s\n", packet)
+ if err != nil {
+ return fmt.Errorf("logout send: %v", err)
+ }
+
+ // Wait and try to get logout ACK from server.
+ spacket, err := bufio.NewReader(conn).ReadString('\n')
+ if err != nil {
+ return fmt.Errorf("logout recv: %v", err)
+ }
+ // Remove newline character.
+ spacket = spacket[:len(spacket)-1]
+ if spacket != "OK" {
+ return fmt.Errorf("logout ack: %s", spacket)
+ }
+
+ // Logout user.
+ client.Session = nil
+
+ return nil
+ }
+ // Start SRP client.
+ clientSpawn := func() {
+ client := new(lib.SRPClient)
+ // Enter repl.
+ for {
+ // Read message from stdin.
+ fmt.Printf("%s> ", client.Ident())
+ msg, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ if err != nil {
+ fmt.Printf("read error: %v\n", err)
+ return
+ }
+ // Remove newline character.
+ msg = msg[:len(msg)-1]
+
+ msg_parts := lib.StrSplitAt(' ', msg)
+ switch {
+ case !client.LoggedIn() && msg_parts[0] == "register" &&
+ len(msg_parts) == 2:
+ err := clientRegisterUser(client, msg_parts[1])
+ if err != nil {
+ fmt.Printf("Registration failed: %v\n", err)
+ } else {
+ fmt.Printf("Registered!\n")
+ }
+ case !client.LoggedIn() && msg_parts[0] == "login" &&
+ len(msg_parts) == 2:
+ err := clientLoginUser(client, msg_parts[1])
+ if err != nil {
+ fmt.Printf("Login failed: %v\n", err)
+ } else {
+ fmt.Printf("Logged in!\n")
+ }
+ case client.LoggedIn() && msg_parts[0] == "logout":
+ err := clientLogoutUser(client)
+ if err != nil {
+ fmt.Printf("Logout failed: %v\n", err)
+ } else {
+ fmt.Printf("Logged out!\n")
+ }
+ }
+ }
+ }
+
+ // Take action based on entity.
+ switch {
+ case entity == "server":
+ serverSpawn()
+ case entity == "client":
+ clientSpawn()
+ default:
+ fmt.Println("uknown entity")
+ }
+}
diff --git a/cryptopals.go b/cryptopals.go
index 0bb909b..82627e0 100644
--- a/cryptopals.go
+++ b/cryptopals.go
@@ -94,5 +94,7 @@ func main() {
challenge.C36(flag.Args())
case 37:
challenge.C37(flag.Args())
+ case 38:
+ challenge.C38(flag.Args())
}
}