RemoteRF 0.0.7.40__tar.gz → 0.0.7.41__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.
Files changed (40) hide show
  1. {remoterf-0.0.7.40/src/remoteRF.egg-info → remoterf-0.0.7.41}/PKG-INFO +1 -1
  2. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/setup.py +3 -2
  3. remoterf-0.0.7.41/src/remoteRF/core/cert_fetcher.py +140 -0
  4. remoterf-0.0.7.41/src/remoteRF/core/config.py +143 -0
  5. {remoterf-0.0.7.40 → remoterf-0.0.7.41/src/remoteRF.egg-info}/PKG-INFO +1 -1
  6. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF.egg-info/SOURCES.txt +2 -0
  7. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF.egg-info/entry_points.txt +1 -0
  8. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/LICENSE +0 -0
  9. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/MANIFEST.in +0 -0
  10. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/README.md +0 -0
  11. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/setup.cfg +0 -0
  12. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/__init__.py +0 -0
  13. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/__init__.py +0 -0
  14. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/grpc/__init__.py +0 -0
  15. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
  16. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
  17. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/utils/__init__.py +0 -0
  18. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/utils/ansi_codes.py +0 -0
  19. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/utils/api_token.py +0 -0
  20. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/utils/list_string.py +0 -0
  21. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/common/utils/process_arg.py +0 -0
  22. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/__init__.py +0 -0
  23. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/acc_login.py +0 -0
  24. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/app.py +0 -0
  25. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/__init__.py +0 -0
  26. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/ca.crt +0 -0
  27. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/ca.key +0 -0
  28. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/cert.pem +0 -0
  29. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/key.pem +0 -0
  30. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/server.crt +0 -0
  31. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/certs/server.key +0 -0
  32. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/grpc_acc.py +0 -0
  33. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/grpc_client.py +0 -0
  34. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/core/version.py +0 -0
  35. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/drivers/__init__.py +0 -0
  36. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
  37. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
  38. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF.egg-info/dependency_links.txt +0 -0
  39. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF.egg-info/requires.txt +0 -0
  40. {remoterf-0.0.7.40 → remoterf-0.0.7.41}/src/remoteRF.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 0.0.7.40
3
+ Version: 0.0.7.41
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
6
6
 
7
7
  setup(
8
8
  name="remoterf",
9
- version="0.0.7.40",
9
+ version="0.0.7.41",
10
10
  author="Ethan Ge",
11
11
  author_email="ethoGalaxy@gmail.com",
12
12
  description="A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.",
@@ -28,7 +28,8 @@ setup(
28
28
  entry_points={
29
29
  'console_scripts': [
30
30
  'remoterf-login=remoteRF.core.acc_login:main',
31
- 'remoterf-v=remoteRF.core.version:main'
31
+ 'remoterf-v=remoteRF.core.version:main',
32
+ 'remoterf-config=remoteRF.core.config:main',
32
33
  ],
33
34
  },
34
35
  )
@@ -0,0 +1,140 @@
1
+ # cert_fetcher.py
2
+ """
3
+ RemoteRF Client-Side Cert Fetcher (bootstrap)
4
+
5
+ Purpose:
6
+ - Fetch the public CA certificate (ca.crt) from a server's bootstrap endpoint
7
+ (your cert_provider) and save it locally for later use in:
8
+ grpc.ssl_channel_credentials(root_certificates=ca_bytes)
9
+
10
+ Security note (TOFU):
11
+ - This fetch is typically done over plaintext HTTP (or raw TCP). The CA cert is public,
12
+ but authenticity is not guaranteed on first fetch. If you want TOFU done "right",
13
+ you should display the SHA256 fingerprint and require user confirmation elsewhere.
14
+ This module focuses only on: fetch + save + return success.
15
+
16
+ Behavior:
17
+ - Attempts HTTP GET first: http://{host}:{port}/ca.crt
18
+ - If HTTP fails, falls back to raw TCP (reads until EOF).
19
+ - Saves to a per-profile file under ~/.config/remoterf/certs/ by default.
20
+
21
+ Exported API:
22
+ - fetch_and_save_ca_cert(host, port, *, out_path=None, profile=None, timeout_sec=3.0) -> bool
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import os
29
+ import socket
30
+ import urllib.request
31
+ from pathlib import Path
32
+ from typing import Optional
33
+
34
+
35
+ CERT_FILENAME_DEFAULT = "ca.crt"
36
+
37
+
38
+ def _default_config_dir() -> Path:
39
+ # Cross-platform-ish default; adjust if you already have a standard in your project.
40
+ # Linux: ~/.config/remoterf
41
+ # macOS: also acceptable; if you want Apple-standard, use ~/Library/Application Support/remoterf
42
+ base = Path(os.path.expanduser("~")) / ".config" / "remoterf"
43
+ return base
44
+
45
+
46
+ def _ensure_parent_dir(p: Path) -> None:
47
+ p.parent.mkdir(parents=True, exist_ok=True)
48
+
49
+
50
+ def _looks_like_pem_cert(data: bytes) -> bool:
51
+ return b"BEGIN CERTIFICATE" in data and b"END CERTIFICATE" in data
52
+
53
+
54
+ def sha256_fingerprint_pem(pem_bytes: bytes) -> str:
55
+ h = hashlib.sha256(pem_bytes).hexdigest()
56
+ return ":".join(h[i:i+2] for i in range(0, len(h), 2))
57
+
58
+
59
+ def _fetch_http(host: str, port: int, timeout_sec: float) -> bytes:
60
+ url = f"http://{host}:{port}/ca.crt"
61
+ req = urllib.request.Request(url, method="GET")
62
+ with urllib.request.urlopen(req, timeout=timeout_sec) as resp:
63
+ data = resp.read()
64
+ return data
65
+
66
+
67
+ def _fetch_raw_tcp(host: str, port: int, timeout_sec: float) -> bytes:
68
+ chunks: list[bytes] = []
69
+ with socket.create_connection((host, port), timeout=timeout_sec) as s:
70
+ s.settimeout(timeout_sec)
71
+ while True:
72
+ try:
73
+ b = s.recv(4096)
74
+ except socket.timeout:
75
+ break
76
+ if not b:
77
+ break
78
+ chunks.append(b)
79
+ return b"".join(chunks)
80
+
81
+
82
+ def fetch_and_save_ca_cert(
83
+ host: str,
84
+ port: int,
85
+ *,
86
+ out_path: Optional[str | Path] = None,
87
+ profile: Optional[str] = None,
88
+ timeout_sec: float = 3.0,
89
+ overwrite: bool = True,
90
+ ) -> bool:
91
+ """
92
+ Fetch CA cert from the server bootstrap endpoint and save it to disk.
93
+
94
+ Args:
95
+ host: server host/ip running cert_provider
96
+ port: cert_provider port (NOT the TLS gRPC port)
97
+ out_path: explicit output path (overrides profile/default location)
98
+ profile: if provided, saves as ~/.config/remoterf/certs/<profile>.crt
99
+ timeout_sec: network timeout
100
+ overwrite: whether to overwrite existing file
101
+
102
+ Returns:
103
+ True on success, False on any failure.
104
+ """
105
+ try:
106
+ if not isinstance(port, int):
107
+ port = int(port)
108
+
109
+ # Determine destination path
110
+ if out_path is not None:
111
+ dest = Path(out_path).expanduser().resolve()
112
+ else:
113
+ cfg = _default_config_dir()
114
+ certs_dir = cfg / "certs"
115
+ name = f"{profile}.crt" if profile else CERT_FILENAME_DEFAULT
116
+ dest = certs_dir / name
117
+
118
+ _ensure_parent_dir(dest)
119
+
120
+ if dest.exists() and not overwrite:
121
+ return True # already present, treat as success
122
+
123
+ # Fetch (HTTP first, then raw TCP fallback)
124
+ data = b""
125
+ try:
126
+ data = _fetch_http(host, port, timeout_sec)
127
+ except Exception:
128
+ data = _fetch_raw_tcp(host, port, timeout_sec)
129
+
130
+ if not data or not _looks_like_pem_cert(data):
131
+ return False
132
+
133
+ # Save
134
+ dest.write_bytes(data)
135
+
136
+ # Optional: you may want to return/print fingerprint, but requested API is bool.
137
+ return True
138
+
139
+ except Exception:
140
+ return False
@@ -0,0 +1,143 @@
1
+ # src/remoteRF/core/remoterf_config.py
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Tuple
10
+
11
+ from .cert_fetcher import fetch_and_save_ca_cert # your earlier module
12
+
13
+ # -----------------------------
14
+ # Local config locations
15
+ # -----------------------------
16
+ def _config_root() -> Path:
17
+ # Keep it simple and consistent with earlier suggestions.
18
+ return Path(os.path.expanduser("~")) / ".config" / "remoterf"
19
+
20
+ def _env_path() -> Path:
21
+ return _config_root() / ".env"
22
+
23
+ def _certs_dir() -> Path:
24
+ return _config_root() / "certs"
25
+
26
+ def _parse_hostport(s: str) -> Tuple[str, int]:
27
+ s = s.strip()
28
+ if "://" in s:
29
+ s = s.split("://", 1)[1]
30
+
31
+ if ":" not in s:
32
+ raise ValueError("Expected format host:port")
33
+
34
+ host, port_str = s.rsplit(":", 1)
35
+ host = host.strip()
36
+ port = int(port_str.strip())
37
+ if not host:
38
+ raise ValueError("Host is empty")
39
+ if port <= 0 or port > 65535:
40
+ raise ValueError("Port out of range")
41
+ return host, port
42
+
43
+
44
+ def _write_env_kv(path: Path, kv: dict[str, str]) -> None:
45
+ """
46
+ Writes/overwrites a simple dotenv-style file.
47
+ (We overwrite the whole file for simplicity and correctness.)
48
+ """
49
+ path.parent.mkdir(parents=True, exist_ok=True)
50
+ lines = []
51
+ for k, v in kv.items():
52
+ # Quote only if needed; keep simple and safe.
53
+ if any(c.isspace() for c in v) or any(c in v for c in ['"', "'"]):
54
+ v = v.replace('"', '\\"')
55
+ lines.append(f'{k}="{v}"')
56
+ else:
57
+ lines.append(f"{k}={v}")
58
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
59
+
60
+
61
+ def main() -> None:
62
+ parser = argparse.ArgumentParser(
63
+ prog="remoterf-config",
64
+ description="Configure RemoteRF client to talk to a specific server (fetch CA cert + save target)."
65
+ )
66
+ parser.add_argument(
67
+ "addr",
68
+ help="Server gRPC address in host:port form (this is the TLS gRPC port)."
69
+ )
70
+ parser.add_argument(
71
+ "--cert-port",
72
+ type=int,
73
+ default=None,
74
+ help="Bootstrap cert-provider port. If omitted, defaults to <grpc_port + 1>."
75
+ )
76
+ parser.add_argument(
77
+ "--profile",
78
+ default="default",
79
+ help="Profile name (controls CA cert filename). Default: default"
80
+ )
81
+ parser.add_argument(
82
+ "--timeout",
83
+ type=float,
84
+ default=3.0,
85
+ help="Network timeout seconds. Default: 3.0"
86
+ )
87
+ parser.add_argument(
88
+ "--overwrite",
89
+ action="store_true",
90
+ help="Overwrite existing cert/config if present."
91
+ )
92
+ args = parser.parse_args()
93
+
94
+ try:
95
+ host, grpc_port = _parse_hostport(args.addr)
96
+ except Exception as e:
97
+ print(f"Error: invalid addr '{args.addr}': {e}", file=sys.stderr)
98
+ sys.exit(2)
99
+
100
+ cert_port = args.cert_port if args.cert_port is not None else (grpc_port + 1)
101
+
102
+ # Destination CA cert file
103
+ certs_dir = _certs_dir()
104
+ certs_dir.mkdir(parents=True, exist_ok=True)
105
+ ca_out = certs_dir / f"{args.profile}.crt"
106
+
107
+ if ca_out.exists() and not (args.overwrite):
108
+ # Treat as OK; still (re)write env target.
109
+ fetched_ok = True
110
+ else:
111
+ fetched_ok = fetch_and_save_ca_cert(
112
+ host,
113
+ cert_port,
114
+ out_path=ca_out,
115
+ timeout_sec=args.timeout,
116
+ overwrite=True,
117
+ )
118
+
119
+ if not fetched_ok:
120
+ print(
121
+ f"Failed to fetch CA cert from {host}:{cert_port}.\n"
122
+ f"Expected cert provider at: http://{host}:{cert_port}/ca.crt (or raw TCP).\n"
123
+ f"Server must be running cert_provider on that port.",
124
+ file=sys.stderr
125
+ )
126
+ sys.exit(1)
127
+
128
+ # Write local .env (or config) with chosen server and CA path
129
+ env_file = _env_path()
130
+ _write_env_kv(env_file, {
131
+ "REMOTERF_ADDR": f"{host}:{grpc_port}",
132
+ "REMOTERF_CA_CERT": str(ca_out),
133
+ "REMOTERF_PROFILE": args.profile,
134
+ })
135
+
136
+ print("RemoteRF configured successfully.")
137
+ print(f" gRPC target : {host}:{grpc_port}")
138
+ print(f" cert port : {host}:{cert_port}")
139
+ print(f" CA cert : {ca_out}")
140
+ print(f" env file : {env_file}")
141
+
142
+ if __name__ == "__main__":
143
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 0.0.7.40
3
+ Version: 0.0.7.41
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -21,6 +21,8 @@ src/remoteRF/common/utils/process_arg.py
21
21
  src/remoteRF/core/__init__.py
22
22
  src/remoteRF/core/acc_login.py
23
23
  src/remoteRF/core/app.py
24
+ src/remoteRF/core/cert_fetcher.py
25
+ src/remoteRF/core/config.py
24
26
  src/remoteRF/core/grpc_acc.py
25
27
  src/remoteRF/core/grpc_client.py
26
28
  src/remoteRF/core/version.py
@@ -1,3 +1,4 @@
1
1
  [console_scripts]
2
+ remoterf-config = remoteRF.core.config:main
2
3
  remoterf-login = remoteRF.core.acc_login:main
3
4
  remoterf-v = remoteRF.core.version:main
File without changes
File without changes
File without changes
File without changes