Intro
This post sprung up due to the nature of the unofficial macOS Rtools toolchain being an “online-based” installer. Meaning, it will attempt to download the appropriate binaries from CRAN, the official gfortran site, and Apple. In particular, Simon Urbanek, who maintains the official CRAN macOS R binary installer, remarked on R-SIG-Mac that the installer is “potentially unsafe and dangerous.”
Unfortunately, there was one flaw in the initial “online” installer: it didn’t check whether the binary downloaded was really the official variant. That is, the old approach taken allowed for a “man-in-the-middle attack.” This kind of attack arises since the request could be intercepted and re-routed by a nefarious actor monitoring web traffic. The actor could then supply a modified binary and the installer wouldn’t know. Thus, the user at the end of the process had the potential to receive software that may be malicious in nature.
With this being said, there are multiple ways to combat a “man-in-the-middle” attack. One way of the common approaches is to check a pre-defined hash against the downloaded file hash. If the hashes are the same, allow the installation to continue. Otherwise, stop the installation and throw an error.
Below are two bash functions that implement the necessary logic to check hashes. These should work on all macOS installations regardless of whether or not Xcode Command Line Tools are installed.
Hash Check Functions
The hash functions perform a string comparison check on a supplied hash (e.g. built into the installer) and the generated file hash obtained from the downloaded file. If the hashes match, return true (e.g. 0
). Otherwise, throw an error with exit 1
.
Checking an MD5 Hash
The first function is geared to checking an MD5 hash. It uses md5
and awk
to extract the hash value. The awk formatting assumes a hash of:
# MD5 (path/to/file.pkg) = c29700c4e7b2914073ef7e741eb105bc
# 1 % 2 %3% 4
MD5 Check Implementation
#1 Path to File
#2 md5 Hash to Check Against
check_md5_hash() {
# Obtain file hash
FILE_HASH=$(md5 $1 | awk '{print $4}')
# Check against hash
if [ "$FILE_HASH" = "$2" ]; then
echo 0
else
exit 1
fi
}
Checking an SHA 256 Hash
The second function is geared to checking an SHA 256 hash. It uses shasum
and awk
to extract the hash value. The awk formatting assumes a hash of:
# 687d2cf2d665614daa45005b1d1ca2942c73575e3372f94d3b60d4d7e6a8f732 path/to/file.pkg
# 1 % 2
SHA 256 Check Implementation
#1 Path to File
#2 SHA 256 Hash to Check Against
check_sha256_hash() {
# Obtain file hash
FILE_HASH=$(shasum -a 256 $1 | awk '{print $1}')
# Check against hash
if [ "$FILE_HASH" = "$2" ]; then
echo 0
else
exit 1
fi
}