msid 0.1.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 +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +22 -0
- data/LICENSE +21 -0
- data/README.md +169 -0
- data/Rakefile +11 -0
- data/lib/msid/version.rb +5 -0
- data/lib/msid.rb +248 -0
- data/msid.gemspec +28 -0
- metadata +55 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bf25c3ca34367f6073a84e93f267d835f34f56775664a23dc913d2143f9b5b61
|
|
4
|
+
data.tar.gz: 94ca6e504098733c75dd7017a42343d5ee41af696c9acc95f7b44824d9b15ba4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7652b3c1bce0ad68794440ce3a6d8a572d038ae38ef4c22e47af028e1a40f0aac148a969c9255e4dd7e1adec0605eaf84c76dcb9d305ba8346d93ab36f4fa952
|
|
7
|
+
data.tar.gz: 1675bb45cc39e7060bb3eecdd0ff64b1362b6761c0304e384759695a24b61a62cf078bd826a0d9e0a081475664403e924c832e6fa2daf18f03fefeaa6b4c8fa1
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
msid (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
minitest (5.25.5)
|
|
10
|
+
rake (13.3.0)
|
|
11
|
+
|
|
12
|
+
PLATFORMS
|
|
13
|
+
arm64-darwin-24
|
|
14
|
+
ruby
|
|
15
|
+
|
|
16
|
+
DEPENDENCIES
|
|
17
|
+
minitest (~> 5.0)
|
|
18
|
+
msid!
|
|
19
|
+
rake (~> 13.0)
|
|
20
|
+
|
|
21
|
+
BUNDLED WITH
|
|
22
|
+
2.6.3
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Davide Santangelo
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# msid
|
|
2
|
+
|
|
3
|
+
`msid` is a Ruby gem for generating a unique and secure machine fingerprint ID. It gathers a wide range of system identifiers to create a hash that is highly specific to the machine it's run on.
|
|
4
|
+
|
|
5
|
+
This is useful for licensing, device identification, or any scenario where you need to reliably identify a specific machine.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
`msid` collects the following system information:
|
|
10
|
+
- Hostname
|
|
11
|
+
- All MAC addresses
|
|
12
|
+
- CPU model and core count
|
|
13
|
+
- Total RAM
|
|
14
|
+
- OS and kernel information
|
|
15
|
+
- Hardware Serial Number
|
|
16
|
+
- Hardware UUID
|
|
17
|
+
- Motherboard Serial Number
|
|
18
|
+
- Root disk volume UUID
|
|
19
|
+
- System Model Identifier
|
|
20
|
+
- GPU Information
|
|
21
|
+
- BIOS/Firmware Information
|
|
22
|
+
- Serial numbers of all physical disks
|
|
23
|
+
|
|
24
|
+
It then combines these components into a single string and uses the SHA-256 algorithm to produce a consistent, unique fingerprint.
|
|
25
|
+
|
|
26
|
+
## Use Cases
|
|
27
|
+
|
|
28
|
+
Here are some practical scenarios where `msid` can be valuable:
|
|
29
|
+
|
|
30
|
+
### Software Licensing
|
|
31
|
+
- **Offline License Activation**: Bind software licenses to specific machines without requiring constant internet validation.
|
|
32
|
+
- **Trial Period Management**: Track installations to prevent users from repeatedly installing trial versions.
|
|
33
|
+
- **License Auditing**: Keep track of which machines have your software installed for compliance purposes.
|
|
34
|
+
|
|
35
|
+
### Security Applications
|
|
36
|
+
- **Multi-factor Authentication**: Add an extra layer of security by validating not just "something you know" (password) but also "something you have" (the specific device).
|
|
37
|
+
- **Session Security**: Detect when a user's session moves to a different machine, potentially indicating session hijacking.
|
|
38
|
+
- **Fraud Detection**: Flag accounts that suddenly access your service from multiple different machines in rapid succession.
|
|
39
|
+
|
|
40
|
+
### System Management
|
|
41
|
+
- **Asset Tracking**: Inventory management for IT departments to track hardware across an organization.
|
|
42
|
+
- **Configuration Management**: Associate specific configurations or settings with particular machines.
|
|
43
|
+
- **Environment-specific Features**: Enable or disable features based on the hardware environment.
|
|
44
|
+
|
|
45
|
+
### DevOps and Deployment
|
|
46
|
+
- **Environment Fingerprinting**: Distinguish between development, staging, and production environments.
|
|
47
|
+
- **Deployment Validation**: Ensure that deployments only happen from authorized build machines.
|
|
48
|
+
- **Infrastructure Auditing**: Track which servers or virtual machines are running your applications.
|
|
49
|
+
|
|
50
|
+
### Usage Analytics
|
|
51
|
+
- **Device Demographics**: Gather anonymous statistics about what kinds of machines are running your software.
|
|
52
|
+
- **Installation Metrics**: Track unique installations versus reinstallations.
|
|
53
|
+
- **Hardware Compatibility**: Identify hardware configurations where your software performs well or poorly.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
Add this line to your application's Gemfile:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
gem 'msid'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
And then execute:
|
|
64
|
+
|
|
65
|
+
$ bundle install
|
|
66
|
+
|
|
67
|
+
Or install it yourself as:
|
|
68
|
+
|
|
69
|
+
$ gem install msid
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
### From Ruby
|
|
74
|
+
|
|
75
|
+
You can generate the machine ID from your Ruby code.
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
require 'msid'
|
|
79
|
+
|
|
80
|
+
# Generate the default ID
|
|
81
|
+
machine_id = Msid.generate
|
|
82
|
+
puts machine_id
|
|
83
|
+
# => "a1b2c3d4..."
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Using a Salt
|
|
87
|
+
|
|
88
|
+
For enhanced security, you can provide a `salt`. This is useful if you want to generate different IDs for different applications on the same machine.
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
require 'msid'
|
|
92
|
+
|
|
93
|
+
# Generate an ID with a salt
|
|
94
|
+
app_specific_id = Msid.generate(salt: 'my-super-secret-app-key')
|
|
95
|
+
puts app_specific_id
|
|
96
|
+
# => "e5f6g7h8..." (different from the one without a salt)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### With IRB (for local development)
|
|
100
|
+
|
|
101
|
+
If you are developing the gem locally, you can test it with `irb`. From the root directory of the gem, run `irb` with the `-I` flag to add the `lib` directory to Ruby's load path.
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
$ irb -I lib
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then, inside `irb`, you can require and use the gem:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
irb(main):001:0> require 'msid'
|
|
111
|
+
=> true
|
|
112
|
+
irb(main):002:0> Msid.generate
|
|
113
|
+
=> "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### In a Rails Application
|
|
117
|
+
|
|
118
|
+
To use `msid` in a Rails application, add it to your `Gemfile`.
|
|
119
|
+
|
|
120
|
+
If you are developing the gem locally, you can use the `path` option:
|
|
121
|
+
```ruby
|
|
122
|
+
# Gemfile
|
|
123
|
+
gem 'msid', path: '/path/to/your/msid/gem'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If the gem is on RubyGems, add it normally:
|
|
127
|
+
```ruby
|
|
128
|
+
# Gemfile
|
|
129
|
+
gem 'msid'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Then run `bundle install`.
|
|
133
|
+
|
|
134
|
+
Now you can use it anywhere in your Rails application, including the Rails console:
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
$ rails console
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
# In Rails console
|
|
142
|
+
irb(main):001:0> Msid.generate
|
|
143
|
+
=> "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
|
|
144
|
+
|
|
145
|
+
# For extra security, you can use a Rails secret as a salt
|
|
146
|
+
irb(main):002:0> Msid.generate(salt: Rails.application.credentials.secret_key_base)
|
|
147
|
+
=> "f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e2"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### From the Command Line
|
|
151
|
+
|
|
152
|
+
The gem provides a command-line executable to easily retrieve the machine ID.
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
$ msid
|
|
156
|
+
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests.
|
|
162
|
+
|
|
163
|
+
## Contributing
|
|
164
|
+
|
|
165
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/davidesantangelo/msid.
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/msid/version.rb
ADDED
data/lib/msid.rb
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'msid/version'
|
|
4
|
+
require 'socket'
|
|
5
|
+
require 'digest'
|
|
6
|
+
require 'open3'
|
|
7
|
+
|
|
8
|
+
module Msid
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
|
|
11
|
+
# Generates a unique machine fingerprint ID.
|
|
12
|
+
#
|
|
13
|
+
# The fingerprint is created by collecting various system identifiers,
|
|
14
|
+
# combining them into a single string, and then hashing it with SHA-256.
|
|
15
|
+
# This makes the ID highly unique to the machine it's generated on.
|
|
16
|
+
class Generator
|
|
17
|
+
class << self
|
|
18
|
+
# Gathers a collection of machine-specific identifiers.
|
|
19
|
+
# @return [Array<String>] a list of identifiers.
|
|
20
|
+
def gather_components
|
|
21
|
+
[
|
|
22
|
+
hostname,
|
|
23
|
+
all_mac_addresses,
|
|
24
|
+
cpu_info,
|
|
25
|
+
cpu_cores,
|
|
26
|
+
total_memory,
|
|
27
|
+
os_info,
|
|
28
|
+
serial_number,
|
|
29
|
+
hardware_uuid,
|
|
30
|
+
baseboard_serial,
|
|
31
|
+
disk_uuid,
|
|
32
|
+
system_model,
|
|
33
|
+
gpu_info,
|
|
34
|
+
bios_info,
|
|
35
|
+
all_disk_serials
|
|
36
|
+
].compact.reject(&:empty?)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Generates the unique machine ID.
|
|
40
|
+
# @param salt [String, nil] An optional salt to add to the fingerprint.
|
|
41
|
+
# @return [String] The SHA-256 hash representing the machine ID.
|
|
42
|
+
def generate(salt: nil)
|
|
43
|
+
components = gather_components
|
|
44
|
+
raise Error, 'Could not gather any machine identifiers.' if components.empty?
|
|
45
|
+
|
|
46
|
+
components.push(salt.to_s) if salt
|
|
47
|
+
|
|
48
|
+
data_string = components.join(':')
|
|
49
|
+
Digest::SHA256.hexdigest(data_string)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Executes a shell command and returns its stripped stdout.
|
|
55
|
+
# Returns nil if the command fails or an error occurs.
|
|
56
|
+
def run_command(command)
|
|
57
|
+
stdout, _stderr, status = Open3.capture3(command)
|
|
58
|
+
status.success? ? stdout.strip : nil
|
|
59
|
+
rescue StandardError
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [String, nil] The machine's hostname.
|
|
64
|
+
def hostname
|
|
65
|
+
Socket.gethostname
|
|
66
|
+
rescue StandardError
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @return [String, nil] A sorted, concatenated string of all MAC addresses.
|
|
71
|
+
def all_mac_addresses
|
|
72
|
+
macs = case RUBY_PLATFORM
|
|
73
|
+
when /darwin/ # macOS
|
|
74
|
+
run_command("ifconfig -a | grep -o -E '([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}'")&.split("\n")
|
|
75
|
+
when /linux/
|
|
76
|
+
# Read from /sys/class/net, which is more reliable than parsing `ip` command output
|
|
77
|
+
begin
|
|
78
|
+
Dir.glob('/sys/class/net/*/address').map { |f| File.read(f).strip }
|
|
79
|
+
rescue StandardError
|
|
80
|
+
[]
|
|
81
|
+
end
|
|
82
|
+
when /mswin|mingw/ # Windows
|
|
83
|
+
run_command('getmac /v /fo csv | findstr /V "Disconnected"')
|
|
84
|
+
&.lines
|
|
85
|
+
&.map { |line| line.split(',')[2]&.gsub('"', '')&.strip }
|
|
86
|
+
else
|
|
87
|
+
[]
|
|
88
|
+
end
|
|
89
|
+
macs&.compact&.reject(&:empty?)&.sort&.join(',')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [String, nil] CPU model information.
|
|
93
|
+
def cpu_info
|
|
94
|
+
case RUBY_PLATFORM
|
|
95
|
+
when /darwin/
|
|
96
|
+
run_command('sysctl -n machdep.cpu.brand_string')
|
|
97
|
+
when /linux/
|
|
98
|
+
run_command("grep 'model name' /proc/cpuinfo | uniq | awk -F': ' '{print $2}'")
|
|
99
|
+
when /mswin|mingw/
|
|
100
|
+
run_command('wmic cpu get name /format:list | findstr "Name="')&.gsub('Name=', '')
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @return [String, nil] Number of CPU cores.
|
|
105
|
+
def cpu_cores
|
|
106
|
+
case RUBY_PLATFORM
|
|
107
|
+
when /darwin/
|
|
108
|
+
run_command('sysctl -n hw.ncpu')
|
|
109
|
+
when /linux/
|
|
110
|
+
run_command('nproc')
|
|
111
|
+
when /mswin|mingw/
|
|
112
|
+
run_command('wmic cpu get NumberOfCores | findstr /V "NumberOfCores"')
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @return [String, nil] Total physical memory in bytes or KB.
|
|
117
|
+
def total_memory
|
|
118
|
+
case RUBY_PLATFORM
|
|
119
|
+
when /darwin/
|
|
120
|
+
run_command('sysctl -n hw.memsize')
|
|
121
|
+
when /linux/
|
|
122
|
+
run_command("grep MemTotal /proc/meminfo | awk '{print $2}'")
|
|
123
|
+
when /mswin|mingw/
|
|
124
|
+
run_command('wmic ComputerSystem get TotalPhysicalMemory | findstr /V "TotalPhysicalMemory"')
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @return [String, nil] Operating system and kernel information.
|
|
129
|
+
def os_info
|
|
130
|
+
kernel_info = case RUBY_PLATFORM
|
|
131
|
+
when /mswin|mingw/
|
|
132
|
+
run_command('ver')
|
|
133
|
+
else
|
|
134
|
+
run_command('uname -r')
|
|
135
|
+
end
|
|
136
|
+
"#{RUBY_PLATFORM}-#{kernel_info}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @return [String, nil] The machine's hardware serial number.
|
|
140
|
+
def serial_number
|
|
141
|
+
case RUBY_PLATFORM
|
|
142
|
+
when /darwin/
|
|
143
|
+
run_command("system_profiler SPHardwareDataType | awk '/Serial Number/{print $4}'")
|
|
144
|
+
when /linux/
|
|
145
|
+
run_command('cat /sys/class/dmi/id/product_serial')
|
|
146
|
+
when /mswin|mingw/
|
|
147
|
+
run_command('wmic bios get serialnumber | findstr /V "SerialNumber"')
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @return [String, nil] The machine's hardware UUID.
|
|
152
|
+
def hardware_uuid
|
|
153
|
+
case RUBY_PLATFORM
|
|
154
|
+
when /darwin/
|
|
155
|
+
run_command("system_profiler SPHardwareDataType | awk '/Hardware UUID/ {print $3}'")
|
|
156
|
+
when /linux/
|
|
157
|
+
run_command('cat /sys/class/dmi/id/product_uuid')
|
|
158
|
+
when /mswin|mingw/
|
|
159
|
+
run_command('wmic csproduct get uuid | findstr /V "UUID"')
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @return [String, nil] The machine's baseboard serial number.
|
|
164
|
+
def baseboard_serial
|
|
165
|
+
case RUBY_PLATFORM
|
|
166
|
+
when /darwin/
|
|
167
|
+
nil # Not easily available/distinct from system serial on macOS
|
|
168
|
+
when /linux/
|
|
169
|
+
run_command('cat /sys/class/dmi/id/board_serial')
|
|
170
|
+
when /mswin|mingw/
|
|
171
|
+
run_command('wmic baseboard get serialnumber | findstr /V "SerialNumber"')
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# @return [String, nil] The UUID of the root volume.
|
|
176
|
+
def disk_uuid
|
|
177
|
+
case RUBY_PLATFORM
|
|
178
|
+
when /darwin/
|
|
179
|
+
run_command("diskutil info / | awk '/Volume UUID/{print $3}'")
|
|
180
|
+
when /linux/
|
|
181
|
+
device = run_command("df / | tail -n1 | awk '{print $1}'")
|
|
182
|
+
run_command("lsblk -no UUID #{device}") if device
|
|
183
|
+
when /mswin|mingw/
|
|
184
|
+
run_command('vol c: | findstr "Serial Number"')&.split&.last
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @return [String, nil] The machine's system model identifier.
|
|
189
|
+
def system_model
|
|
190
|
+
case RUBY_PLATFORM
|
|
191
|
+
when /darwin/
|
|
192
|
+
run_command("system_profiler SPHardwareDataType | awk '/Model Identifier/ {print $3}'")
|
|
193
|
+
when /linux/
|
|
194
|
+
run_command('cat /sys/class/dmi/id/product_name')
|
|
195
|
+
when /mswin|mingw/
|
|
196
|
+
run_command('wmic csproduct get name | findstr /V "Name"')
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# @return [String, nil] The machine's GPU information.
|
|
201
|
+
def gpu_info
|
|
202
|
+
case RUBY_PLATFORM
|
|
203
|
+
when /darwin/
|
|
204
|
+
run_command("system_profiler SPDisplaysDataType | grep 'Chipset Model:' | awk -F': ' '{print $2}'")
|
|
205
|
+
when /linux/
|
|
206
|
+
run_command("lspci | grep -i 'vga\\|3d\\|2d' | head -n 1 | awk -F': ' '{print $3}'")
|
|
207
|
+
when /mswin|mingw/
|
|
208
|
+
run_command('wmic path win32_videocontroller get name | findstr /V "Name"')
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @return [String, nil] The machine's BIOS/firmware information.
|
|
213
|
+
def bios_info
|
|
214
|
+
case RUBY_PLATFORM
|
|
215
|
+
when /darwin/
|
|
216
|
+
run_command("system_profiler SPHardwareDataType | awk '/Boot ROM Version/ {print $4}'")
|
|
217
|
+
when /linux/
|
|
218
|
+
vendor = run_command('cat /sys/class/dmi/id/bios_vendor')
|
|
219
|
+
version = run_command('cat /sys/class/dmi/id/bios_version')
|
|
220
|
+
"#{vendor}-#{version}" if vendor && version
|
|
221
|
+
when /mswin|mingw/
|
|
222
|
+
run_command('wmic bios get manufacturer,version /format:list')
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# @return [String, nil] A sorted, concatenated string of all physical disk serial numbers.
|
|
227
|
+
def all_disk_serials
|
|
228
|
+
serials = case RUBY_PLATFORM
|
|
229
|
+
when /darwin/
|
|
230
|
+
run_command("system_profiler SPStorageDataType | grep 'Serial Number:' | awk '{print $3}'")&.split("\n")
|
|
231
|
+
when /linux/
|
|
232
|
+
run_command('lsblk -d -o serial -n')&.split("\n")
|
|
233
|
+
when /mswin|mingw/
|
|
234
|
+
run_command('wmic diskdrive get serialnumber | findstr /V "SerialNumber"')&.split("\n")
|
|
235
|
+
else
|
|
236
|
+
[]
|
|
237
|
+
end
|
|
238
|
+
serials&.compact&.map(&:strip)&.reject(&:empty?)&.sort&.join(',')
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Shortcut for Msid::Generator.generate
|
|
244
|
+
# @param salt [String, nil] An optional salt to add to the fingerprint.
|
|
245
|
+
def self.generate(salt: nil)
|
|
246
|
+
Generator.generate(salt: salt)
|
|
247
|
+
end
|
|
248
|
+
end
|
data/msid.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/msid/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'msid'
|
|
7
|
+
spec.version = Msid::VERSION
|
|
8
|
+
spec.authors = ['Davide Santangelo']
|
|
9
|
+
spec.email = ['']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'Generates a unique machine fingerprint ID.'
|
|
12
|
+
spec.description = 'Creates a unique and secure machine fingerprint ID by gathering various system hardware and software identifiers. Designed to be difficult to replicate on another machine.'
|
|
13
|
+
spec.homepage = 'https://github.com/davidesantangelo/msid'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.0'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
19
|
+
|
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
24
|
+
end
|
|
25
|
+
spec.bindir = 'exe'
|
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: msid
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Davide Santangelo
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-06-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Creates a unique and secure machine fingerprint ID by gathering various
|
|
14
|
+
system hardware and software identifiers. Designed to be difficult to replicate
|
|
15
|
+
on another machine.
|
|
16
|
+
email:
|
|
17
|
+
- ''
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- Gemfile
|
|
23
|
+
- Gemfile.lock
|
|
24
|
+
- LICENSE
|
|
25
|
+
- README.md
|
|
26
|
+
- Rakefile
|
|
27
|
+
- lib/msid.rb
|
|
28
|
+
- lib/msid/version.rb
|
|
29
|
+
- msid.gemspec
|
|
30
|
+
homepage: https://github.com/davidesantangelo/msid
|
|
31
|
+
licenses:
|
|
32
|
+
- MIT
|
|
33
|
+
metadata:
|
|
34
|
+
homepage_uri: https://github.com/davidesantangelo/msid
|
|
35
|
+
source_code_uri: https://github.com/davidesantangelo/msid
|
|
36
|
+
post_install_message:
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '3.0'
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0'
|
|
50
|
+
requirements: []
|
|
51
|
+
rubygems_version: 3.3.26
|
|
52
|
+
signing_key:
|
|
53
|
+
specification_version: 4
|
|
54
|
+
summary: Generates a unique machine fingerprint ID.
|
|
55
|
+
test_files: []
|