summaryrefslogtreecommitdiffstats
path: root/libblkid/src/superblocks/luks.c
blob: 4623c98fc9d64db75504a7b09b4566cec2f2dc30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
 * Copyright (C) 2018-2024 Milan Broz <gmazyland@gmail.com>
 *
 * Inspired by libvolume_id by
 *     Kay Sievers <kay.sievers@vrfy.org>
 *
 * This file may be redistributed under the terms of the
 * GNU Lesser General Public License.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdint.h>
#include <stdbool.h>

#include "superblocks.h"

#define LUKS_CIPHERNAME_L		32
#define LUKS_CIPHERMODE_L		32
#define LUKS_HASHSPEC_L			32
#define LUKS_DIGESTSIZE			20
#define LUKS_SALTSIZE			32
#define LUKS_MAGIC_L			6
#define UUID_STRING_L			40
#define LUKS2_LABEL_L			48
#define LUKS2_SALT_L			64
#define LUKS2_CHECKSUM_ALG_L		32
#define LUKS2_CHECKSUM_L		64

#define LUKS_MAGIC	"LUKS\xba\xbe"
#define LUKS_MAGIC_2	"SKUL\xba\xbe"

/* Offsets for secondary header (for scan if primary header is corrupted). */
#define LUKS2_HDR2_OFFSETS { 0x04000, 0x008000, 0x010000, 0x020000, \
                             0x40000, 0x080000, 0x100000, 0x200000, 0x400000 }

static const uint64_t secondary_offsets[] = LUKS2_HDR2_OFFSETS;

struct luks_phdr {
	uint8_t		magic[LUKS_MAGIC_L];
	uint16_t	version;
	uint8_t		cipherName[LUKS_CIPHERNAME_L];
	uint8_t		cipherMode[LUKS_CIPHERMODE_L];
	uint8_t		hashSpec[LUKS_HASHSPEC_L];
	uint32_t	payloadOffset;
	uint32_t	keyBytes;
	uint8_t		mkDigest[LUKS_DIGESTSIZE];
	uint8_t		mkDigestSalt[LUKS_SALTSIZE];
	uint32_t	mkDigestIterations;
	uint8_t		uuid[UUID_STRING_L];
} __attribute__((packed));

struct luks2_phdr {
	char		magic[LUKS_MAGIC_L];
	uint16_t	version;
	uint64_t	hdr_size;	/* in bytes, including JSON area */
	uint64_t	seqid;		/* increased on every update */
	char		label[LUKS2_LABEL_L];
	char		checksum_alg[LUKS2_CHECKSUM_ALG_L];
	uint8_t		salt[LUKS2_SALT_L]; /* unique for every header/offset */
	char		uuid[UUID_STRING_L];
	char		subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
	uint64_t	hdr_offset;	/* offset from device start in bytes */
	char		_padding[184];
	uint8_t		csum[LUKS2_CHECKSUM_L];
	/* Padding to 4k, then JSON area */
} __attribute__ ((packed));

static int luks_attributes(blkid_probe pr, struct luks2_phdr *header, uint64_t offset)
{
	int version;
	struct luks_phdr *header_v1;

	if (blkid_probe_set_magic(pr, offset, LUKS_MAGIC_L, (unsigned char *) &header->magic))
		return BLKID_PROBE_NONE;

	version = be16_to_cpu(header->version);
	blkid_probe_sprintf_version(pr, "%u", version);

	if (version == 1) {
		header_v1 = (struct luks_phdr *)header;
		blkid_probe_strncpy_uuid(pr,
			(unsigned char *) header_v1->uuid, UUID_STRING_L);
	} else if (version == 2) {
		blkid_probe_strncpy_uuid(pr,
			(unsigned char *) header->uuid, UUID_STRING_L);
		blkid_probe_set_label(pr,
			(unsigned char *) header->label, LUKS2_LABEL_L);
		blkid_probe_set_id_label(pr, "SUBSYSTEM",
			(unsigned char *) header->subsystem, LUKS2_LABEL_L);
	}

	return BLKID_PROBE_OK;
}

static bool luks_valid(struct luks2_phdr *header, const char *magic, uint64_t offset)
{
	if (memcmp(header->magic, magic, LUKS_MAGIC_L))
		return false;

	/* LUKS2 header is not at expected offset */
	if (be16_to_cpu(header->version) == 2 &&
	    be64_to_cpu(header->hdr_offset) != offset)
		return false;

	return true;
}

static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__)))
{
	struct luks2_phdr *header;
	size_t i;

	header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, 0, sizeof(struct luks2_phdr));
	if (!header)
		return errno ? -errno : BLKID_PROBE_NONE;

	if (luks_valid(header, LUKS_MAGIC, 0)) {
		/* LUKS primary header was found. */
		return luks_attributes(pr, header, 0);
	}

	/* No primary header, scan for known offsets of LUKS2 secondary header. */
	for (i = 0; i < ARRAY_SIZE(secondary_offsets); i++) {
		header = (struct luks2_phdr *) blkid_probe_get_buffer(pr,
			  secondary_offsets[i], sizeof(struct luks2_phdr));

		if (!header)
			return errno ? -errno : BLKID_PROBE_NONE;

		if (luks_valid(header, LUKS_MAGIC_2, secondary_offsets[i]))
			return luks_attributes(pr, header, secondary_offsets[i]);
	}

	return BLKID_PROBE_NONE;
}

const struct blkid_idinfo luks_idinfo =
{
	.name		= "crypto_LUKS",
	.usage		= BLKID_USAGE_CRYPTO,
	.probefunc	= probe_luks,
	.magics		= BLKID_NONE_MAGIC
};