lsvmtools/lsvmutils/luks.c

900 строки
20 KiB
C

/*
**==============================================================================
**
** LSVMTools
**
** MIT License
**
** Copyright (c) Microsoft Corporation. All rights reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
** SOFTWARE
**
**==============================================================================
*/
#include "luks.h"
#include <lsvmutils/strings.h>
#include <lsvmutils/byteorder.h>
#include <lsvmutils/alloc.h>
#include <lsvmutils/print.h>
#include <lsvmutils/sha.h>
#include "tpm2.h"
#include "dump.h"
#include "gpt.h"
#include "luksblkdev.h"
#include "ext2.h"
#if !defined(BUILD_EFI)
# include <stdio.h>
#endif
/*
**==============================================================================
**
** Private definitions:
**
**==============================================================================
*/
#if defined(BUILD_EFI)
# define VSNPRINTF VSPrint
# define SNPRINTF SPrint
#else
# define VSNPRINTF vsnprintf
# define SNPRINTF snprintf
#endif
#define LUKS_VERSION 1
#define LUKS_SLOT_ENABLED 0x00ac71f3
#define LUKS_SLOT_DISABLED 0x0000dead
#define LUKS_SECTOR_SIZE 512
#define LUKS_IV_SIZE 16
static UINT8 _magic[LUKS_MAGIC_SIZE] = LUKS_MAGIC_INITIALIZER;
void LUKSFixByteOrder(
LUKSHeader *header)
{
int i;
if (!IS_BIG_ENDIAN)
{
header->version = ByteSwapU16(header->version);
header->payload_offset = ByteSwapU32(header->payload_offset);
header->key_bytes = ByteSwapU32(header->key_bytes);
header->mk_digest_iter = ByteSwapU32(header->mk_digest_iter);
for (i = 0; i < LUKS_SLOTS_SIZE; i++)
{
LUKSKeySlot* slot = &header->slots[i];
slot->enabled = ByteSwapU32(slot->enabled);
slot->password_iters = ByteSwapU32(slot->password_iters);
slot->key_material_offset = ByteSwapU32(slot->key_material_offset);
slot->af_stripes = ByteSwapU32(slot->af_stripes);
}
}
}
static void _PrintHexByte(UINT8 x)
{
# if defined(BUILD_EFI)
{
CHAR16 buf[9];
UINTN n = SPrint(buf, sizeof(buf), L"%X", x);
if (n == 8)
{
UINTN i;
for (i = 0; i < n; i++)
buf[i] = Tolower(buf[i]);
Print(L"%s", buf + 6);
}
else
Print(L"??");
}
# else
Print(TCS("%02x"), x);
# endif
}
static void _PrintHexBytes(const UINT8* data, UINTN size)
{
int i;
for (i = 0; i < size; i++)
{
_PrintHexByte(data[i]);
if (i + 1 != size)
Print(TCS(" "));
}
Print(TCS("\n"));
}
static void _DumpKeySlot(
const LUKSKeySlot* slot)
{
if (slot->enabled == LUKS_SLOT_DISABLED)
{
Print(TCS("DISABLED\n"));
return;
}
if (slot->enabled != LUKS_SLOT_ENABLED)
{
/* ignore error */
return;
}
Print(TCS("ENABLED\n"));
Print(TCS("\tIterations:\t\t%d\n"), slot->password_iters);
Print(TCS("\tSalt:\t\t\t"));
_PrintHexBytes(slot->password_salt, LUKS_SALT_SIZE/2);
Print(TCS("\t\t\t\t"));
_PrintHexBytes(slot->password_salt + LUKS_SALT_SIZE/2, LUKS_SALT_SIZE/2);
Print(TCS("\tKey material offset:\t%d\n"), slot->key_material_offset);
Print(TCS("\tAF stripes:\t\t%d\n"), slot->af_stripes);
}
static int _GenIV(
const LUKSHeader *header,
UINT64 sec,
UINT8 *iv,
const UINT8 *key)
{
int rc = -1;
int ivSize = 8;
LUKSCipher* cipher = NULL;
if (Strcmp("ecb", header->cipher_mode) == 0)
{
rc = 0;
goto done;
}
if (Strcmp("cbc-plain", header->cipher_mode) == 0)
ivSize = 4;
Memcpy(iv, &sec, ivSize);
if (Strcmp("xts-plain64", header->cipher_mode) == 0)
{
rc = 0;
goto done;
}
if (Strcmp("cbc-plain", header->cipher_mode) == 0)
{
rc = 0;
goto done;
}
SHA256Hash sha256;
UINT8 temp[LUKS_IV_SIZE] = { 0 };
ComputeSHA256(key, header->key_bytes, &sha256);
Memcpy(temp, &sec, ivSize);
if (!(cipher = LUKSGetAES256ECBCipher(sha256.buf, sizeof(sha256))))
goto done;
if (LUKSCryptData(
cipher,
LUKS_CRYPT_MODE_ENCRYPT,
NULL, /* iv */
0, /* ivSize */
temp, /* in */
LUKS_IV_SIZE, /* inSize */
iv) != 0) /* out */
{
goto done;
}
rc = 0;
done:
if (cipher)
LUKSReleaseCipher(cipher);
return rc;
}
static int _DiffuseSHA(
SHAAlgorithm alg,
UINT8 *data,
UINT32 bytes)
{
UINT32 i;
INT32 hashSize;
UINT32 pad;
hashSize = SHAHashSize(alg);
if (hashSize < 0)
return -1;
pad = (bytes % ((UINT32) hashSize)) != 0;
for (i = 0; i < bytes / hashSize + pad; i++)
{
__SHAContext ctx;
__SHAHash hash;
UINT32 iv = ByteSwapU32(i);
int size;
if (!__SHAInit(&ctx, alg))
return -1;
if (!__SHAUpdate(&ctx, (UINT8*)&iv, sizeof(iv)))
return -1;
size = hashSize;
if (size > bytes - i * hashSize)
size = bytes - i * hashSize;
if (!__SHAUpdate(&ctx, data + i * hashSize, size))
return -1;
if (!__SHAFinal(&ctx, &hash))
return -1;
Memcpy(data + i * hashSize, hash.buf, size);
}
return 0;
}
static int _Diffuse(
const LUKSHeader* header,
UINT8 *data,
UINT32 bytes)
{
SHAAlgorithm alg;
if (Strcmp(header->hash_spec, "sha1") == 0)
alg = SHA1_ALG;
else if (Strcmp(header->hash_spec, "sha256") == 0)
alg = SHA256_ALG;
else if (Strcmp(header->hash_spec, "sha384") == 0)
alg = SHA384_ALG;
else if (Strcmp(header->hash_spec, "sha512") == 0)
alg = SHA512_ALG;
else
return -1;
return _DiffuseSHA(alg, data, bytes);
}
static void _ComputeXOR(
UINT8* x,
const UINT8* lhs,
const UINT8* rhs,
UINTN size)
{
/* x = lhs ^ rhs */
while (size--)
*x++ = *lhs++ ^ *rhs++;
}
/* Merge the stripes into the master key */
static int _AFMerge(
const LUKSHeader *header,
const LUKSKeySlot* slot,
const UINT8 *stripes,
UINT8 *masterKey)
{
UINT32 i;
Memset(masterKey, 0, header->key_bytes);
/* For each stripe */
for (i = 0; i < slot->af_stripes; i++)
{
const UINT8* stripe;
/* Get pointer to current stripe */
stripe = stripes + (i * header->key_bytes);
/* XOR current stripe into the master key */
_ComputeXOR(masterKey, masterKey, stripe, header->key_bytes);
/* Diffuse all but the last stripe */
if (i + 1 != slot->af_stripes)
{
if (_Diffuse(header, masterKey, header->key_bytes) != 0)
return -1;
}
}
return 0;
}
static int _UnsealMasterKey(
Blkdev* rawdev,
const LUKSHeader *header,
const LUKSKeySlot* slot,
const UINT8 *passphrase,
UINTN passphraseSize,
UINT8 *masterKey)
{
int rc = -1;
UINT8 *derivedKey = NULL;
UINT8 *encryptedStripes = NULL;
UINT8 *decryptedStripes = NULL;
UINTN stripesBytes = 0;
UINT8 mkDigest[LUKS_DIGEST_SIZE];
/* Check for null parameters */
if (!rawdev || !header || !slot || !passphrase || !masterKey)
goto done;
/* Allocate bytes for the derived key (used to decrypt the stripes) */
if (!(derivedKey = (UINT8*)Malloc(header->key_bytes)))
goto done;
/* Compute the derived key from passphrase, salt, and iterations-count */
if (LUKSDeriveKey(
(const char*) passphrase,
passphraseSize,
slot->password_salt,
LUKS_SALT_SIZE,
slot->password_iters,
header->hash_spec,
header->key_bytes,
derivedKey) != 0)
{
goto done;
}
/* Compute the total size of the stripes */
stripesBytes = header->key_bytes * slot->af_stripes;
/* Allocate space for the encrypted stripes */
if (!(encryptedStripes = (UINT8*)Malloc(stripesBytes)))
goto done;
/* Allocate space for the decrypted stripes */
if (!(decryptedStripes = (UINT8*)Malloc(stripesBytes)))
goto done;
/* Read the key material (stripes) into memory */
if ((BlkdevRead(
rawdev,
slot->key_material_offset,
encryptedStripes,
stripesBytes)) != 0)
{
goto done;
}
/* Decrypt the stripes */
if (LUKSCrypt(
LUKS_CRYPT_MODE_DECRYPT,
header,
derivedKey,
encryptedStripes,
decryptedStripes,
stripesBytes,
0) != 0)
{
goto done;
}
/* Merge the split stripes into the unsplit master key */
if (_AFMerge(header, slot, decryptedStripes, masterKey) != 0)
goto done;
/* Compute the digest of the master key */
if (LUKSDeriveKey(
(char*)masterKey,
header->key_bytes,
header->mk_digest_salt,
LUKS_SALT_SIZE,
header->mk_digest_iter,
header->hash_spec,
LUKS_DIGEST_SIZE,
mkDigest) != 0)
{
goto done;
}
/* Verify that the master key digest matches the one in LUKS header */
if (Memcmp(mkDigest, header->mk_digest, LUKS_DIGEST_SIZE) != 0)
{
goto done;
}
rc = 0;
done:
if (derivedKey)
Free(derivedKey);
if (encryptedStripes)
Free(encryptedStripes);
if (decryptedStripes)
Free(decryptedStripes);
return rc;
}
/*
**==============================================================================
**
** Public definitions:
**
**==============================================================================
*/
int LUKSReadHeader(
Blkdev* rawdev,
LUKSHeader* header)
{
int rc = -1;
/* Reject null parameters */
if (!rawdev || !header)
{
rc = -2;
goto done;
}
/* Read the header */
if (BlkdevRead(
rawdev,
0, /* Block 0 */
header,
sizeof(LUKSHeader)) != 0)
{
rc = -4;
goto done;
}
/* Check the magic number */
if (Memcmp(header->magic, _magic, sizeof(_magic)) != 0)
{
rc = -5;
goto done;
}
/* Adjust byte order from big-endian to native */
LUKSFixByteOrder(header);
rc = 0;
done:
return rc;
}
int LUKSDumpHeader(
const LUKSHeader* header)
{
int rc = -1;
int i;
/* Reject null parameters */
if (!header)
{
rc = -2;
goto done;
}
Print(TCS("Header Size: %d"), (int)sizeof(LUKSHeader));
Print(TCS("\n"));
Print(TCS("Version:\t%d\n"), header->version);
#if defined(BUILD_EFI)
Print(TCS("Cipher name:\t%a\n"), header->cipher_name);
Print(TCS("Cipher mode:\t%a\n"), header->cipher_mode);
Print(TCS("Hash spec:\t%a\n"), header->hash_spec);
#else
Print(TCS("Cipher name:\t%s\n"), header->cipher_name);
Print(TCS("Cipher mode:\t%s\n"), header->cipher_mode);
Print(TCS("Hash spec:\t%s\n"), header->hash_spec);
#endif
Print(TCS("Payload offset:\t%d\n"), header->payload_offset);
Print(TCS("MK bits:\t%d\n"), header->key_bytes * 8);
Print(TCS("MK digest\t"));
_PrintHexBytes(header->mk_digest, LUKS_DIGEST_SIZE);
Print(TCS("MK salt:\t"));
_PrintHexBytes(header->mk_digest_salt, LUKS_SALT_SIZE/2);
Print(TCS("\t\t"));
_PrintHexBytes(header->mk_digest_salt + LUKS_SALT_SIZE/2, LUKS_SALT_SIZE/2);
Print(TCS("MK iterations:\t%d\n"), header->mk_digest_iter);
#if defined(BUILD_EFI)
Print(TCS("UUID:\t\t%a\n"), header->uuid);
#else
Print(TCS("UUID:\t\t%s\n"), header->uuid);
#endif
Print(TCS("\n"));
for (i = 0; i < LUKS_SLOTS_SIZE; i++)
{
Print(TCS("Key Slot %d: "), i);
_DumpKeySlot(&header->slots[i]);
}
rc = 0;
done:
return rc;
}
int LUKSGetMasterKey(
Blkdev* rawdev,
const LUKSHeader *header,
const UINT8 *passphrase,
UINTN passphraseSize,
UINT8 *masterKey)
{
int rc = -1;
int i;
LUKSInitialize();
for (i = 0; i < LUKS_SLOTS_SIZE; i++)
{
const LUKSKeySlot* slot = &header->slots[i];
if (slot->enabled == LUKS_SLOT_DISABLED)
continue;
if (slot->enabled == LUKS_SLOT_ENABLED)
{
rc = _UnsealMasterKey(rawdev, header, slot, passphrase, passphraseSize, masterKey);
if (rc == 0)
goto done;
}
}
/* Not found! */
done:
return rc;
}
int LUKSCrypt(
LUKSCryptMode mode,
const LUKSHeader *header,
const UINT8 *key,
const UINT8 *dataIn,
UINT8 *dataOut,
UINTN dataSize,
UINT64 sector)
{
int rc = -1;
LUKSCipher* cipher = NULL;
UINT8 iv[LUKS_IV_SIZE];
UINT64 i;
LUKSInitialize();
/* Get the EVP cipher */
if (!(cipher = LUKSGetCipher(header, key, header->key_bytes)))
goto done;
UINT64 iters = dataSize / LUKS_SECTOR_SIZE;
UINT64 block_len = LUKS_SECTOR_SIZE;
if (Strcmp(header->cipher_mode, "ecb") == 0)
{
iters = 1;
block_len = dataSize;
}
for (i = 0; i < iters; i++)
{
UINT64 pos;
Memset(iv, 0, LUKS_IV_SIZE);
if (_GenIV(header, sector + i, iv, key) == -1)
goto done;
pos = i * block_len;
if(LUKSCryptData(
cipher,
mode,
iv,
LUKS_IV_SIZE,
dataIn + pos,
block_len,
dataOut + pos) == -1)
{
goto done;
}
}
rc = 0;
done:
return rc;
}
int LUKSGetPayloadSector(
Blkdev* rawdev,
const LUKSHeader *header,
const UINT8* masterKey,
UINTN sectorNumber,
UINT8 sector[LUKS_SECTOR_SIZE])
{
int rc = -1;
UINT8 data[LUKS_SECTOR_SIZE];
LUKSInitialize();
/* Check parameters */
if (!rawdev || !header || !masterKey || !sector)
goto done;
/* Read the encrypted sector */
if (BlkdevRead(
rawdev,
header->payload_offset + sectorNumber,
data,
LUKS_SECTOR_SIZE) != 0)
{
goto done;
}
/* Decrypt the sector with the master key */
if (LUKSCrypt(
LUKS_CRYPT_MODE_DECRYPT,
header,
masterKey,
data,
sector,
LUKS_SECTOR_SIZE,
sectorNumber) != 0)
{
goto done;
}
rc = 0;
done:
return rc;
}
int LUKSPutPayloadSector(
Blkdev* rawdev,
const LUKSHeader *header,
const UINT8* masterKey,
UINTN sectorNumber,
const UINT8 sector[LUKS_SECTOR_SIZE])
{
int rc = -1;
UINT8 data[LUKS_SECTOR_SIZE];
LUKSInitialize();
/* Check parameters */
if (!rawdev || !header || !masterKey || !sector)
goto done;
/* Decrypt the sector with the master key */
if (LUKSCrypt(
LUKS_CRYPT_MODE_ENCRYPT,
header,
masterKey,
sector,
data,
LUKS_SECTOR_SIZE,
sectorNumber) != 0)
{
goto done;
}
/* Write the encrypted sector */
if (BlkdevWrite(
rawdev,
header->payload_offset + sectorNumber,
data,
LUKS_SECTOR_SIZE) != 0)
{
goto done;
}
rc = 0;
done:
return rc;
}
int LUKSComputeMKDigest(
const LUKSHeader* header,
const UINT8* masterKey,
UINT8 mkDigest[LUKS_DIGEST_SIZE])
{
LUKSInitialize();
/* Compute the digest of the master key */
if (LUKSDeriveKey(
(char*)masterKey,
header->key_bytes,
header->mk_digest_salt,
LUKS_SALT_SIZE,
header->mk_digest_iter,
header->hash_spec,
LUKS_DIGEST_SIZE,
mkDigest) != 0)
{
return -1;
}
return 0;
}
BOOLEAN LUKSMatchHeader(
const UINT8 sectors[2 * LUKS_SECTOR_SIZE], /* Contains raw LUKS header */
const UINT8* masterkeyData,
UINT32 masterkeySize)
{
BOOLEAN result = FALSE;
union
{
LUKSHeader header;
UINT8 blocks[2 * LUKS_SECTOR_SIZE];
}
u;
UINT8 mkDigest[LUKS_DIGEST_SIZE];
static const UINT8 magic[] = LUKS_MAGIC_INITIALIZER;
if (!sectors || !masterkeyData || !masterkeySize)
return FALSE;
/* Read the LUKS header (blkno == 0) */
Memcpy(&u, sectors, 2 * LUKS_SECTOR_SIZE);
/* If the header does not have the LUKS magic number */
if (Memcmp(u.header.magic, magic, sizeof(magic)) != 0)
goto done;
/* Fix the byte order of the hader */
LUKSFixByteOrder(&u.header);
/* If unsealed key is the wrong size it is from wrong parition */
if (masterkeySize != u.header.key_bytes)
goto done;
/* Compute MK-digest of the masterkey */
if (LUKSComputeMKDigest(&u.header, masterkeyData, mkDigest) != 0)
goto done;
/* Check whether this masterkey matches the MK-digest in the header */
if (Memcmp(u.header.mk_digest, mkDigest, LUKS_DIGEST_SIZE) != 0)
goto done;
result = TRUE;
done:
return result;
}
#if !defined(BUILD_EFI)
int LUKSFindBootDevice(
const char* path, /* example: /dev/sda */
const UINT8* masterkeyData,
UINT32 masterkeySize,
UINT8 uuid[16],
UINT32* partitionNumber,
UINT32* availPartitionNumber)
{
int rc = -1;
GPT gpt;
UINTN i;
EXT2* ext2 = NULL;
/* Check parameters */
if (!path || !masterkeyData || !masterkeySize || !uuid ||
!availPartitionNumber)
{
goto done;
}
/* Initialize parameters */
Memset(uuid, 0, 16);
*partitionNumber = 0;
*availPartitionNumber = 0;
/* Load the GPT */
if (LoadGPT(path, &gpt) != 0)
goto done;
/* Try each parition */
for (i = 0; i < GPT_MAX_ENTRIES && gpt.entries[i].typeGUID1; i++)
{
char devname[32];
U32tostrBuf buf;
Blkdev* dev = NULL;
Blkdev* luksdev = NULL;
/* Format device name: example: "/dev/sda2" */
Strlcpy(devname, path, sizeof(devname));
Strlcat(devname, U32tostr(&buf, i + 1), sizeof(devname));
/** Create object chain: [ext2] -> [luksdev] -> [dev] **/
/* Open this block device */
if (!(dev = BlkdevOpen(devname, BLKDEV_ACCESS_RDWR, 0)))
goto done;
/* Try to open as a LUKS device */
if (!(luksdev = LUKSBlkdevFromMasterkey(
dev,
masterkeyData,
masterkeySize)))
{
/* Not a valid LUKS device */
dev->Close(dev);
dev = NULL;
continue;
}
/* Try to create EXT2 file system (will fail if not EXT2) */
if (EXT2New(luksdev, &ext2) != EXT2_ERR_NONE)
{
/* Not a valid EXT2 file system */
luksdev->Close(luksdev);
continue;
}
/* Found valid ext2 */
*partitionNumber = i + 1;
break;
}
if (!ext2)
goto done;
Memcpy(uuid, ext2->sb.s_uuid, sizeof(ext2->sb.s_uuid));
*availPartitionNumber = CountGPTEntries(&gpt) + 1;
rc = 0;
done:
if (ext2)
EXT2Delete(ext2);
return rc;
}
#endif /* !defined(BUILD_EFI) */