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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ede54bdc497c24a54cb29df640fb7c5d72f5dc1ae24ab55d88a25f10218b3664
4
- data.tar.gz: 51ab9e3cc032475fab45a863e1586412e629529ad83c0f961132f865f9382e3c
3
+ metadata.gz: 46d63ec0fb7276e1741780b97152879fb319bf42c0fd37c079b18faf355170ae
4
+ data.tar.gz: c7d07927e338e9ce4248c6909c2ec65903d23af9f09da94cf837181d14ac5ed0
5
5
  SHA512:
6
- metadata.gz: 58200a1c18fdc38e68d029d0f2558330369ff3c7ddac7c8f954a88bf71aa04fc4fcd501b4386c2586f92fb0fef2f7cb49fd0cbda805d56567e5d83fa75c0a686
7
- data.tar.gz: 165cb9a1ccf3672ed98af138e0a91149b3818b5e7043f796cb0229f5482598ddda49ffed62e9d7b4409902d73fc1f50ca4f425cc5625813446ab2219a599a645
6
+ metadata.gz: '018bc023c9d38cce80a28495df69d6ae66aeaa0977862fbab115e4eb77340337366b615ce73041b41f416c10baa4905da00ca4966008875553a1b881b8ee3678'
7
+ data.tar.gz: 4f173fb766cb88c84340c84327db204853328016be6a50cd4ea3cd183501aaabfd7f1019ba306d79e110daa5b0a6d1fe0a2a57d2bf8f0f1fec8beb2e54c52cc0
data/CHANGELOG.md CHANGED
@@ -1,25 +1,36 @@
1
- ## [Unreleased]
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 gem
7
- - Core network functionality:
8
- - Network scanning with customizable IP ranges
9
- - Network discovery with detailed host information
10
- - Host pinging with response time measurement
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lanet (0.1.0)
4
+ lanet (0.2.0)
5
5
  thor (~> 1.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # Lanet
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/lanet.svg?icon=si%3Arubygems)](https://badge.fury.io/rb/lanet)
3
+ ![Gem Version](https://img.shields.io/gem/v/lanet?style=flat)
4
+ ![Gem Total Downloads](https://img.shields.io/gem/dt/lanet?style=flat)
4
5
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
6
 
6
- A lightweight, powerful LAN communication tool for Ruby 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
+ 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
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Lanet - Lightweight LAN Communication Tool for Networking</title>
7
- <style>
8
- body {
9
- font-family: 'Arial', sans-serif;
10
- line-height: 1.6;
11
- color: #333;
12
- margin: 0;
13
- padding: 0;
14
- background-color: #f4f4f4;
15
- }
16
- .container {
17
- max-width: 1200px;
18
- margin: 0 auto;
19
- padding: 20px;
20
- }
21
- header {
22
- background: linear-gradient(90deg, #007BFF, #0056b3);
23
- color: white;
24
- padding: 40px 0;
25
- text-align: center;
26
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
27
- }
28
- header h1 {
29
- font-size: 3em;
30
- margin: 0;
31
- }
32
- header p {
33
- font-size: 1.2em;
34
- margin-top: 10px;
35
- }
36
- .badges {
37
- margin-top: 20px;
38
- }
39
- .badges img {
40
- margin: 0 10px;
41
- }
42
- section {
43
- margin: 40px 0;
44
- padding: 20px;
45
- background-color: white;
46
- border-radius: 8px;
47
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
48
- }
49
- h2 {
50
- color: #007BFF;
51
- font-size: 2em;
52
- margin-bottom: 20px;
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
- <h1>Lanet</h1>
107
- <p>A lightweight, powerful LAN communication tool for Networking</p>
108
- <div class="badges">
109
- <a href="https://badge.fury.io/rb/lanet"><img src="https://badge.fury.io/rb/lanet.svg" alt="Gem Version"></a>
110
- <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
111
- </div>
112
- </div>
113
- </header>
114
-
115
- <div class="container">
116
- <section id="about">
117
- <h2>About Lanet</h2>
118
- <p>Lanet is a lightweight and powerful LAN communication tool for Ruby that enables secure message exchange between devices on the same network. It simplifies peer-to-peer networking with built-in encryption, network discovery, and both targeted and broadcast messaging capabilities.</p>
119
- </section>
120
-
121
- <section id="features">
122
- <h2>Features</h2>
123
- <ul class="feature-list">
124
- <li>Simple API - An intuitive Ruby interface for straightforward network communication.</li>
125
- <li>Built-in encryption - Optional message encryption with AES-256-GCM for confidentiality.</li>
126
- <li>Network scanning - Automatically discover active devices on your local network.</li>
127
- <li>Targeted messaging - Send messages to specific IP addresses.</li>
128
- <li>Broadcasting - Send messages to all devices on the network.</li>
129
- <li>Host pinging - Check host availability and measure response times (with a familiar <code>ping</code> interface).</li>
130
- <li>Command-line interface - Perform common network operations directly from your terminal.</li>
131
- <li>Extensible - Easily build custom tools and integrations using the Lanet API.</li>
132
- <li>Configurable - Adjust port settings, encryption keys, and network scan ranges.</li>
133
- </ul>
134
- </section>
135
-
136
- <section id="installation">
137
- <h2>Installation</h2>
138
- <p>Add this line to your application's Gemfile:</p>
139
- <pre><code>gem 'lanet'</code></pre>
140
- <p>And then execute:</p>
141
- <pre><code>bundle install</code></pre>
142
- <p>Or install it yourself as:</p>
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
- scanner = Lanet.scanner
176
- active_ips = scanner.scan('192.168.1.0/24')
177
- puts "Found devices: #{active_ips.join(', ')}"</code></pre>
178
- </div>
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
- <section id="contributing">
212
- <h2>Contributing</h2>
213
- <p>Bug reports and pull requests are welcome on GitHub at <a href="https://github.com/davidesantangelo/lanet">https://github.com/davidesantangelo/lanet</a>. Follow these steps:</p>
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
- <section id="license">
224
- <h2>License</h2>
225
- <p>The gem is available under the <a href="https://opensource.org/licenses/MIT">MIT License</a>.</p>
226
- </section>
227
- </div>
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
- <footer>
230
- <p>&copy; 2025 Lanet. All rights reserved. | <a href="https://github.com/davidesantangelo/lanet">View on GitHub</a></p>
231
- </footer>
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 --target IP --message MSG [--key KEY] [--port PORT]", "Send a message to a specific target"
18
- option :target, required: true
19
- option :message, required: true
20
- option :key
21
- option :port, type: :numeric, default: 5000
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
- message = Encryptor.prepare_message(options[:message], options[:key])
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 --message MSG [--key KEY] [--port PORT]", "Broadcast a message to all devices"
30
- option :message, required: true
31
- option :key
32
- option :port, type: :numeric, default: 5000
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
- message = Encryptor.prepare_message(options[:message], options[:key])
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 [--port PORT] [--key KEY]", "Listen for incoming messages"
81
- option :port, type: :numeric, default: 5000
82
- option :key
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
- puts "Listening on port #{options[:port]}..."
86
- receiver.listen do |data, ip|
87
- message = Encryptor.process_message(data, options[:key])
88
- puts "From #{ip}: #{message}"
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
- desc "ping [HOST]", "Ping a host or multiple hosts with real-time output"
93
- option :host, type: :string, desc: "Single host to ping"
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
- pinger = Lanet::Ping.new(timeout: options[:timeout], count: options[:count])
129
+ receiver.listen do |data, sender_ip|
130
+ result = Lanet::Encryptor.process_message(data, options[:encryption_key], public_key)
112
131
 
113
- if target_host || options[:host]
114
- # For a single host, we use real-time output unless quiet is specified
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 options[:quiet]
126
- results = pinger.ping_hosts(hosts, false, options[:continuous])
127
- hosts.each do |host|
128
- display_ping_summary(host, results[host])
129
- puts "\n" unless host == hosts.last
130
- end
131
- else
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)
@@ -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
- # Encrypts a message if key is provided, otherwise marks it as plaintext
23
+ # Prepares a message with encryption and/or signing
19
24
  # @param message [String] the message to prepare
20
- # @param key [String, nil] encryption key or nil for plaintext
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, key)
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 key [String, nil] decryption key or nil
41
- # @return [String] processed message
42
- def self.process_message(data, key)
43
- return "[Empty message]" if data.nil? || data.empty?
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
- content = data[1..]
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 key.nil? || key.strip.empty?
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, key)
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lanet
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
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.1.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-06 00:00:00.000000000 Z
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