sb-auth 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Richard Pham
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sb_auth-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: sb_auth
3
+ Version: 0.0.1
4
+ Summary: An implementation of a WebSocket authentication scheme, unsecured ws://
5
+ Home-page: https://github.com/Changissnz/sb_auth
6
+ Author: Richard Pham
7
+ Author-email: Richard Pham <phamrichard45@gmail.com>
8
+ License-Expression: MIT
9
+ Project-URL: Homepage, https://github.com/changissnz/sb_auth
10
+ Project-URL: Issues, https://github.com/changissnz/sb_auth/issues
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE.txt
16
+ Requires-Dist: numpy>=1.22.1
17
+ Requires-Dist: matplotlib>=3.5.3
18
+ Requires-Dist: scipy>=1.14.1
19
+ Requires-Dist: morebs2>=0.1.94
20
+ Requires-Dist: python-resources>=0.3
21
+ Requires-Dist: seqbuild>=0.0.49
22
+ Dynamic: author
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+
26
+ # sb_auth (SeqBuild Authenticator)
27
+
28
+ *Work in progress*
29
+
30
+ A WebSocket authentication schematic b/t two devices, both using the `Seqbuild` library.
31
+
32
+ Simple client-server layout. Authentication allows for file read/write operations to take
33
+ place between the two devices.
34
+
35
+ *NOTE*
36
+ The current websocket implementation uses the unsecured `ws://` protocol instead of the
37
+ secured `wss://` one. The development of this authenticator has not advanced into
38
+ production-level mode.
39
+
40
+ This codebase works for local WiFi networks but otherwise, more code with third-party
41
+ services must be used for secured communication.
42
+
43
+ ## Description
44
+
45
+ There are three perspectives for use:
46
+ - local (server or client side): offline mode, used to modify read/write settings and delete bunk
47
+ Comm Lang key files.
48
+ - server: can serve an unspecified number of clients. Every authenticated client has read/write access to permitted files/folders.
49
+ - client: can access one server at a time to read/write permitted files.
50
+
51
+ The procedure in which a client interacts with a server goes so:
52
+ 1. Client sends IP address and port number (default is 8765, variable<DEFAULT_PORT>), and
53
+ connects to the server.
54
+ 2. Client inputs username. If server has no registered username, it sends client a new
55
+ Comm Lang file key. This is a script, containing a primary generator pseudo-random number
56
+ generator G, that can be executed by `seqbuild`'s Comm Lang interpreter. Client stores this
57
+ Comm Lang file key and sets the number of past iterations `i_p` to 0.
58
+ 3. Client is required to output `q` integers from generator G, `q` specified by the server and
59
+ in the range of `DEFAULT_SB_AUTH_KEYSIZE_RANGE`, default set to `[24,58]`, every time client
60
+ prompts to read/write a file.
61
+ 4. When the number of past iterations `i_p` of G passes a number `q2`, specified by the server
62
+ and in the range of `DEFAULT_SB_AUTH_INDEX_RANGE`, default set to `[627,1450)`, server sends
63
+ client a new Comm Lang file key.
64
+
65
+ Server does not require any user input. Client requires user to input IP address/port, as well
66
+ as for read/write operations to permitted files.
67
+
68
+ On the server side, every username it has stored in its client directory is associated with a
69
+ file for file+folder exclusions. These exclusions prohibit the client from read/write operations
70
+ concerning them.
71
+
72
+ ## Default File Naming
73
+
74
+ - Server Side:
75
+ - username directory file is @ `user_data/server_dir`.
76
+ - directory file stores Comm Lang key file names and generator index (number of past iterations)
77
+ for every username.
78
+ - directory file column layout is
79
+ - username
80
+ - Comm Lang key filepath
81
+ - primary generator name
82
+ - generator index
83
+ - number of sessions key has been used
84
+ - every username `U` has a Comm Lang file key @ `user_data/commond_U.txt`.
85
+ - default permissions file is @ `user_data/default_user_permissions.txt`.
86
+ - every username `U` has an exclusion file @ `user_data/permissions_U.txt`.
87
+
88
+ - Client Side:
89
+ - username directory file is @ `user_data/client_dir`.
90
+ - column layout is virtually identical to that of server side.
91
+ - every server of IP address `I` and port `Q` is assigned a Comm Lang key file.
92
+
93
+ ## The Identity Aspect
94
+
95
+ Websocket has the problem of authentication. If a username's Comm Lang file key for a
96
+ server is shared between `> 1` devices, those devices can all access the server under
97
+ the same username.
98
+
99
+ For a server with a fixed IP address and port combination, a device can only access it
100
+ through one username, given how the file naming convention goes.
101
+
102
+ ## Technicals Behind the Generator Key
103
+
104
+ The program used to generator Comm Lang key files is `seqbuild` @
105
+ [Seqbuild](https://www.github.com/changissnz/seqbuild). Specifically, the Comm Lang file
106
+ generator is @ the file `face/easy_gen_struct.py`. There may be instances where the key
107
+ may be a generator such that two different devices output different sequences of integers.
108
+ So the Comm Lang key file generator is not guaranteed to be stable.
109
+
110
+ ## Usage
111
+
112
+ Go into this directory, and run this script:
113
+
114
+ ```
115
+ from sb_auth.exec import *
116
+ ```
@@ -0,0 +1,91 @@
1
+ # sb_auth (SeqBuild Authenticator)
2
+
3
+ *Work in progress*
4
+
5
+ A WebSocket authentication schematic b/t two devices, both using the `Seqbuild` library.
6
+
7
+ Simple client-server layout. Authentication allows for file read/write operations to take
8
+ place between the two devices.
9
+
10
+ *NOTE*
11
+ The current websocket implementation uses the unsecured `ws://` protocol instead of the
12
+ secured `wss://` one. The development of this authenticator has not advanced into
13
+ production-level mode.
14
+
15
+ This codebase works for local WiFi networks but otherwise, more code with third-party
16
+ services must be used for secured communication.
17
+
18
+ ## Description
19
+
20
+ There are three perspectives for use:
21
+ - local (server or client side): offline mode, used to modify read/write settings and delete bunk
22
+ Comm Lang key files.
23
+ - server: can serve an unspecified number of clients. Every authenticated client has read/write access to permitted files/folders.
24
+ - client: can access one server at a time to read/write permitted files.
25
+
26
+ The procedure in which a client interacts with a server goes so:
27
+ 1. Client sends IP address and port number (default is 8765, variable<DEFAULT_PORT>), and
28
+ connects to the server.
29
+ 2. Client inputs username. If server has no registered username, it sends client a new
30
+ Comm Lang file key. This is a script, containing a primary generator pseudo-random number
31
+ generator G, that can be executed by `seqbuild`'s Comm Lang interpreter. Client stores this
32
+ Comm Lang file key and sets the number of past iterations `i_p` to 0.
33
+ 3. Client is required to output `q` integers from generator G, `q` specified by the server and
34
+ in the range of `DEFAULT_SB_AUTH_KEYSIZE_RANGE`, default set to `[24,58]`, every time client
35
+ prompts to read/write a file.
36
+ 4. When the number of past iterations `i_p` of G passes a number `q2`, specified by the server
37
+ and in the range of `DEFAULT_SB_AUTH_INDEX_RANGE`, default set to `[627,1450)`, server sends
38
+ client a new Comm Lang file key.
39
+
40
+ Server does not require any user input. Client requires user to input IP address/port, as well
41
+ as for read/write operations to permitted files.
42
+
43
+ On the server side, every username it has stored in its client directory is associated with a
44
+ file for file+folder exclusions. These exclusions prohibit the client from read/write operations
45
+ concerning them.
46
+
47
+ ## Default File Naming
48
+
49
+ - Server Side:
50
+ - username directory file is @ `user_data/server_dir`.
51
+ - directory file stores Comm Lang key file names and generator index (number of past iterations)
52
+ for every username.
53
+ - directory file column layout is
54
+ - username
55
+ - Comm Lang key filepath
56
+ - primary generator name
57
+ - generator index
58
+ - number of sessions key has been used
59
+ - every username `U` has a Comm Lang file key @ `user_data/commond_U.txt`.
60
+ - default permissions file is @ `user_data/default_user_permissions.txt`.
61
+ - every username `U` has an exclusion file @ `user_data/permissions_U.txt`.
62
+
63
+ - Client Side:
64
+ - username directory file is @ `user_data/client_dir`.
65
+ - column layout is virtually identical to that of server side.
66
+ - every server of IP address `I` and port `Q` is assigned a Comm Lang key file.
67
+
68
+ ## The Identity Aspect
69
+
70
+ Websocket has the problem of authentication. If a username's Comm Lang file key for a
71
+ server is shared between `> 1` devices, those devices can all access the server under
72
+ the same username.
73
+
74
+ For a server with a fixed IP address and port combination, a device can only access it
75
+ through one username, given how the file naming convention goes.
76
+
77
+ ## Technicals Behind the Generator Key
78
+
79
+ The program used to generator Comm Lang key files is `seqbuild` @
80
+ [Seqbuild](https://www.github.com/changissnz/seqbuild). Specifically, the Comm Lang file
81
+ generator is @ the file `face/easy_gen_struct.py`. There may be instances where the key
82
+ may be a generator such that two different devices output different sequences of integers.
83
+ So the Comm Lang key file generator is not guaranteed to be stable.
84
+
85
+ ## Usage
86
+
87
+ Go into this directory, and run this script:
88
+
89
+ ```
90
+ from sb_auth.exec import *
91
+ ```
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "sb_auth"
3
+ version = "0.0.1"
4
+ dependencies = [
5
+ "numpy>=1.22.1",
6
+ "matplotlib>=3.5.3",
7
+ "scipy>=1.14.1",
8
+ "morebs2>=0.1.94",
9
+ "python-resources>=0.3",
10
+ "seqbuild>=0.0.49",
11
+ ]
12
+ authors = [
13
+ { name="Richard Pham", email="phamrichard45@gmail.com" },
14
+ ]
15
+ description = 'An implementation of a WebSocket authentication scheme, unsecured ws://'
16
+ readme = "README.md"
17
+ requires-python = ">=3.9"
18
+ classifiers = [
19
+ "Programming Language :: Python :: 3",
20
+ "Operating System :: OS Independent",
21
+ ]
22
+ license = "MIT"
23
+ license-files = ["LICEN[CS]E*"]
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["."]
27
+ include = ["sb_auth*"]
28
+
29
+ [tool.setuptools.package-data]
30
+ "seqbuild.face.sample_script" = ["*"]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/changissnz/sb_auth"
34
+ Issues = "https://github.com/changissnz/sb_auth/issues"
35
+
File without changes
@@ -0,0 +1,29 @@
1
+ from .sb_local import *
2
+ from .sb_client import *
3
+ from .sb_serve import *
4
+
5
+ while True:
6
+ s = input("(s) server (c) client (l) local[offline]: ")
7
+ s = s.strip().lower()
8
+
9
+ if s in {"s","c","l"}:
10
+ break
11
+
12
+ if s == "s":
13
+ sbs = SBAuthServer()
14
+ asyncio.run(sbs.service())
15
+
16
+ elif s == "c":
17
+ sbc = SBAuthClient()
18
+ asyncio.run(sbc.contact())
19
+ else:
20
+ while True:
21
+ s = input("(s) server OR (c) client side: ")
22
+ s = s.strip().lower()
23
+
24
+ if s in {"s","c"}:
25
+ break
26
+
27
+ stat = s == "s"
28
+ sls = SBLocalService(stat)
29
+ sls.run()
@@ -0,0 +1,33 @@
1
+ """
2
+ read/write operations for Seqbuild's Comm Lang language.
3
+ """
4
+ import websockets
5
+ import asyncio
6
+ from .sb_op import *
7
+ import json
8
+ from morebs2.matrix_methods import vector_to_string,cr
9
+ from morebs2.globalls import is_alphanumeric
10
+
11
+ DEFAULT_PORT = 8765
12
+
13
+ """
14
+ outputs the program's conventional filename
15
+ for `info` (client name or server address).
16
+ """
17
+ def filename_for_CL(info,is_server_side:bool):
18
+ assert type(is_server_side) == bool
19
+
20
+ if is_server_side:
21
+ return "client__" + info + ".txt"
22
+
23
+ q = info.split(".")
24
+ assert len(q) == 4
25
+
26
+ q1 = q[-1].split(":")
27
+ assert len(q1) == 2
28
+
29
+ q[-1] = q1[0]
30
+ q1 = q1[1]
31
+
32
+ s = "_".join(q)
33
+ return "server__" + s + "-" + q1 + ".txt"
@@ -0,0 +1,271 @@
1
+ from .rw_comm_lang import *
2
+
3
+ class SBAuthClient:
4
+
5
+ def __init__(self):
6
+ self.addr = None
7
+ self.user_idn = None
8
+ self.utable = UserTable(False)
9
+ self.is_new_user = False
10
+
11
+ self.finstat = False
12
+ self.active_clp = None
13
+ return
14
+
15
+ async def contact(self):
16
+ s = await asyncio.get_running_loop().run_in_executor(None, \
17
+ input, "enter in IP address: ")
18
+ s1 = await asyncio.get_running_loop().run_in_executor(None, \
19
+ input, "enter in port number: ")
20
+
21
+ self.addr = s + ":" + s1
22
+ server = "ws://" + self.addr
23
+ await self.act(server)
24
+
25
+ async def act(self,server):
26
+ async with websockets.connect(server) as wsock:
27
+ print("Connected to server!")
28
+ while True:
29
+ await self.recv(wsock)
30
+ if self.finstat:
31
+ break
32
+
33
+ async def recv(self,wsock):
34
+ async for message in wsock:
35
+ print(message)
36
+
37
+ try:
38
+ message2 = json.loads(message)
39
+ message = message2
40
+ except: pass
41
+
42
+ # case: data input, CL key or data
43
+ if type(message) == list:
44
+ #q = message.split("\n")
45
+ # case: receive COMM LANG key
46
+ if message[0].strip() == "COMM LANG":
47
+ gen_name = message[1]
48
+ cl_string = message[2]
49
+ stat = self.write_key_to_file(cl_string,gen_name,False)
50
+ if not stat:
51
+ self.finstat = True
52
+ return
53
+ continue
54
+
55
+ # case: initial login
56
+ elif message.strip() == "username. leave blank if you are new:":
57
+ response = await self.login(wsock)
58
+ continue
59
+
60
+ elif message.strip() == "read (r) or write (w) or quit (q)?":
61
+ stat = await self.rw_ops(wsock)
62
+ if stat == -1:
63
+ self.finstat = True
64
+ return
65
+ continue
66
+
67
+ async def login(self,wsock):
68
+ s = await asyncio.get_running_loop().run_in_executor(None, input, "[x] ")
69
+
70
+ await wsock.send(s)
71
+ self.user_idn = s
72
+ response = await wsock.recv()
73
+ print(response)
74
+ response = response.strip()
75
+ q = response.split(" ")
76
+
77
+ # case: new user
78
+ if response == "new username:":
79
+ s2 = await asyncio.get_running_loop().run_in_executor(None, input, "[x] ")
80
+ await wsock.send(s2)
81
+ self.user_idn = s2
82
+ response = wsock.recv()
83
+ self.is_new_user = True
84
+ # case: username does not exist.
85
+ elif q[-2] == ["try","again!"]:
86
+ print("Username does not exist. Please try again.")
87
+ s2 = await asyncio.get_running_loop().run_in_executor(None, input, "[x] ")
88
+ response = await wsock.send(s2)
89
+ # case: provide the key
90
+ elif q[:4] == ["enter","in","your","key"]:
91
+ num_iter = int(q[5])
92
+ await self.send_passwd(wsock,num_iter,is_login=True)
93
+
94
+ return response
95
+
96
+ def write_key_to_file(self,cl_string,gen_name,is_update:bool):
97
+ user_str = filename_for_CL(self.addr,False)
98
+ fp = os.path.join(DEFAULT_SB_USER_DIR, user_str)
99
+
100
+ fobj = open(fp, "w")
101
+ fobj.write(cl_string)
102
+ fobj.close()
103
+
104
+ try:
105
+ if not is_update:
106
+ self.utable.add_user(self.addr,user_str,gen_name)
107
+ else:
108
+ self.utable.delta_user(self.addr,user_str,gen_name)
109
+ except:
110
+ print("key already present for server/port OR could not be updated")
111
+ return False
112
+ return True
113
+
114
+ async def send_passwd(self,wsock,num_iter,is_login:bool):
115
+
116
+ user_str = filename_for_CL(self.addr,False)
117
+
118
+ if type(self.active_clp) == type(None):
119
+ fp = filename_for_CL(self.addr,False)
120
+ full_fp = os.path.join(DEFAULT_SB_USER_DIR,fp)
121
+ self.active_clp = CommLangParser(full_fp)
122
+ self.active_clp.process_file()
123
+
124
+ gen_name = self.utable.user_to_X(self.addr,"g-name")
125
+
126
+ prev_elements = 0
127
+ if is_login:
128
+ prev_elements = self.utable.user_to_X(self.addr,"# iterations")
129
+
130
+ total_elements = prev_elements + num_iter
131
+ q = process_CommLang_generator(self.active_clp,gen_name,\
132
+ total_elements+1,num_iter)
133
+
134
+ self.utable.update_user(self.addr,num_iter)
135
+ s = vector_to_string(q,cr)
136
+ print("\t\t sending key")
137
+ print(s)
138
+ await wsock.send(s)
139
+
140
+ #------------------------------- post login
141
+
142
+ async def rw_ops(self,wsock):
143
+ # read/write
144
+ rw_stat = None
145
+ fpath = None
146
+ while True:
147
+ q = await asyncio.get_running_loop().run_in_executor(None, input, "[x] ")
148
+ await wsock.send(q)
149
+ s = await wsock.recv()
150
+ print(s)
151
+ if s.strip() == "enter in filepath:":
152
+ rw_stat = q
153
+ break
154
+
155
+ if q == "q":
156
+ rw_stat = "q"
157
+ break
158
+
159
+ if rw_stat == "q":
160
+ return -1
161
+
162
+ F = self.r_ops if rw_stat == "r" else self.w_ops
163
+ stat = await F(wsock)
164
+ return stat
165
+
166
+ async def r_ops(self,wsock):
167
+ fpath = await asyncio.get_running_loop().run_in_executor(None, input, "[x] ")
168
+ await wsock.send(fpath)
169
+
170
+ #while True:
171
+ sec_check_prompt = await wsock.recv()
172
+ sec_check_prompt = sec_check_prompt.strip().split(" ")
173
+ assert sec_check_prompt[:4] == ["enter","in","your","key"]
174
+ #break
175
+
176
+ num_iter = int(sec_check_prompt[5])
177
+ await self.send_passwd(wsock,num_iter,is_login=False)
178
+
179
+ sec_check_two = await wsock.recv()
180
+ try:
181
+ sec_check_two_ = json.loads(sec_check_two)
182
+ sec_check_two = sec_check_two_
183
+ except:
184
+ return False
185
+
186
+ # case: correct key
187
+ if type(sec_check_two) == list:
188
+ ##fpath2 = await asyncio.get_running_loop().run_in_executor(None, input, "[x] write out path? ")
189
+ stat = await self.record_read_file(sec_check_two[1])
190
+ if not stat:
191
+ print("could not write out contents to file.")
192
+ return False
193
+ else:
194
+ print("Security check failed.")
195
+ return False
196
+
197
+ x = await wsock.recv()
198
+ self.update_key_proc(x)
199
+ return True
200
+
201
+ async def record_read_file(self,contents):
202
+
203
+ fpath2 = await asyncio.get_running_loop().run_in_executor(None, input, "[x] write out path? ")
204
+
205
+ try:
206
+ with open(fpath2,"w") as f:
207
+ f.write(contents)
208
+ except:
209
+ return False
210
+ return True
211
+
212
+ async def w_ops(self,wsock):
213
+ q = await wsock.recv()
214
+ print(q)
215
+
216
+ while True:
217
+ fpath = await asyncio.get_running_loop().run_in_executor(None, input, "[x]: ")
218
+
219
+ contents = None
220
+ try:
221
+ with open(fpath,"r") as f:
222
+ contents_ = f.read()
223
+ contents = contents_
224
+ except:
225
+ pass
226
+
227
+ if type(contents) == type(None):
228
+ print("invalid path.")
229
+ continue
230
+ break
231
+
232
+ sec_check_prompt = await wsock.recv()
233
+ sec_check_prompt = sec_check_prompt.strip().split(" ")
234
+ assert sec_check_prompt[:4] == ["enter","in","your","key"]
235
+
236
+ num_iter = int(sec_check_prompt[5])
237
+ await self.send_passwd(wsock,num_iter,is_login=False)
238
+
239
+ sec_check_two = await wsock.recv()
240
+ print(sec_check_two)
241
+
242
+ if sec_check_two[:8] != "proceed.":
243
+ print("Security check failed.")
244
+ return False
245
+
246
+ fpath2 = await asyncio.get_running_loop().run_in_executor(None, input, "[x] server side path: ")
247
+ C = json.dumps([fpath2,contents])
248
+ await wsock.send(C)
249
+ s = await wsock.recv()
250
+ print(s)
251
+
252
+ if s == "Wrote content.":
253
+ x = await wsock.recv()
254
+ self.update_key_proc(x)
255
+ return True
256
+
257
+ def update_key_proc(self,x):
258
+
259
+ # check for update to key here
260
+ try:
261
+ x_ = json.loads(x)
262
+
263
+ #
264
+ if type(x_) == list:
265
+ print("updating key")
266
+ #q = ["COMM LANG",gen_name,content]
267
+ gen_name = x_[1]
268
+ cl_string = x_[2]
269
+ self.write_key_to_file(cl_string,gen_name,is_update=True)
270
+ except:
271
+ print(x)