summaryrefslogtreecommitdiffstats
path: root/challenge/c37.go
diff options
context:
space:
mode:
Diffstat (limited to 'challenge/c37.go')
-rw-r--r--challenge/c37.go554
1 files changed, 554 insertions, 0 deletions
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 <s@ricketyspace.net>
+// 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