From 16f14789cecb555647eca2680d2d3fa968fa459c Mon Sep 17 00:00:00 2001 From: harlekin <5800926-mw102@users.noreply.gitlab.com> Date: Tue, 4 Jan 2022 17:25:41 +0000 Subject: [PATCH] Feat: add hardware uuid --- protocol.txt | 1 + src/com/sheepit/client/Server.java | 2 + .../hardware/hwid/BasicHWInfoStrategy.java | 10 +++ .../client/hardware/hwid/HWIdentifier.java | 74 +++++++++++++++++++ .../hardware/hwid/impl/BaseHWInfoImpl.java | 74 +++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 src/com/sheepit/client/hardware/hwid/BasicHWInfoStrategy.java create mode 100644 src/com/sheepit/client/hardware/hwid/HWIdentifier.java create mode 100644 src/com/sheepit/client/hardware/hwid/impl/BaseHWInfoImpl.java diff --git a/protocol.txt b/protocol.txt index e430ad7..957781b 100644 --- a/protocol.txt +++ b/protocol.txt @@ -18,6 +18,7 @@ Parameters as GET: * hostname (optional): Hostname of the machine, useful for distinguishing multiple machines with the same hardware configuration. Only used for display on the website. * ui (optional): User interface used by the client, useful for statistics * headless: Is the machine is running headless (Eevee is not compatible with headless) + * hwid: a pseudonymous hardware hash Answer in case of error: diff --git a/src/com/sheepit/client/Server.java b/src/com/sheepit/client/Server.java index fc4179c..1ac7703 100644 --- a/src/com/sheepit/client/Server.java +++ b/src/com/sheepit/client/Server.java @@ -40,6 +40,7 @@ import java.util.stream.Collectors; import com.sheepit.client.datamodel.SpeedTestTarget; import com.sheepit.client.datamodel.SpeedTestResult; import com.sheepit.client.datamodel.SpeedTestTargetResult; +import com.sheepit.client.hardware.hwid.HWIdentifier; import com.sheepit.client.os.Windows; import lombok.Getter; import org.simpleframework.xml.core.Persister; @@ -213,6 +214,7 @@ public class Server extends Thread { .add("ui", client.getGui().getClass().getSimpleName()) .add("extras", user_config.getExtras()) .add("headless", java.awt.GraphicsEnvironment.isHeadless() ? "1" : (user_config.isHeadless() ? "1" : "0")) + .add("hwid", new HWIdentifier(log).getHardwareHash()) .build(); this.log.debug("Server::getConfiguration url " + remoteURL.build().toString()); diff --git a/src/com/sheepit/client/hardware/hwid/BasicHWInfoStrategy.java b/src/com/sheepit/client/hardware/hwid/BasicHWInfoStrategy.java new file mode 100644 index 0000000..df9521c --- /dev/null +++ b/src/com/sheepit/client/hardware/hwid/BasicHWInfoStrategy.java @@ -0,0 +1,10 @@ +package com.sheepit.client.hardware.hwid; + +import java.util.Optional; + +public interface BasicHWInfoStrategy { + + Optional getHarddriveID(); + Optional getMAC(); + Optional getProcessorName(); +} diff --git a/src/com/sheepit/client/hardware/hwid/HWIdentifier.java b/src/com/sheepit/client/hardware/hwid/HWIdentifier.java new file mode 100644 index 0000000..e83eef3 --- /dev/null +++ b/src/com/sheepit/client/hardware/hwid/HWIdentifier.java @@ -0,0 +1,74 @@ +package com.sheepit.client.hardware.hwid; + +import com.sheepit.client.Log; +import com.sheepit.client.hardware.hwid.impl.BaseHWInfoImpl; + +import java.io.File; +import java.math.BigInteger; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +public class HWIdentifier { + private final BasicHWInfoStrategy strategy; + private Log log; + + public HWIdentifier(Log log) { + strategy = new BaseHWInfoImpl(); + this.log = log; + } + + public String getMAC() { + return strategy.getMAC().orElse(""); + } + + public String getHarddriveSerial() { + return strategy.getHarddriveID().orElse(""); + } + + public String getProcessorName() { + return strategy.getProcessorName().orElse(""); + } + + public String getHardwareHash() { + byte[] hash; + String mac; + String cpuName; + String hdSerial; + + try { + MessageDigest digest = MessageDigest.getInstance("md5"); + if ((hdSerial = getHarddriveSerial()).length() > 0) { + hash = digest.digest(hdSerial.getBytes(StandardCharsets.UTF_8)); + log.debug("HWIdentifier::getHardwareHash found hdd hash"); + } + else if ((mac = getMAC()).length() > 0) { + hash = digest.digest(mac.getBytes(StandardCharsets.UTF_8)); + log.debug("HWIdentifier::getHardwareHash found MAC hash"); + } + else { //Fallback: computing a hash out of homepath+jarFileLocation+cpuName + log.debug("HWIdentifier::getHardwareHash using fallback method"); + cpuName = getProcessorName(); + if (cpuName.isEmpty()) { + log.error("HWIdentifier::getHardwareHash failed to retrieve CPU name. Can't create hardware hash"); + throw new UnsupportedOperationException("Unable to create hash!"); + } + + String homeDir = System.getProperty("user.home"); //get home path + URL clientURL = getClass().getProtectionDomain().getCodeSource().getLocation(); + String clientPath = new File(clientURL.toString()).getParent(); //get jar file location + + hash = digest.digest((homeDir + clientPath + cpuName).getBytes(StandardCharsets.UTF_8)); + } + + BigInteger num = new BigInteger(1, hash); + return num.toString(16); + + } + catch (Exception e) { + e.printStackTrace(); + log.error("HWIdentifier::getHardwareHash could not retrieve hash: " + e); + return "unknown"; + } + } +} diff --git a/src/com/sheepit/client/hardware/hwid/impl/BaseHWInfoImpl.java b/src/com/sheepit/client/hardware/hwid/impl/BaseHWInfoImpl.java new file mode 100644 index 0000000..5ba2fc1 --- /dev/null +++ b/src/com/sheepit/client/hardware/hwid/impl/BaseHWInfoImpl.java @@ -0,0 +1,74 @@ +package com.sheepit.client.hardware.hwid.impl; + +import com.sheepit.client.hardware.hwid.BasicHWInfoStrategy; +import com.sun.jna.Platform; +import oshi.SystemInfo; +import oshi.hardware.HWDiskStore; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.hardware.NetworkIF; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +public class BaseHWInfoImpl implements BasicHWInfoStrategy { + protected HardwareAbstractionLayer hardware; + + public BaseHWInfoImpl() { + hardware = new SystemInfo().getHardware(); + } + + @Override + public Optional getMAC() { + List macs = new ArrayList<>(); + List nics = hardware.getNetworkIFs(); + for (NetworkIF nic : nics) { + macs.add(nic.getMacaddr()); + } + Collections.sort(macs); + return Optional.of(String.join(" ", macs)); + } + + @Override + public Optional getProcessorName() { + return Optional.of(hardware.getProcessor().getProcessorIdentifier().getName()); + } + + public Optional getHarddriveID() { + String rootMountpoint; + if (Platform.isWindows()) { + rootMountpoint = "C:"; + } + else { + rootMountpoint = "/"; + } + return getHarddriveID(rootMountpoint); + } + + /** + * Tries to find the root partition and returns that hard drives serial + * @param rootMountpoint + * @return + */ + private Optional getHarddriveID(String rootMountpoint) { + var drives = hardware.getDiskStores(); + + String hddSerial = ""; + boolean rootFound = false; + Iterator iterator = drives.iterator(); + + while (rootFound == false && iterator.hasNext()) { + var drive = iterator.next(); + for (var partition : drive.getPartitions()) { + if (partition.getMountPoint().equals(rootMountpoint)) { + hddSerial = drive.getSerial(); + rootFound = true; + break; + } + } + } + return hddSerial.isEmpty() ? Optional.empty() : Optional.of(hddSerial); + } +}