lanet 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -21
- data/Gemfile.lock +1 -1
- data/README.md +86 -2
- data/index.html +129 -220
- data/lib/lanet/cli.rb +110 -62
- data/lib/lanet/encryptor.rb +94 -16
- data/lib/lanet/signer.rb +47 -0
- data/lib/lanet/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46d63ec0fb7276e1741780b97152879fb319bf42c0fd37c079b18faf355170ae
|
4
|
+
data.tar.gz: c7d07927e338e9ce4248c6909c2ec65903d23af9f09da94cf837181d14ac5ed0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '018bc023c9d38cce80a28495df69d6ae66aeaa0977862fbab115e4eb77340337366b615ce73041b41f416c10baa4905da00ca4966008875553a1b881b8ee3678'
|
7
|
+
data.tar.gz: 4f173fb766cb88c84340c84327db204853328016be6a50cd4ea3cd183501aaabfd7f1019ba306d79e110daa5b0a6d1fe0a2a57d2bf8f0f1fec8beb2e54c52cc0
|
data/CHANGELOG.md
CHANGED
@@ -1,25 +1,36 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.2.0] - 2025-03-08
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Digital signature support for message authentication and integrity
|
12
|
+
- New `Signer` class for creating and verifying digital signatures
|
13
|
+
- RSA-based key pair generation for signing and verification
|
14
|
+
- CLI command for generating key pairs (`keygen`)
|
15
|
+
- Support for signed-encrypted and signed-plaintext message formats
|
16
|
+
- Signature verification in message processing
|
17
|
+
- Updated documentation with signature examples
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
- Enhanced `Encryptor.prepare_message` to accept a private key for signing
|
21
|
+
- Modified `Encryptor.process_message` to verify signatures with public keys
|
22
|
+
- Extended message formats to include signature information
|
23
|
+
- CLI commands updated to support digital signature options
|
24
|
+
- Return values from message processing now include verification status
|
25
|
+
|
26
|
+
### Fixed
|
27
|
+
- Improved error handling for encryption and signature operations
|
2
28
|
|
3
29
|
## [0.1.0] - 2025-03-06
|
4
30
|
|
5
31
|
### Added
|
6
|
-
- Initial release of Lanet
|
7
|
-
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
- Multiple host ping support with continuous mode
|
12
|
-
- Messaging capabilities:
|
13
|
-
- Direct messaging to specific IP addresses
|
14
|
-
- Broadcast messaging to all network devices
|
15
|
-
- Message listening service
|
16
|
-
- Security features:
|
17
|
-
- Built-in encryption and decryption for secure messaging
|
18
|
-
- Command-line interface:
|
19
|
-
- `scan` command for network discovery
|
20
|
-
- `send` command for direct messaging
|
21
|
-
- `broadcast` command for network-wide announcements
|
22
|
-
- `listen` command to receive incoming messages
|
23
|
-
- `ping` command for host availability checking
|
24
|
-
- Ruby API for programmatic usage
|
25
|
-
- Complete documentation with usage examples
|
32
|
+
- Initial release of Lanet
|
33
|
+
- Basic UDP-based message sending and receiving
|
34
|
+
- Support for encrypted and plaintext messages
|
35
|
+
- Broadcasting capability
|
36
|
+
- Simple CLI interface
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# Lanet
|
2
2
|
|
3
|
-
|
3
|
+

|
4
|
+

|
4
5
|
[](https://opensource.org/licenses/MIT)
|
5
6
|
|
6
|
-
A lightweight, powerful LAN communication tool
|
7
|
+
A lightweight, powerful LAN communication tool that enables secure message exchange between devices on the same network. Lanet makes peer-to-peer networking simple with built-in encryption, network discovery, and both targeted and broadcast messaging capabilities.
|
7
8
|
|
8
9
|
## Features
|
9
10
|
|
@@ -16,7 +17,20 @@ A lightweight, powerful LAN communication tool for Ruby that enables secure mess
|
|
16
17
|
- 🖥️ **Command-line interface** - Perform common network operations directly from your terminal.
|
17
18
|
- 🧩 **Extensible** - Easily build custom tools and integrations using the Lanet API.
|
18
19
|
- ⚙️ **Configurable:** Adjust port settings, encryption keys, and network scan ranges.
|
20
|
+
- **Digital Signatures**: Ensure message authenticity and integrity
|
19
21
|
|
22
|
+
## Security Features
|
23
|
+
|
24
|
+
### Encryption
|
25
|
+
|
26
|
+
Lanet uses AES-256-CBC encryption to protect the content of messages. This ensures confidentiality during transmission.
|
27
|
+
|
28
|
+
### Digital Signatures
|
29
|
+
|
30
|
+
Digital signatures provide:
|
31
|
+
- **Authentication**: Verify the identity of the message sender
|
32
|
+
- **Data Integrity**: Ensure the message hasn't been tampered with during transit
|
33
|
+
- **Non-repudiation**: Senders cannot deny sending a message they signed
|
20
34
|
|
21
35
|
## Installation
|
22
36
|
|
@@ -44,6 +58,76 @@ gem install lanet
|
|
44
58
|
|
45
59
|
Lanet provides a powerful CLI for common network operations:
|
46
60
|
|
61
|
+
#### Generating Signature Keys
|
62
|
+
|
63
|
+
Generate a key pair for digital signatures:
|
64
|
+
|
65
|
+
```bash
|
66
|
+
lanet keygen
|
67
|
+
```
|
68
|
+
|
69
|
+
Generate a key pair with specific options:
|
70
|
+
|
71
|
+
```bash
|
72
|
+
lanet keygen --bits 4096 --output ~/.lanet_keys
|
73
|
+
```
|
74
|
+
|
75
|
+
The command will generate two files:
|
76
|
+
- `lanet_private.key`: Keep this secure and don't share it
|
77
|
+
- `lanet_public.key`: Share this with others who need to verify your messages
|
78
|
+
|
79
|
+
#### Sending Signed Messages
|
80
|
+
|
81
|
+
Send a digitally signed message (without encryption):
|
82
|
+
|
83
|
+
```bash
|
84
|
+
lanet send --target 192.168.1.5 --message "Signed message" --private-key-file lanet_private.key
|
85
|
+
```
|
86
|
+
|
87
|
+
Send an encrypted and signed message:
|
88
|
+
|
89
|
+
```bash
|
90
|
+
lanet send --target 192.168.1.5 --message "Secure signed message" --key "my_secret_key" --private-key-file lanet_private.key
|
91
|
+
```
|
92
|
+
|
93
|
+
Broadcast a signed message to all devices:
|
94
|
+
|
95
|
+
```bash
|
96
|
+
lanet broadcast --message "Important announcement" --private-key-file lanet_private.key
|
97
|
+
```
|
98
|
+
|
99
|
+
#### Receiving and Verifying Signed Messages
|
100
|
+
|
101
|
+
Listen for messages and verify signatures:
|
102
|
+
|
103
|
+
```bash
|
104
|
+
lanet listen --public-key-file lanet_public.key
|
105
|
+
```
|
106
|
+
|
107
|
+
Listen for encrypted and signed messages:
|
108
|
+
|
109
|
+
```bash
|
110
|
+
lanet listen --encryption-key "my_secret_key" --public-key-file lanet_public.key
|
111
|
+
```
|
112
|
+
|
113
|
+
When a signed message is received, the output will show verification status:
|
114
|
+
|
115
|
+
```
|
116
|
+
Message from 192.168.1.5:
|
117
|
+
Content: Hello, this is a signed message
|
118
|
+
Signature: VERIFIED
|
119
|
+
----------------------------------------
|
120
|
+
```
|
121
|
+
|
122
|
+
If signature verification fails:
|
123
|
+
|
124
|
+
```
|
125
|
+
Message from 192.168.1.5:
|
126
|
+
Content: Hello, this message was tampered with
|
127
|
+
Signature: NOT VERIFIED: Signature verification failed
|
128
|
+
----------------------------------------
|
129
|
+
```
|
130
|
+
|
47
131
|
#### Scanning the network
|
48
132
|
|
49
133
|
```bash
|
data/index.html
CHANGED
@@ -1,233 +1,142 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
}
|
54
|
-
h3 {
|
55
|
-
color: #0056b3;
|
56
|
-
font-size: 1.5em;
|
57
|
-
margin-top: 30px;
|
58
|
-
}
|
59
|
-
code {
|
60
|
-
background-color: #f4f4f4;
|
61
|
-
padding: 2px 4px;
|
62
|
-
border-radius: 4px;
|
63
|
-
font-family: 'Courier New', monospace;
|
64
|
-
}
|
65
|
-
pre {
|
66
|
-
background-color: #f4f4f4;
|
67
|
-
padding: 15px;
|
68
|
-
border-radius: 8px;
|
69
|
-
overflow-x: auto;
|
70
|
-
}
|
71
|
-
.feature-list {
|
72
|
-
list-style-type: none;
|
73
|
-
padding: 0;
|
74
|
-
}
|
75
|
-
.feature-list li {
|
76
|
-
margin: 10px 0;
|
77
|
-
font-size: 1.1em;
|
78
|
-
}
|
79
|
-
.feature-list li::before {
|
80
|
-
content: '🚀';
|
81
|
-
margin-right: 10px;
|
82
|
-
}
|
83
|
-
.cli-example, .api-example {
|
84
|
-
margin: 20px 0;
|
85
|
-
}
|
86
|
-
.cli-example h4, .api-example h4 {
|
87
|
-
margin-bottom: 10px;
|
88
|
-
font-size: 1.2em;
|
89
|
-
}
|
90
|
-
footer {
|
91
|
-
text-align: center;
|
92
|
-
padding: 20px;
|
93
|
-
background-color: #007BFF;
|
94
|
-
color: white;
|
95
|
-
margin-top: 40px;
|
96
|
-
}
|
97
|
-
footer a {
|
98
|
-
color: white;
|
99
|
-
text-decoration: none;
|
100
|
-
}
|
101
|
-
</style>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Lanet - Secure Network Communications Library</title>
|
7
|
+
<style>
|
8
|
+
body {
|
9
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
10
|
+
line-height: 1.6;
|
11
|
+
color: #333;
|
12
|
+
max-width: 800px;
|
13
|
+
margin: 0 auto;
|
14
|
+
padding: 20px;
|
15
|
+
}
|
16
|
+
h1 {
|
17
|
+
color: #2c3e50;
|
18
|
+
border-bottom: 2px solid #3498db;
|
19
|
+
padding-bottom: 10px;
|
20
|
+
}
|
21
|
+
h2 {
|
22
|
+
color: #2980b9;
|
23
|
+
margin-top: 30px;
|
24
|
+
}
|
25
|
+
pre {
|
26
|
+
background-color: #f8f8f8;
|
27
|
+
border: 1px solid #ddd;
|
28
|
+
border-left: 3px solid #3498db;
|
29
|
+
padding: 15px;
|
30
|
+
overflow-x: auto;
|
31
|
+
}
|
32
|
+
code {
|
33
|
+
font-family: 'Courier New', Courier, monospace;
|
34
|
+
}
|
35
|
+
.container {
|
36
|
+
background-color: #fff;
|
37
|
+
border-radius: 5px;
|
38
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
39
|
+
padding: 20px;
|
40
|
+
}
|
41
|
+
.feature {
|
42
|
+
margin: 20px 0;
|
43
|
+
padding-left: 20px;
|
44
|
+
border-left: 4px solid #2ecc71;
|
45
|
+
}
|
46
|
+
.security-feature {
|
47
|
+
background-color: #e8f4fc;
|
48
|
+
padding: 15px;
|
49
|
+
margin: 10px 0;
|
50
|
+
border-radius: 4px;
|
51
|
+
}
|
52
|
+
</style>
|
102
53
|
</head>
|
103
54
|
<body>
|
104
|
-
<header>
|
105
55
|
<div class="container">
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
<
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
<
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
<pre><code>gem install lanet</code></pre>
|
144
|
-
</section>
|
145
|
-
|
146
|
-
<section id="usage">
|
147
|
-
<h2>Usage</h2>
|
148
|
-
<h3>Command Line Interface (CLI)</h3>
|
149
|
-
<div class="cli-example">
|
150
|
-
<h4>Scanning the network</h4>
|
151
|
-
<pre><code>lanet scan --range 192.168.1.0/24</code></pre>
|
152
|
-
<p>With verbose output (shows hostname, MAC address, open ports, and response time):</p>
|
153
|
-
<pre><code>lanet scan --range 192.168.1.0/24 --verbose</code></pre>
|
154
|
-
</div>
|
155
|
-
<div class="cli-example">
|
156
|
-
<h4>Sending a message to a specific target</h4>
|
157
|
-
<pre><code>lanet send --target 192.168.1.5 --message "Hello there!"</code></pre>
|
158
|
-
<p>Sending an encrypted message:</p>
|
159
|
-
<pre><code>lanet send --target 192.168.1.5 --message "Secret message" --key "my_secret_key"</code></pre>
|
160
|
-
</div>
|
161
|
-
<div class="cli-example">
|
162
|
-
<h4>Broadcasting a message to all devices</h4>
|
163
|
-
<pre><code>lanet broadcast --message "Announcement for everyone!"</code></pre>
|
164
|
-
</div>
|
165
|
-
<div class="cli-example">
|
166
|
-
<h4>Pinging a specific host</h4>
|
167
|
-
<pre><code>lanet ping --host 192.168.1.5</code></pre>
|
168
|
-
</div>
|
169
|
-
|
170
|
-
<h3>Ruby API</h3>
|
171
|
-
<div class="api-example">
|
172
|
-
<h4>Scanning the network</h4>
|
56
|
+
<h1>Lanet</h1>
|
57
|
+
<p>A secure network communication library for Ruby applications.</p>
|
58
|
+
|
59
|
+
<h2>Key Features</h2>
|
60
|
+
|
61
|
+
<div class="feature">
|
62
|
+
<h3>Encrypted Communication</h3>
|
63
|
+
<p>Protect your messages with military-grade AES-256-CBC encryption to ensure confidentiality.</p>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div class="feature">
|
67
|
+
<h3>Digital Signatures</h3>
|
68
|
+
<p>Verify message authenticity and integrity with RSA-based digital signatures.</p>
|
69
|
+
|
70
|
+
<div class="security-feature">
|
71
|
+
<h4>Benefits of Digital Signatures:</h4>
|
72
|
+
<ul>
|
73
|
+
<li><strong>Authentication</strong>: Verify that the message came from the claimed sender</li>
|
74
|
+
<li><strong>Data Integrity</strong>: Ensure the message hasn't been tampered with during transit</li>
|
75
|
+
<li><strong>Non-repudiation</strong>: Senders cannot deny sending a message they signed</li>
|
76
|
+
<li><strong>Protection against MITM attacks</strong>: Detect man-in-the-middle tampering attempts</li>
|
77
|
+
</ul>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<div class="feature">
|
82
|
+
<h3>Simple UDP-based Messaging</h3>
|
83
|
+
<p>Send and receive messages easily with straightforward sender and receiver classes.</p>
|
84
|
+
</div>
|
85
|
+
|
86
|
+
<div class="feature">
|
87
|
+
<h3>Network Broadcasting</h3>
|
88
|
+
<p>Support for broadcasting messages across your entire network.</p>
|
89
|
+
</div>
|
90
|
+
|
91
|
+
<h2>Quick Example</h2>
|
92
|
+
|
173
93
|
<pre><code>require 'lanet'
|
174
94
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
<div class="api-example">
|
180
|
-
<h4>Sending a message</h4>
|
181
|
-
<pre><code>sender = Lanet.sender
|
182
|
-
sender.send_to('192.168.1.5', 'Hello from Ruby!')</code></pre>
|
183
|
-
<p>Broadcasting a message:</p>
|
184
|
-
<pre><code>sender.broadcast('Announcement to all devices!')</code></pre>
|
185
|
-
</div>
|
186
|
-
<div class="api-example">
|
187
|
-
<h4>Listening for messages</h4>
|
188
|
-
<pre><code>receiver = Lanet.receiver
|
189
|
-
receiver.listen do |data, ip|
|
190
|
-
puts "Received from #{ip}: #{data}"
|
191
|
-
end</code></pre>
|
192
|
-
</div>
|
193
|
-
</section>
|
194
|
-
|
195
|
-
<section id="configuration">
|
196
|
-
<h2>Configuration</h2>
|
197
|
-
<p>Lanet can be configured with several options:</p>
|
198
|
-
<ul>
|
199
|
-
<li><strong>Port</strong>: Default is 5000, but can be changed for both sending and receiving.</li>
|
200
|
-
<li><strong>Encryption Keys</strong>: Use your own encryption keys for secure communication.</li>
|
201
|
-
<li><strong>Custom Ranges</strong>: Scan specific network ranges to discover devices.</li>
|
202
|
-
</ul>
|
203
|
-
</section>
|
204
|
-
|
205
|
-
<section id="development">
|
206
|
-
<h2>Development</h2>
|
207
|
-
<p>After checking out the repo, run <code>bin/setup</code> to install dependencies. Then, run <code>rake spec</code> to run the tests.</p>
|
208
|
-
<p>To install this gem locally, run <code>bundle exec rake install</code>.</p>
|
209
|
-
</section>
|
95
|
+
# Generate RSA key pair for signing
|
96
|
+
key_pair = Lanet::Signer.generate_key_pair
|
97
|
+
private_key = key_pair[:private_key]
|
98
|
+
public_key = key_pair[:public_key]
|
210
99
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
<ol>
|
215
|
-
<li>Fork the repository</li>
|
216
|
-
<li>Create your feature branch (<code>git checkout -b my-new-feature</code>)</li>
|
217
|
-
<li>Commit your changes (<code>git commit -am 'Add some feature'</code>)</li>
|
218
|
-
<li>Push to the branch (<code>git push origin my-new-feature</code>)</li>
|
219
|
-
<li>Create a new Pull Request</li>
|
220
|
-
</ol>
|
221
|
-
</section>
|
100
|
+
# Set up sender and receiver
|
101
|
+
sender = Lanet::Sender.new(9000)
|
102
|
+
encryption_key = "secret-key-123"
|
222
103
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
104
|
+
# Send a signed and encrypted message
|
105
|
+
message = "Hello, secure world!"
|
106
|
+
prepared_message = Lanet::Encryptor.prepare_message(
|
107
|
+
message,
|
108
|
+
encryption_key,
|
109
|
+
private_key
|
110
|
+
)
|
111
|
+
sender.send_to("192.168.1.5", prepared_message)
|
228
112
|
|
229
|
-
|
230
|
-
|
231
|
-
|
113
|
+
# Receive and verify messages
|
114
|
+
receiver = Lanet::Receiver.new(9000)
|
115
|
+
receiver.listen do |data, sender_ip|
|
116
|
+
result = Lanet::Encryptor.process_message(
|
117
|
+
data,
|
118
|
+
encryption_key,
|
119
|
+
public_key
|
120
|
+
)
|
121
|
+
|
122
|
+
puts "Message: #{result[:content]}"
|
123
|
+
puts "Verified: #{result[:verified]}"
|
124
|
+
end</code></pre>
|
125
|
+
|
126
|
+
<h2>Installation</h2>
|
127
|
+
|
128
|
+
<p>Add this to your Gemfile:</p>
|
129
|
+
<pre><code>gem 'lanet'</code></pre>
|
130
|
+
|
131
|
+
<p>Or install directly:</p>
|
132
|
+
<pre><code>gem install lanet</code></pre>
|
133
|
+
|
134
|
+
<h2>Documentation</h2>
|
135
|
+
<p>For complete documentation, please visit the <a href="https://github.com/yourusername/lanet">GitHub repository</a>.</p>
|
136
|
+
|
137
|
+
<footer style="margin-top: 40px; text-align: center; color: #7f8c8d;">
|
138
|
+
<p>Lanet - Secure Network Communications Library</p>
|
139
|
+
</footer>
|
140
|
+
</div>
|
232
141
|
</body>
|
233
142
|
</html>
|
data/lib/lanet/cli.rb
CHANGED
@@ -14,27 +14,55 @@ module Lanet
|
|
14
14
|
false
|
15
15
|
end
|
16
16
|
|
17
|
-
desc "send
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
desc "send", "Send a message to a specific target"
|
18
|
+
method_option :target, type: :string, required: true, desc: "Target IP address"
|
19
|
+
method_option :message, type: :string, required: true, desc: "Message to send"
|
20
|
+
method_option :key, type: :string, desc: "Encryption key (optional)"
|
21
|
+
method_option :private_key_file, type: :string, desc: "Path to private key file for signing (optional)"
|
22
|
+
method_option :port, type: :numeric, default: 5000, desc: "Port number"
|
22
23
|
def send
|
23
|
-
sender = Sender.new(options[:port])
|
24
|
-
|
24
|
+
sender = Lanet::Sender.new(options[:port])
|
25
|
+
|
26
|
+
private_key = nil
|
27
|
+
if options[:private_key_file]
|
28
|
+
begin
|
29
|
+
private_key = File.read(options[:private_key_file])
|
30
|
+
puts "Message will be digitally signed"
|
31
|
+
rescue StandardError => e
|
32
|
+
puts "Error reading private key file: #{e.message}"
|
33
|
+
return
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
message = Lanet::Encryptor.prepare_message(options[:message], options[:key], private_key)
|
38
|
+
|
25
39
|
sender.send_to(options[:target], message)
|
26
40
|
puts "Message sent to #{options[:target]}"
|
27
41
|
end
|
28
42
|
|
29
|
-
desc "broadcast
|
30
|
-
|
31
|
-
|
32
|
-
|
43
|
+
desc "broadcast", "Broadcast a message to all devices on the network"
|
44
|
+
method_option :message, type: :string, required: true, desc: "Message to broadcast"
|
45
|
+
method_option :key, type: :string, desc: "Encryption key (optional)"
|
46
|
+
method_option :private_key_file, type: :string, desc: "Path to private key file for signing (optional)"
|
47
|
+
method_option :port, type: :numeric, default: 5000, desc: "Port number"
|
33
48
|
def broadcast
|
34
|
-
sender = Sender.new(options[:port])
|
35
|
-
|
49
|
+
sender = Lanet::Sender.new(options[:port])
|
50
|
+
|
51
|
+
private_key = nil
|
52
|
+
if options[:private_key_file]
|
53
|
+
begin
|
54
|
+
private_key = File.read(options[:private_key_file])
|
55
|
+
puts "Message will be digitally signed"
|
56
|
+
rescue StandardError => e
|
57
|
+
puts "Error reading private key file: #{e.message}"
|
58
|
+
return
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
message = Lanet::Encryptor.prepare_message(options[:message], options[:key], private_key)
|
63
|
+
|
36
64
|
sender.broadcast(message)
|
37
|
-
puts "Message broadcasted"
|
65
|
+
puts "Message broadcasted to the network"
|
38
66
|
end
|
39
67
|
|
40
68
|
desc "scan --range CIDR [--timeout TIMEOUT] [--threads THREADS] [--verbose]",
|
@@ -77,64 +105,84 @@ module Lanet
|
|
77
105
|
end
|
78
106
|
end
|
79
107
|
|
80
|
-
desc "listen
|
81
|
-
|
82
|
-
|
108
|
+
desc "listen", "Listen for incoming messages"
|
109
|
+
method_option :encryption_key, type: :string, desc: "Encryption key for decrypting messages (optional)"
|
110
|
+
method_option :public_key_file, type: :string, desc: "Path to public key file for signature verification (optional)"
|
111
|
+
method_option :port, type: :numeric, default: 5000, desc: "Port to listen on"
|
83
112
|
def listen
|
84
|
-
receiver = Receiver.new(options[:port])
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
113
|
+
receiver = Lanet::Receiver.new(options[:port])
|
114
|
+
|
115
|
+
public_key = nil
|
116
|
+
if options[:public_key_file]
|
117
|
+
begin
|
118
|
+
public_key = File.read(options[:public_key_file])
|
119
|
+
puts "Digital signature verification enabled"
|
120
|
+
rescue StandardError => e
|
121
|
+
puts "Error reading public key file: #{e.message}"
|
122
|
+
return
|
123
|
+
end
|
89
124
|
end
|
90
|
-
end
|
91
125
|
|
92
|
-
|
93
|
-
|
94
|
-
option :hosts, type: :string, desc: "Comma-separated list of hosts to ping"
|
95
|
-
option :timeout, type: :numeric, default: 1, desc: "Ping timeout in seconds"
|
96
|
-
option :count, type: :numeric, default: 5, desc: "Number of ping packets to send"
|
97
|
-
option :quiet, type: :boolean, default: false, desc: "Only display summary"
|
98
|
-
option :continuous, type: :boolean, default: false, desc: "Ping continuously until interrupted"
|
99
|
-
def ping(target_host = nil)
|
100
|
-
# Support both traditional command (lanet ping 192.168.1.1) and option-style (--host)
|
101
|
-
target = target_host || options[:host] || options[:hosts]
|
102
|
-
|
103
|
-
unless target
|
104
|
-
puts "Error: Missing host to ping"
|
105
|
-
puts "Usage: lanet ping HOST"
|
106
|
-
puts " or: lanet ping --host HOST"
|
107
|
-
puts " or: lanet ping --hosts HOST1,HOST2,HOST3"
|
108
|
-
return
|
109
|
-
end
|
126
|
+
puts "Listening for messages on port #{options[:port]}..."
|
127
|
+
puts "Press Ctrl+C to stop"
|
110
128
|
|
111
|
-
|
129
|
+
receiver.listen do |data, sender_ip|
|
130
|
+
result = Lanet::Encryptor.process_message(data, options[:encryption_key], public_key)
|
112
131
|
|
113
|
-
|
114
|
-
|
115
|
-
host = target_host || options[:host]
|
116
|
-
if options[:quiet]
|
117
|
-
result = pinger.ping_host(host, false, options[:continuous])
|
118
|
-
display_ping_summary(host, result)
|
119
|
-
else
|
120
|
-
pinger.ping_host(host, true, options[:continuous]) # Real-time output with optional continuous mode
|
121
|
-
end
|
122
|
-
else
|
123
|
-
hosts = options[:hosts].split(",").map(&:strip)
|
132
|
+
puts "\nMessage from #{sender_ip}:"
|
133
|
+
puts "Content: #{result[:content]}"
|
124
134
|
|
125
|
-
if
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
# Real-time output for multiple hosts
|
133
|
-
pinger.ping_hosts(hosts, true, options[:continuous])
|
135
|
+
if result.key?(:verified)
|
136
|
+
verification_status = if result[:verified]
|
137
|
+
"VERIFIED"
|
138
|
+
else
|
139
|
+
"NOT VERIFIED: #{result[:verification_status]}"
|
140
|
+
end
|
141
|
+
puts "Signature: #{verification_status}"
|
134
142
|
end
|
143
|
+
|
144
|
+
puts "-" * 40
|
135
145
|
end
|
136
146
|
end
|
137
147
|
|
148
|
+
desc "ping", "Ping a host to check connectivity"
|
149
|
+
method_option :host, type: :string, desc: "Host to ping"
|
150
|
+
method_option :hosts, type: :string, desc: "Comma-separated list of hosts to ping"
|
151
|
+
method_option :timeout, type: :numeric, default: 1, desc: "Timeout in seconds"
|
152
|
+
method_option :count, type: :numeric, default: 4, desc: "Number of pings"
|
153
|
+
method_option :continuous, type: :boolean, default: false, desc: "Ping continuously until interrupted"
|
154
|
+
method_option :quiet, type: :boolean, default: false, desc: "Show only summary"
|
155
|
+
def ping(single_host = nil)
|
156
|
+
# This is a placeholder for the ping implementation
|
157
|
+
target_host = single_host || options[:host]
|
158
|
+
puts "Pinging #{target_host || options[:hosts]}..."
|
159
|
+
puts "Ping functionality not implemented yet"
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "keygen", "Generate key pair for digital signatures"
|
163
|
+
method_option :bits, type: :numeric, default: 2048, desc: "Key size in bits"
|
164
|
+
method_option :output, type: :string, default: ".", desc: "Output directory"
|
165
|
+
def keygen
|
166
|
+
key_pair = Lanet::Signer.generate_key_pair(options[:bits])
|
167
|
+
|
168
|
+
private_key_file = File.join(options[:output], "lanet_private.key")
|
169
|
+
public_key_file = File.join(options[:output], "lanet_public.key")
|
170
|
+
|
171
|
+
File.write(private_key_file, key_pair[:private_key])
|
172
|
+
File.write(public_key_file, key_pair[:public_key])
|
173
|
+
|
174
|
+
puts "Key pair generated!"
|
175
|
+
puts "Private key saved to: #{private_key_file}"
|
176
|
+
puts "Public key saved to: #{public_key_file}"
|
177
|
+
puts "\nIMPORTANT: Keep your private key secure and never share it."
|
178
|
+
puts "Share your public key with others who need to verify your messages."
|
179
|
+
end
|
180
|
+
|
181
|
+
desc "version", "Display the version of Lanet"
|
182
|
+
def version
|
183
|
+
puts "Lanet version #{Lanet::VERSION}"
|
184
|
+
end
|
185
|
+
|
138
186
|
private
|
139
187
|
|
140
188
|
def display_ping_details(host, result)
|
data/lib/lanet/encryptor.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "openssl"
|
4
4
|
require "digest"
|
5
5
|
require "base64"
|
6
|
+
require_relative "signer"
|
6
7
|
|
7
8
|
module Lanet
|
8
9
|
class Encryptor
|
@@ -10,16 +11,50 @@ module Lanet
|
|
10
11
|
CIPHER_ALGORITHM = "AES-256-CBC"
|
11
12
|
ENCRYPTED_PREFIX = "E"
|
12
13
|
PLAINTEXT_PREFIX = "P"
|
14
|
+
SIGNED_ENCRYPTED_PREFIX = "SE"
|
15
|
+
SIGNED_PLAINTEXT_PREFIX = "SP"
|
13
16
|
IV_SIZE = 16
|
17
|
+
SIGNATURE_DELIMITER = "||SIG||"
|
18
|
+
MAX_KEY_LENGTH = 64
|
14
19
|
|
15
20
|
# Error class for encryption/decryption failures
|
16
21
|
class Error < StandardError; end
|
17
22
|
|
18
|
-
#
|
23
|
+
# Prepares a message with encryption and/or signing
|
19
24
|
# @param message [String] the message to prepare
|
20
|
-
# @param
|
25
|
+
# @param encryption_key [String, nil] encryption key or nil for plaintext
|
26
|
+
# @param private_key [String, nil] PEM-encoded private key for signing or nil for unsigned
|
21
27
|
# @return [String] prepared message with appropriate prefix
|
22
|
-
def self.prepare_message(message,
|
28
|
+
def self.prepare_message(message, encryption_key, private_key = nil)
|
29
|
+
if private_key.nil? || private_key.empty?
|
30
|
+
prepare_unsigned_message(message, encryption_key)
|
31
|
+
else
|
32
|
+
# Sign the message
|
33
|
+
signature = Signer.sign(message.to_s, private_key)
|
34
|
+
message_with_signature = "#{message}#{SIGNATURE_DELIMITER}#{signature}"
|
35
|
+
|
36
|
+
return "#{SIGNED_PLAINTEXT_PREFIX}#{message_with_signature}" if encryption_key.nil? || encryption_key.empty?
|
37
|
+
|
38
|
+
# Signed but not encrypted
|
39
|
+
|
40
|
+
# Signed and encrypted
|
41
|
+
begin
|
42
|
+
cipher = OpenSSL::Cipher.new("AES-128-CBC")
|
43
|
+
cipher.encrypt
|
44
|
+
cipher.key = derive_key(encryption_key)
|
45
|
+
iv = cipher.random_iv
|
46
|
+
encrypted = cipher.update(message_with_signature) + cipher.final
|
47
|
+
encoded = Base64.strict_encode64(iv + encrypted)
|
48
|
+
"#{SIGNED_ENCRYPTED_PREFIX}#{encoded}"
|
49
|
+
rescue StandardError => e
|
50
|
+
raise Error, "Encryption failed: #{e.message}"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Original prepare_message renamed
|
57
|
+
def self.prepare_unsigned_message(message, key)
|
23
58
|
return PLAINTEXT_PREFIX + message.to_s if key.nil? || key.empty?
|
24
59
|
|
25
60
|
begin
|
@@ -35,35 +70,78 @@ module Lanet
|
|
35
70
|
end
|
36
71
|
end
|
37
72
|
|
38
|
-
# Processes a message, decrypting if necessary
|
73
|
+
# Processes a message, decrypting and verifying if necessary
|
39
74
|
# @param data [String] the data to process
|
40
|
-
# @param
|
41
|
-
# @
|
42
|
-
|
43
|
-
|
75
|
+
# @param encryption_key [String, nil] decryption key or nil
|
76
|
+
# @param public_key [String, nil] PEM-encoded public key for verification or nil
|
77
|
+
# @return [Hash] processed message with content and verification status
|
78
|
+
def self.process_message(data, encryption_key = nil, public_key = nil)
|
79
|
+
return { content: "[Empty message]", verified: false } if data.nil? || data.empty?
|
44
80
|
|
45
|
-
prefix = data[0]
|
46
|
-
|
81
|
+
prefix = data[0..0] # First character for simple prefixes
|
82
|
+
prefix = data[0..1] if data.length > 1 && %w[SE SP].include?(data[0..1]) # Two characters for complex prefixes
|
83
|
+
content = data[prefix.length..]
|
47
84
|
|
48
85
|
case prefix
|
49
86
|
when ENCRYPTED_PREFIX
|
50
|
-
if
|
51
|
-
"[Encrypted message received, but no key provided]"
|
87
|
+
if encryption_key.nil? || encryption_key.strip.empty?
|
88
|
+
{ content: "[Encrypted message received, but no key provided]", verified: false }
|
52
89
|
else
|
53
90
|
begin
|
54
|
-
decode_encrypted_message(content,
|
91
|
+
decrypted = decode_encrypted_message(content, encryption_key)
|
92
|
+
{ content: decrypted, verified: false }
|
55
93
|
rescue StandardError => e
|
56
|
-
"Decryption failed: #{e.message}"
|
94
|
+
{ content: "Decryption failed: #{e.message}", verified: false }
|
57
95
|
end
|
58
96
|
end
|
59
97
|
when PLAINTEXT_PREFIX
|
60
|
-
content
|
98
|
+
{ content: content, verified: false }
|
99
|
+
when SIGNED_ENCRYPTED_PREFIX
|
100
|
+
if encryption_key.nil? || encryption_key.strip.empty?
|
101
|
+
{ content: "[Signed encrypted message received, but no encryption key provided]", verified: false }
|
102
|
+
else
|
103
|
+
begin
|
104
|
+
decrypted = decode_encrypted_message(content, encryption_key)
|
105
|
+
process_signed_content(decrypted, public_key)
|
106
|
+
rescue StandardError => e
|
107
|
+
{ content: "Processing signed encrypted message failed: #{e.message}", verified: false }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
when SIGNED_PLAINTEXT_PREFIX
|
111
|
+
process_signed_content(content, public_key)
|
61
112
|
else
|
62
|
-
"[Invalid message format]"
|
113
|
+
{ content: "[Invalid message format]", verified: false }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Process content that contains a signature
|
118
|
+
def self.process_signed_content(content, public_key)
|
119
|
+
if content.include?(SIGNATURE_DELIMITER)
|
120
|
+
message, signature = content.split(SIGNATURE_DELIMITER, 2)
|
121
|
+
|
122
|
+
if public_key.nil? || public_key.strip.empty?
|
123
|
+
{ content: message, verified: false, verification_status: "No public key provided for verification" }
|
124
|
+
else
|
125
|
+
begin
|
126
|
+
verified = Signer.verify(message, signature, public_key)
|
127
|
+
{ content: message, verified: verified,
|
128
|
+
verification_status: verified ? "Verified" : "Signature verification failed" }
|
129
|
+
rescue StandardError => e
|
130
|
+
{ content: message, verified: false, verification_status: "Verification error: #{e.message}" }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
else
|
134
|
+
{ content: content, verified: false, verification_status: "No signature found" }
|
63
135
|
end
|
64
136
|
end
|
65
137
|
|
66
138
|
def self.derive_key(key)
|
139
|
+
# Add validation to reject keys that are too long
|
140
|
+
if key && key.length > MAX_KEY_LENGTH
|
141
|
+
raise Error,
|
142
|
+
"Encryption key is too long (maximum #{MAX_KEY_LENGTH} characters)"
|
143
|
+
end
|
144
|
+
|
67
145
|
digest = OpenSSL::Digest.new("SHA256")
|
68
146
|
OpenSSL::PKCS5.pbkdf2_hmac(key, "salt", 1000, 16, digest)
|
69
147
|
end
|
data/lib/lanet/signer.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
module Lanet
|
7
|
+
class Signer
|
8
|
+
# Error class for signature failures
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
# Signs a message using the provided private key
|
12
|
+
# @param message [String] the message to sign
|
13
|
+
# @param private_key_pem [String] the PEM-encoded private key
|
14
|
+
# @return [String] Base64-encoded signature
|
15
|
+
def self.sign(message, private_key_pem)
|
16
|
+
private_key = OpenSSL::PKey::RSA.new(private_key_pem)
|
17
|
+
signature = private_key.sign(OpenSSL::Digest.new("SHA256"), message.to_s)
|
18
|
+
Base64.strict_encode64(signature)
|
19
|
+
rescue StandardError => e
|
20
|
+
raise Error, "Signing failed: #{e.message}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Verifies a signature using the provided public key
|
24
|
+
# @param message [String] the original message
|
25
|
+
# @param signature_base64 [String] the Base64-encoded signature
|
26
|
+
# @param public_key_pem [String] the PEM-encoded public key
|
27
|
+
# @return [Boolean] true if signature is valid
|
28
|
+
def self.verify(message, signature_base64, public_key_pem)
|
29
|
+
public_key = OpenSSL::PKey::RSA.new(public_key_pem)
|
30
|
+
signature = Base64.strict_decode64(signature_base64)
|
31
|
+
public_key.verify(OpenSSL::Digest.new("SHA256"), signature, message.to_s)
|
32
|
+
rescue StandardError => e
|
33
|
+
raise Error, "Verification failed: #{e.message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generates a new RSA key pair
|
37
|
+
# @param bits [Integer] key size in bits
|
38
|
+
# @return [Hash] containing :private_key and :public_key as PEM strings
|
39
|
+
def self.generate_key_pair(bits = 2048)
|
40
|
+
key = OpenSSL::PKey::RSA.new(bits)
|
41
|
+
{
|
42
|
+
private_key: key.to_pem,
|
43
|
+
public_key: key.public_key.to_pem
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/lanet/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lanet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Davide Santangelo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- lib/lanet/receiver.rb
|
98
98
|
- lib/lanet/scanner.rb
|
99
99
|
- lib/lanet/sender.rb
|
100
|
+
- lib/lanet/signer.rb
|
100
101
|
- lib/lanet/version.rb
|
101
102
|
- sig/lanet.rbs
|
102
103
|
homepage: https://github.com/davidesantangelo/lanet
|