diff --git a/Common/DtaDev.cpp b/Common/DtaDev.cpp index f581ba46..48018c03 100644 --- a/Common/DtaDev.cpp +++ b/Common/DtaDev.cpp @@ -32,6 +32,7 @@ along with sedutil. If not, see . #include "DtaConstants.h" #include "DtaEndianFixup.h" #include "DtaHexDump.h" +#include "DtaHashPwd.h" using namespace std; @@ -313,6 +314,24 @@ void DtaDev::discovery0() while (cpos < epos); } + +uint8_t DtaDev::printPasswordHash(char * password) +{ + LOG(D1) << "Entering DtaDev::printPasswordHash()"; + vector hash; + DtaHashPwd(hash, password, this); + + /* std::hex overwrites flags; save them, so we do not alter other output later */ + ios_base::fmtflags saved_flags = cout.flags(); + + /* First two bytes are actually the opal header */ + for (size_t i = 2; i < hash.size(); ++i) + cout << hex << setfill('0') << setw(2) << (int)hash[i]; + cout << endl; + cout.flags(saved_flags); + return 0; +} + void DtaDev::puke() { LOG(D1) << "Entering DtaDev::puke()"; @@ -487,3 +506,9 @@ void DtaDev::puke() if (disk_info.Unknown) cout << "**** " << (uint16_t)disk_info.Unknown << " **** Unknown function codes IGNORED " << std::endl; } + +uint8_t DtaDev::prepareForS3Sleep(uint8_t lockingrange, char* password) +{ + LOG(E) << "S3 sleep not supported on this platform"; + return 1; +} diff --git a/Common/DtaDev.h b/Common/DtaDev.h index 04096a2a..3dd15263 100644 --- a/Common/DtaDev.h +++ b/Common/DtaDev.h @@ -84,6 +84,9 @@ class DtaDev { */ void discovery0(); + /** Print password hash, computed with this device's serial number + */ + uint8_t printPasswordHash(char * password); /* * virtual methods required in the OS specific * device class @@ -259,6 +262,11 @@ class DtaDev { * @param password Password of administrative authority for locking range */ virtual uint8_t eraseLockingRange(uint8_t lockingrange, char * password) = 0; + /** Optionally implemented s3 sleep support. + * On Linux, it saves the password to the kernel to use on resume. + * @param password the password to save to the kernel + */ + virtual uint8_t prepareForS3Sleep(uint8_t lockingrange, char* password); /** Dumps an object for diagnostic purposes * @param sp index into the OPALUID table for the SP the object is in * @param auth the authority to use for the dump @@ -294,6 +302,7 @@ class DtaDev { /** return the communications ID to be used for sessions to this device */ virtual uint16_t comID() = 0; bool no_hash_passwords; /** disables hashing of passwords */ + bool hex_passwords; /** converts passwords from hex before using them */ sedutiloutput output_format; /** standard, readable, JSON */ protected: const char * dev; /**< character string representing the device in the OS lexicon */ diff --git a/Common/DtaHashPwd.cpp b/Common/DtaHashPwd.cpp index e33d7585..909df393 100644 --- a/Common/DtaHashPwd.cpp +++ b/Common/DtaHashPwd.cpp @@ -32,8 +32,8 @@ extern "C" { } using namespace std; -void DtaHashPassword(vector &hash, char * password, vector salt, - unsigned int iter, uint8_t hashsize) +void DtaHashPassword(vector &hash, const vector &password, + const vector &salt, unsigned int iter, uint8_t hashsize) { LOG(D1) << " Entered DtaHashPassword"; // if the hashsize can be > 255 the token overhead logic needs to be fixed @@ -42,7 +42,7 @@ void DtaHashPassword(vector &hash, char * password, vector sal hash.clear(); // don't hash the devault OPAL password '' - if (0 == strnlen(password, 32)) { + if (0 == password.size()) { goto exit; } hash.reserve(hashsize + 2); // hope this will prevent reallocation @@ -50,7 +50,7 @@ void DtaHashPassword(vector &hash, char * password, vector sal hash.push_back(' '); } - cf_pbkdf2_hmac((uint8_t *)password, strnlen(password, 256), + cf_pbkdf2_hmac(&password[0], password.size(), salt.data(), salt.size(), iter, hash.data(), hash.size(), @@ -67,21 +67,38 @@ void DtaHashPwd(vector &hash, char * password, DtaDev * d) { LOG(D1) << " Entered DtaHashPwd"; char *serNum; + vector decoded_password; + if (d->hex_passwords) + { + for (char* p=password; *p; ++p) + { + uint8_t num1 = (uint8_t)(*p & 0x40 ? (*p & 0xf) + 9 : *p & 0xf); + ++p; + if (*p == 0) + break; + uint8_t num2 = (uint8_t)(*p & 0x40 ? (*p & 0xf) + 9 : *p & 0xf); + decoded_password.push_back(num1 * 16 + num2); + } + } + else + { + decoded_password.assign(password, password + strlen(password)); + } if (d->no_hash_passwords) { - hash.clear(); - for (uint16_t i = 0; i < strnlen(password, 32); i++) - hash.push_back(password[i]); - // add the token overhead - hash.insert(hash.begin(), (uint8_t)hash.size()); - hash.insert(hash.begin(), 0xd0); - LOG(D1) << " Exit DtaHashPwd"; - return; + if (decoded_password.size() > 32) + decoded_password.resize(32); + hash = decoded_password; + // add the token overhead + hash.insert(hash.begin(), (uint8_t)hash.size()); + hash.insert(hash.begin(), 0xd0); + LOG(D1) << " Exit DtaHashPwd"; + return; } serNum = d->getSerialNum(); vector salt(serNum, serNum + 20); // vector salt(DEFAULTSALT); - DtaHashPassword(hash, password, salt); + DtaHashPassword(hash, decoded_password, salt); LOG(D1) << " Exit DtaHashPwd"; // log for hash timing } @@ -109,7 +126,7 @@ int testresult(std::vector &result, const char * expected, size_t len) int Testsedutil(const PBKDF_TestTuple *testSet, unsigned int testSetSize) { int pass = 1; - std::vector hash, seaSalt; + std::vector hash, seaSalt, password; for (unsigned int i = 0; i < testSetSize; i++) { const PBKDF_TestTuple &tuple = testSet[i]; @@ -120,7 +137,8 @@ int Testsedutil(const PBKDF_TestTuple *testSet, unsigned int testSetSize) } printf("Password %s Salt %s Iterations %i Length %i\n", (char *)tuple.Password, (char *) tuple.Salt, tuple.iterations, tuple.hashlen); - DtaHashPassword(hash, (char *) tuple.Password, seaSalt, tuple.iterations, tuple.hashlen); + password.assign(tuple.Password, tuple.Password+strlen(tuple.Password)); + DtaHashPassword(hash, password, seaSalt, tuple.iterations, tuple.hashlen); int fail = (testresult(hash, tuple.hexDerivedKey, tuple.hashlen) == 0); pass = pass & fail; } diff --git a/Common/DtaHashPwd.h b/Common/DtaHashPwd.h index d17ae7c3..961c4ebc 100644 --- a/Common/DtaHashPwd.h +++ b/Common/DtaHashPwd.h @@ -41,7 +41,7 @@ void DtaHashPwd(vector &hash, char * password, DtaDev * device); * @param iter number of iterations to be preformed * @param hashsize size of hash to be returned */ -void DtaHashPassword(vector &hash, char * password, vector salt, +void DtaHashPassword(vector &hash, const vector &password, const vector &salt, unsigned int iter = 500000, uint8_t hashsize = 32); /** Test the hshing function using publicly available test cased and report */ int TestPBKDF2(); diff --git a/Common/DtaOptions.cpp b/Common/DtaOptions.cpp index c4151299..4474e8e6 100644 --- a/Common/DtaOptions.cpp +++ b/Common/DtaOptions.cpp @@ -27,10 +27,11 @@ void usage() printf("a utility to manage self encrypting drives that conform\n"); printf("to the TCG Enterprise, Opal, Opalite and Pyrite SSC specs\n"); printf("General Usage: (see readme for extended commandset)\n"); - printf("sedutil-cli <-v> <-n> \n"); + printf("sedutil-cli <-v> <-n> <-x> \n"); printf("-v (optional) increase verbosity, one to five v's\n"); printf("-n (optional) no password hashing. Passwords will be sent in clear text!\n"); printf("-l (optional) log style output to stderr only\n"); + printf("-x (optional) password inputs are in hex form\n"); printf("actions \n"); printf("--scan \n"); printf(" Scans the devices on the system \n"); @@ -99,6 +100,12 @@ void usage() printf(" AdminSP->Revert instead of ThisSP->RevertSP\n"); printf("--printDefaultPassword \n"); printf(" print MSID \n"); + printf("--printPasswordHash \n"); + printf(" print the hash of the password \n"); + printf(" as computed by sedutil. Hex-ecoded.\n"); + printf("--prepareForS3Sleep <0...n> \n"); + printf(" Automatically unlock range after S3 resume\n"); + printf(" This command will save the password to kernel memory\n"); printf("\n"); printf("Examples \n"); printf("sedutil-cli --scan \n"); @@ -144,6 +151,10 @@ uint8_t DtaOptions(int argc, char * argv[], DTA_OPTIONS * opts) opts->output_format = sedutilNormal; outputFormat = sedutilNormal; } + else if (!strcmp("-x", argv[i])) { + baseOptions += 1; + opts->hex_passwords = true; + } else if (!(('-' == argv[i][0]) && ('-' == argv[i][1])) && (0 == opts->action)) { @@ -515,6 +526,31 @@ uint8_t DtaOptions(int argc, char * argv[], DTA_OPTIONS * opts) END_OPTION BEGIN_OPTION(objDump, 5) i += 4; OPTION_IS(device) END_OPTION BEGIN_OPTION(printDefaultPassword, 1) OPTION_IS(device) END_OPTION + BEGIN_OPTION(printPasswordHash, 2) + OPTION_IS(password) + OPTION_IS(device) + END_OPTION + BEGIN_OPTION(prepareForS3Sleep, 3) + TESTARG(0, lockingrange, 0) + TESTARG(1, lockingrange, 1) + TESTARG(2, lockingrange, 2) + TESTARG(3, lockingrange, 3) + TESTARG(4, lockingrange, 4) + TESTARG(5, lockingrange, 5) + TESTARG(6, lockingrange, 6) + TESTARG(7, lockingrange, 7) + TESTARG(8, lockingrange, 8) + TESTARG(9, lockingrange, 9) + TESTARG(10, lockingrange, 10) + TESTARG(11, lockingrange, 11) + TESTARG(12, lockingrange, 12) + TESTARG(13, lockingrange, 13) + TESTARG(14, lockingrange, 14) + TESTARG(15, lockingrange, 15) + TESTFAIL("Invalid Locking Range (0-15)") + OPTION_IS(password) + OPTION_IS(device) + END_OPTION BEGIN_OPTION(rawCmd, 7) i += 6; OPTION_IS(device) END_OPTION else { LOG(E) << "Invalid command line argument " << argv[i]; diff --git a/Common/DtaOptions.h b/Common/DtaOptions.h index c012af1d..669eef8c 100644 --- a/Common/DtaOptions.h +++ b/Common/DtaOptions.h @@ -43,6 +43,7 @@ typedef struct _DTA_OPTIONS { uint8_t lrlength; /** the length in blocks of a lockingrange */ bool no_hash_passwords; /** global parameter, disables hashing of passwords */ + bool hex_passwords; /** global parameter, all incoming passwords are treated as hex-encoded */ sedutiloutput output_format; } DTA_OPTIONS; /** Print a usage message */ @@ -95,6 +96,8 @@ typedef enum _sedutiloption { validatePBKDF2, objDump, printDefaultPassword, + printPasswordHash, + prepareForS3Sleep, rawCmd, } sedutiloption; diff --git a/Common/sedutil.cpp b/Common/sedutil.cpp index 270709e9..2c816d10 100644 --- a/Common/sedutil.cpp +++ b/Common/sedutil.cpp @@ -107,6 +107,8 @@ int main(int argc, char * argv[]) // make sure DtaDev::no_hash_passwords is initialized d->no_hash_passwords = opts.no_hash_passwords; + d->hex_passwords = opts.hex_passwords; + d->output_format = opts.output_format; } @@ -273,6 +275,14 @@ int main(int argc, char * argv[]) LOG(D) << "print default password"; return d->printDefaultPassword(); break; + case sedutiloption::printPasswordHash: + LOG(D) << "print password hash"; + return d->printPasswordHash(argv[opts.password]); + break; + case sedutiloption::prepareForS3Sleep: + LOG(D) << "Preparing for S3 sleep " << (uint16_t) opts.lockingrange; + return d->prepareForS3Sleep(opts.lockingrange, argv[opts.password]); + break; case sedutiloption::rawCmd: LOG(D) << "Performing cmdDump "; return d->rawCmd(argv[argc - 7], argv[argc - 6], argv[argc - 5], argv[argc - 4], argv[argc - 3], argv[argc - 2]); diff --git a/Makefile.am b/Makefile.am index a8e59d2d..c4ccf5f6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,7 +29,8 @@ sedutil_cli_SOURCES = linux/Version.h Common/sedutil.cpp Common/DtaOptions.cpp C linux/DtaDevLinuxNvme.cpp linux/DtaDevLinuxNvme.h \ linux/DtaDevLinuxSata.cpp linux/DtaDevLinuxSata.h \ linux/DtaDevOS.cpp linux/DtaDevOS.h \ - linux/DtaDevLinuxDrive.h linux/os.h \ + linux/DtaDevLinuxDrive.cpp linux/DtaDevLinuxDrive.h \ + linux/os.h \ $(SEDUTIL_COMMON_CODE) CLEANFILES = linux/Version.h BUILT_SOURCES = linux/Version.h @@ -40,7 +41,8 @@ linuxpba_SOURCES = LinuxPBA/LinuxPBA.cpp LinuxPBA/GetPassPhrase.cpp LinuxPBA/Unl linux/DtaDevLinuxNvme.cpp linux/DtaDevLinuxNvme.h \ linux/DtaDevLinuxSata.cpp linux/DtaDevLinuxSata.h \ linux/DtaDevOS.cpp linux/DtaDevOS.h \ - linux/DtaDevLinuxDrive.h linux/os.h \ + linux/DtaDevLinuxDrive.cpp linux/DtaDevLinuxDrive.h \ + linux/os.h \ \ $(SEDUTIL_COMMON_CODE) EXTRA_DIST = linux/GitVersion.sh linux/PSIDRevert_LINUX.txt linux/TestSuite.sh README.md docs/sedutil-cli.8 diff --git a/README.md b/README.md index 9c3113eb..cd4c4700 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Unique to this repo are the following modifications: * SHA512 password hashing vs SHA1 on original SEDutil * Compatibile with AMD Ryzen and AMD Ryzen mobile systems +* Supports S3 sleep ## Build Process @@ -238,7 +239,7 @@ Drive /dev/sdd Samsung SSD 850 EVO 250GB is OPAL NOT LOCKED Verify that the PBA unlocks your drive, it should say "is OPAL Unlocked" If it doesn't then you will need to follow the steps at the end of this page to either remove OPAL or disable locking. -#Set a real password +# Set a real password The SID and Admin1 passwords do not have to match but it makes things easier. ``` @@ -270,7 +271,48 @@ Expected Output: You now need to COMPLETELY POWER DOWN YOUR SYSTEM This will lock the drive so that when you restart your system it will boot the PBA. -##Recovery information: +## S3 sleep support + +To make the machine able to recover from sleep, the following is needed: +* Kernel 4.13 or later +* this patched sedutil-cli +* following the guide here: https://github.com/ladar/sedutil/issues/4#issue-482078217 , (which in turn was born from Drive-Trust-Alliance/sedutil#90). For completeness, mirrored below: + +1. (Optional for NVME drives, required for SATA) Edit /etc/default/grub to append `libata.allow_tpm=1` to the end the line GRUB_CMDLINE_LINUX_DEFAULT= + +2. (Optional for NVME drives, required for SATA) Update `grub.cfg` by running # `update-grub` + +3. Download (or build) the Linux binary from the release of this repo; extract and have it ready to run + +4. (Optional for NVME drives, required for SATA) Reboot + +5. Find your encryption key by +``` +# sedutil-cli --printPasswordHash /dev/nvme? +``` +(namespace needs to be included for NVMe, e.g. /dev/nvme0n1; same for the following) + +6. Add a systemd service that will execute the following command that will tell the kernel what password to use after wakeup. Note that the password needs to be in the hashed format already. + +``` +# sedutil-cli -n -x --prepareForS3Sleep 0 Admin1 /dev/nvme? +``` +Below is my /etc/systemd/system/seds3sleep.service + +``` +[Service] +Type=oneshot +ExecStart=/opt/sedutil-1.15.1-87/sedutil-cli -n -x --prepareForS3Sleep 0 Admin1 /dev/nvme? + +[Install] +WantedBy=multi-user.target +Enable this service. # systemctl enable seds3sleep.service && systemctl start seds3sleep.service +``` + +**NOTE:** if you have multiple disks, you may want to include multiple `ExecStart` commands, and ideally try all the disks for all passwords as their numbers may randomly change. So for 2 disks having different passwords you may want to include 4 ExecStart statements. + + +# Recovery information: If there is an issue after enabling locking you can either disable locking or remove OPAL to continue using your drive without locking. @@ -307,7 +349,7 @@ Expected Output: Some OPAL drives have a firmware bug that will erase all of your data if you issue the commands below. See [Remove OPAL](https://github.com/Drive-Trust-Alliance/sedutil/wiki/Remove-OPAL) for a list of drive/firmware pairs that is know to have been tested. -##To remove OPAL issue these commands: +## To remove OPAL issue these commands: ``` sedutil-cli --revertnoerase @@ -351,5 +393,3 @@ Expected output: When this is finished the drive will be in a non-opal managed state. This would allow you to do anything that you could have done before starting OPAL management under OPAL. You can also reinitiate OPAL management if you wish. - - diff --git a/linux/DtaDevLinuxDrive.cpp b/linux/DtaDevLinuxDrive.cpp new file mode 100755 index 00000000..d04606a9 --- /dev/null +++ b/linux/DtaDevLinuxDrive.cpp @@ -0,0 +1,46 @@ +/* C:B************************************************************************** +Copyright 2017, Alex Badics + +This file is part of sedutil. + +sedutil is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sedutil is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sedutil. If not, see . + + * C:E********************************************************************** */ +#include "os.h" +#include +#include +#include "DtaDevLinuxDrive.h" + +using namespace std; + +uint8_t DtaDevLinuxDrive::prepareForS3Sleep(uint8_t lockingrange, const vector &password_hash) +{ + LOG(D1) << "Entering DtaDevLinuxDrive::prepareForS3Sleep"; + + opal_lock_unlock opal_ioctl_data={}; + opal_ioctl_data.l_state = OPAL_RW; + opal_ioctl_data.session.who = OPAL_ADMIN1; + opal_ioctl_data.session.opal_key.lr = 0; + + size_t hash_len=min(password_hash.size(), sizeof(opal_ioctl_data.session.opal_key.key)); + LOG(D2) << "Setting a hash of length" << hash_len; + + memcpy(opal_ioctl_data.session.opal_key.key, &password_hash[0], hash_len); + opal_ioctl_data.session.opal_key.key_len = hash_len; + + int err = ioctl(fd, IOC_OPAL_SAVE, &opal_ioctl_data); + if (err < 0) + return errno; + return 0; +} diff --git a/linux/DtaDevLinuxDrive.h b/linux/DtaDevLinuxDrive.h index d2022a16..39d1951f 100755 --- a/linux/DtaDevLinuxDrive.h +++ b/linux/DtaDevLinuxDrive.h @@ -18,8 +18,10 @@ along with sedutil. If not, see . * C:E********************************************************************** */ #pragma once +#include #include "DtaStructures.h" +using namespace std; /** virtual implementation for a disk interface-generic disk drive */ class DtaDevLinuxDrive { @@ -45,4 +47,7 @@ class DtaDevLinuxDrive { void * buffer, uint32_t bufferlen) = 0; /** Routine to send an identify to the device */ virtual void identify(OPAL_DiskInfo& disk_info) = 0; + /** Save the password hash to the kernel for S3 sleep wakeup */ + uint8_t prepareForS3Sleep(uint8_t lockingrange, const vector &password_hash); + int fd; /**< Linux handle for the device */ }; diff --git a/linux/DtaDevLinuxNvme.h b/linux/DtaDevLinuxNvme.h index 3ea6874d..b305a88f 100755 --- a/linux/DtaDevLinuxNvme.h +++ b/linux/DtaDevLinuxNvme.h @@ -59,5 +59,4 @@ class DtaDevLinuxNvme: public DtaDevLinuxDrive{ void * buffer, uint32_t bufferlen); /** NVMe specific routine to send an identify to the device */ void identify(OPAL_DiskInfo& disk_info); - int fd; /**< Linux handle for the device */ }; diff --git a/linux/DtaDevLinuxSata.h b/linux/DtaDevLinuxSata.h index 14b7e12a..6e19b44d 100755 --- a/linux/DtaDevLinuxSata.h +++ b/linux/DtaDevLinuxSata.h @@ -55,6 +55,5 @@ class DtaDevLinuxSata: public DtaDevLinuxDrive { void * buffer, uint32_t bufferlen); /** Linux specific routine to send an ATA identify to the device */ void identify_SAS(OPAL_DiskInfo *disk_info); - int fd; /**< Linux handle for the device */ int isSAS; /* The device is sas */ }; diff --git a/linux/DtaDevOS.cpp b/linux/DtaDevOS.cpp index e1db38c5..710812ca 100644 --- a/linux/DtaDevOS.cpp +++ b/linux/DtaDevOS.cpp @@ -38,6 +38,9 @@ along with sedutil. If not, see . #include "DtaDevLinuxSata.h" #include "DtaDevLinuxNvme.h" #include "DtaDevGeneric.h" +#include "DtaHashPwd.h" +#include "DtaSession.h" +#include "DtaDevOpal.h" using namespace std; @@ -167,6 +170,36 @@ int DtaDevOS::diskScan() return 0; } +uint8_t DtaDevOS::prepareForS3Sleep(uint8_t lockingrange, char* password) +{ + LOG(D1) << "Entering DtaDevOS::prepareForS3Sleep "; + LOG(D2) << "Starting testing of password "; + session = new DtaSession(this); + if (NULL == session) { + LOG(E) << "Unable to create session object "; + return DTAERROR_OBJECT_CREATE_FAILED; + } + int err; + if ((err = session->start(OPAL_UID::OPAL_LOCKINGSP_UID, password, OPAL_UID::OPAL_ADMIN1_UID)) != 0) { + delete session; + LOG(E) << "Unable to authenticate with the given password"; + return err; + } + delete session; + LOG(D2) << "Test successful, saving it to kernel "; + vector hash; + DtaHashPwd(hash, password, this); + hash.erase(hash.begin(), hash.begin()+2); + + err = drive->prepareForS3Sleep(0, hash); + if (err) + { + LOG(E) << "Error saving the password to the kernel errno = " << errno; + return errno; + } + return 0; +} + /** Close the device reference so this object can be delete. */ DtaDevOS::~DtaDevOS() { diff --git a/linux/DtaDevOS.h b/linux/DtaDevOS.h index beeacb38..fc3705fc 100644 --- a/linux/DtaDevOS.h +++ b/linux/DtaDevOS.h @@ -49,6 +49,8 @@ class DtaDevOS : public DtaDev { void * buffer, uint32_t bufferlen); /** A static class to scan for supported drives */ static int diskScan(); + /** Save device key to kernel for S3 sleep resume */ + uint8_t prepareForS3Sleep(uint8_t lockingrange, char* password); protected: /** OS specific command to Wait for specified number of milliseconds * @param ms number of milliseconds to wait