MacAddress.java

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
 * Disclaimer:
 *      This class is copied from https://github.com/Microsoft/azure-tools-for-java/ with minor modification (fixing
 *      static analysis error).
 *      Location in the repo: /Utils/azuretools-core/src/com/microsoft/azuretools/azurecommons/util/MacAddress.java
 */

package com.azure.spring.data.cosmos.common;

import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Mac address class to transfer mac address to hash mac address.
 */
public final class MacAddress {

    private static final String UNKNOWN_MAC_ADDRESS = "Unknown-Mac-Address";
    private static final String MAC_REGEX = "([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}";
    private static final String MAC_REGEX_ZERO = "([0]{2}[:-]){5}[0]{2}";
    private static final String HASHED_MAC_REGEX = "[0-9a-f]{64}";

    private MacAddress() {
    }

    private static boolean isValidHashMacFormat(@NonNull String hashMac) {
        if (hashMac.isEmpty()) {
            return false;
        }

        return Pattern.compile(HASHED_MAC_REGEX).matcher(hashMac).matches();
    }

    private static String getRawMac() {
        final List<String> commands;
        final String os = System.getProperty("os.name");
        final StringBuilder macBuilder = new StringBuilder();

        if (os != null
                && !os.isEmpty()
                && os.toLowerCase(Locale.US).startsWith("win")) {
            commands = Collections.singletonList("getmac");
        } else {
            commands = Arrays.asList("ifconfig", "-a");
        }

        try {
            String tmp;
            final ProcessBuilder builder = new ProcessBuilder(commands);
            final Process process = builder.start();
            final InputStreamReader streamReader = new InputStreamReader(process.getInputStream(),
                StandardCharsets.UTF_8);

            try {
                final BufferedReader reader = new BufferedReader(streamReader);
                try {
                    while ((tmp = reader.readLine()) != null) {
                        macBuilder.append(tmp);
                    }
                } finally {
                    reader.close();
                }
            } finally {
                streamReader.close();
            }
        } catch (IOException e) {
            return "";
        }

        return macBuilder.toString();
    }

    private static String getHexDigest(byte digest) {
        final String hex = Integer.toString((digest & 0xff) + 0x100, 16);

        return hex.substring(1);
    }

    private static String hash(@NonNull String mac) {
        if (mac.isEmpty()) {
            return "";
        }

        final StringBuilder builder = new StringBuilder();

        try {
            final MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");

            messageDigest.update(mac.getBytes(StandardCharsets.UTF_8));

            final byte[] digestBytes = messageDigest.digest();

            for (final byte digest : digestBytes) {
                builder.append(getHexDigest(digest));
            }
        } catch (NoSuchAlgorithmException ex) {
            return "";
        }

        Assert.isTrue(isValidHashMacFormat(builder.toString()), "Invalid format for HashMac");

        return builder.toString();
    }

    /**
     * To get a hash Mac address.
     *
     * @return String Hash mac address
     */
    public static String getHashMac() {
        final String rawMac = getRawMac();

        if (rawMac.isEmpty()) {
            return UNKNOWN_MAC_ADDRESS;
        }

        final Pattern pattern = Pattern.compile(MAC_REGEX);
        final Pattern patternZero = Pattern.compile(MAC_REGEX_ZERO);
        final Matcher matcher = pattern.matcher(rawMac);

        String mac = "";

        while (matcher.find()) {
            mac = matcher.group(0);

            if (!patternZero.matcher(mac).matches()) {
                break;
            }
        }

        final String hashMac = hash(mac);

        if (StringUtils.hasText(hashMac)) {
            return hashMac;
        }

        return UNKNOWN_MAC_ADDRESS;
    }
}