From 400b1906499083e1cac0ce2432ff66b69b925b2f Mon Sep 17 00:00:00 2001 From: siddharth ravikumar Date: Mon, 30 May 2022 15:12:30 -0400 Subject: challenge: do challenge 37 --- challenge/c37.go | 554 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 challenge/c37.go diff --git a/challenge/c37.go b/challenge/c37.go new file mode 100644 index 0000000..4ff10e8 --- /dev/null +++ b/challenge/c37.go @@ -0,0 +1,554 @@ +// Copyright © 2022 siddharth ravikumar +// SPDX-License-Identifier: ISC + +package challenge + +import ( + "bufio" + "fmt" + "math/big" + "net" + "os" + + "ricketyspace.net/cryptopals/lib" +) + +func C37(args []string) { + usage := func() { + fmt.Println("Usage: cryptopals -c 37 [ client | server ] PORT [ PUBKEY ]") + fmt.Println("\n PUBKEY is required for the client. Valid values are") + fmt.Println(" 0, N1, N2, N3, N4, etc.") + } + if len(args) < 2 { + usage() + 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 + } + + // SRP params. + paramN := lib.StripSpaceChars( + `ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024 + e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd + 3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec + 6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f + 24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361 + c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552 + bb9ed529077096966d670c354e4abc9804f1746c08ca237327fff + fffffffffffff`) + paramG := "2" + paramK := "3" + + // Process client's pub key if this is a client. + clientPubKey := []byte{} + if entity == "client" { + if len(args) != 3 { + usage() + return + } + pub := args[2] + // Validate pub key. + switch { + case pub[0] == '0': + clientPubKey = []byte{0} + case len(pub) > 1 && pub[0] == 'N': + pow, err := lib.StrToNum(pub[1:]) + if err != nil { + fmt.Printf("error: public key invalid\n") + return + } + pubi, ok := new(big.Int).SetString( + lib.StripSpaceChars(paramN), + 16, + ) + if !ok { + fmt.Errorf("unable to process pub key") + return + } + if pow == 1 { + clientPubKey = pubi.Bytes() + pubi = pubi.Exp(pubi, big.NewInt(int64(pow)), pubi) + } else { + pubi = pubi.Exp(pubi, big.NewInt(int64(pow)), pubi) + if pubi.Cmp(big.NewInt(0)) == 0 { + clientPubKey = []byte{0} + } else { + clientPubKey = pubi.Bytes() + } + } + default: + usage() + return + } + } + + // Zero key. + zeroSessionKey := func() []byte { + // SHA256 of empty string. + sha256 := lib.Sha256{} + sha256.Init([]uint32{}) + sha256.Message([]byte{}) + return sha256.Hash() + } + // 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 invalid") + } + 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])) + + user.EphemeralKeyGen() // Generate server pub key for user. + serverPub, err := user.EphemeralKeyPub() + if err != nil { + return fmt.Errorf("server pub key: %v", err) + } + + // Make ACK packet + packet := fmt.Sprintf("%s+%s", lib.BytesToHexStr(user.Salt()), + lib.BytesToHexStr(serverPub.Bytes())) + + // 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.SetScramblingParam(clientPub) + if err != nil { + return fmt.Errorf("setting scrambling param: %v", err) + } + 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 := paramN + g := paramG + k := paramK + + // 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 := paramN + g := paramG + k := paramK + + // 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) + } + + // Set public key to N^t where t is 0, 1, 2, etc. + pub := clientPubKey + + // Make SRP login packet. + packet := fmt.Sprintf("%s+%s+%s", "login", + ident, lib.BytesToHexStr(pub)) + + // 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) < 2 { + return fmt.Errorf("server login response invalid") + } + salt := lib.HexStrToBytes(parts[0]) + serverPub := new(big.Int).SetBytes(lib.HexStrToBytes(parts[1])) + + // Compute session key. + err = client.Session.SetScramblingParam(serverPub) + if err != nil { + return fmt.Errorf("setting scrambling param: %v", err) + } + err = client.Session.ComputeSessionKey(salt, pass, serverPub) + if err != nil { + return fmt.Errorf("computing session key: %v", err) + } + + // Set to "zero" session key. Since the client's + // public key is a multiple of N, the session key will + // always be zero. + client.Session.SetSessionKey(zeroSessionKey()) + + // Compute session key hmac + hmac, err := client.Session.SessionKeyMac(salt) + if err != nil { + return fmt.Errorf("session 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") + } +} + +// Output: +// +// https://ricketyspace.net/cryptopals/c37.webm +// +// $ ./cryptopals -c 37 server 12000 +// Waiting for connection... +// Waiting for connection... +// Got connection from 127.0.0.1:25923 +// Waiting for connection... +// Got connection from 127.0.0.1:47437 +// Waiting for connection... +// Got connection from 127.0.0.1:47489 +// Waiting for connection... +// Got connection from 127.0.0.1:17179 +// Waiting for connection... +// Got connection from 127.0.0.1:24965 +// Waiting for connection... +// Got connection from 127.0.0.1:26381 +// Waiting for connection... +// Got connection from 127.0.0.1:4572 +// Waiting for connection... +// Got connection from 127.0.0.1:47202 +// Waiting for connection... +// Got connection from 127.0.0.1:42154 +// +// $ ./cryptopals -c 37 client 12000 0 +// > register bob +// password> theonjoy +// Registered! +// > login bob +// password> +// Logged in! +// bob> logout +// Logged out! +// > ^C +// ada$ ./cryptopals -c 37 client 12000 N1 +// > login bob +// password> +// Logged in! +// bob> logout +// Logged out! +// > ^C +// ada$ ./cryptopals -c 37 client 12000 N2 +// > login bob +// password> +// Logged in! +// bob> logout +// Logged out! +// > ^C +// ada$ ./cryptopals -c 37 client 12000 N9 +// > login bob +// password> +// Logged in! +// bob> logout +// Logged out! +// > ^C -- cgit v1.2.3