LUKS

LUKS(Linux Unified Key Setup)는 2004년 클레멘스 프루비르트(Clemens Fruhwirth)가 만든 디스크 암호화 사양으로, 본래 리눅스를 위해 설계되었다.

LUKS는 다양한 도구에서 사용할 수 있도록 플랫폼 독립적인 표준 온디스크 포맷을 구현한다. 이는 서로 다른 프로그램과 운영 체제 간의 호환성 및 상호운용성을 용이하게 하며, 모든 프로그램이 안전하고 문서화된 방식으로 비밀번호 관리를 구현하도록 보장한다.

 설명 

LUKS는 블록 장치를 암호화하는 데 사용된다. 암호화된 장치의 내용은 임의적이며, 따라서 스왑 파티션을 포함한 모든 파일 시스템을 암호화할 수 있다. 암호화된 볼륨의 시작 부분에는 암호화되지 않은 헤더가 있으며, 여기에는 암호화 유형 및 키 크기와 같은 암호화 매개변수와 함께 최대 8개(LUKS1) 또는 32개(LUKS2)의 암호화 키를 저장할 수 있다.

이 헤더의 존재는 LUKS와 dm-crypt의 주요 차이점인데, 헤더를 통해 여러 개의 서로 다른 암호를 사용할 수 있고 이를 변경하거나 제거할 수 있기 때문이다. 헤더가 손실되거나 손상되면 장치를 더 이상 복호화할 수 없게 된다.

암호화는 다층 구조 방식을 사용한다. 먼저 블록 장치는 마스터 키를 사용하여 암호화된다. 이 마스터 키는 활성화된 각 사용자 키로 암호화된다. 사용자 키는 암호, FIDO2 보안 키, TPM 또는 스마트카드에서 파생된다. 이러한 다층 구조 방식을 통해 사용자는 전체 블록 장치를 다시 암호화하지 않고도 암호를 변경할 수 있다. 키 슬롯에는 사용자 암호 또는 다른 유형의 키를 확인하기 위한 정보가 포함될 수 있다.

LUKS에는 두 가지 버전이 있는데, LUKS2는 헤더 손상에 대한 복구 능력을 갖추고 기본적으로 Argon2 키 유도 함수를 사용하는 반면, LUKS1은 PBKDF2를 사용한다. 특정 상황에서 LUKS의 두 버전 간 변환이 가능하지만, Argon2와 같은 일부 기능은 LUKS1에서 사용하지 못할 수 있다. LUKS2는 메타데이터 포맷으로 JSON을 사용한다.

사용 가능한 암호화 알고리즘은 호스트의 개별 커널 지원에 따라 달라진다. 해싱을 위한 백엔드로 Libgcrypt를 사용할 수 있으며, 이는 해당 라이브러리의 모든 알고리즘을 지원한다. 기본 알고리즘을 선택하는 것은 운영 체제 공급업체의 몫이다. LUKS1은 AFsplitter라고 불리는 안티 포렌식 기법을 사용하여 안전한 데이터 소거 및 보호를 가능하게 한다.

 LVM과 함께 사용하는 LUKS 

논리 볼륨 관리(LVM)는 LUKS와 함께 사용할 수 있다.

 LVM on LUKS  잠금 해제된 LUKS 컨테이너 위에서 LVM을 사용하면, 모든 하위 파티션(LVM 논리 볼륨)을 단일 키로 암호화할 수 있다. 이는 LUKS 컨테이너를 여러 파티션으로 나누는 것과 유사하다. 디스크가 복호화되기 전까지는 LVM 구조가 보이지 않는다.
 LUKS on LVM  LVM 논리 볼륨을 암호화하기 위해 LUKS를 사용하면, 암호화된 볼륨이 여러 장치에 걸쳐 있을 수 있다. 하위 LVM 볼륨 그룹은 암호화된 볼륨을 복호화하지 않고도 볼 수 있다.

 전체 디스크 암호화 
섬네일|LUKS 위에서 LVM을 사용하는 자동 파티션 옵션을 보여주는 데비안 인소톨러
LUKS의 일반적인 용도는 전체 디스크 암호화를 제공하는 것이다. 이는 운영 체제 설치 시 루트 파티션을 암호화하여 운영 체제 파일이 사악한 가정부 공격을 받거나 권한이 없는 당사자에 의해 읽히는 것을 방지하는 과정을 포함한다.

리눅스 시스템에서 부트로더(예: GRUB) 자체가 LUKS를 지원하는 경우 부트 파티션(/boot)도 암호화될 수 있다. 이는 리눅스 커널의 변조를 방지하기 위해 수행된다. 그러나 1단계 부트로더나 EFI 시스템 파티션은 암호화될 수 없다(전체 디스크 암호화의 부트 키 문제 참조).

모바일 리눅스 시스템에서는 postmarketOS가 터치스크린을 사용하여 전체 디스크 암호화 시스템의 잠금을 해제할 수 있도록 osk-sdl을 개발했다.

Systemd를 실행하는 시스템의 경우, systemd-homed 구성 요소를 사용하여 개별 홈 디렉토리를 암호화할 수 있다.

 운영 체제 지원 

LUKS의 참조 구현은 리눅스에서 작동하며, 디스크 암호화 백엔드로 dm-crypt를 사용하는 cryptsetup의 개선된 버전을 기반으로 한다. 마이크로소프트 윈도우에서는 리눅스용 윈도우 하위 시스템을 통해 LUKS로 암호화된 디스크를 사용할 수 있다. (이전에는 LibreCrypt를 통해 가능했으나, 현재는 근본적인 보안 결함이 발견되었으며, 이는 DoxBox의 후속인 FreeOTFE를 계승한 것이었다.)

DragonFly BSD는 LUKS를 지원한다.

 설치 프로그램 지원 

여러 리눅스 배포판은 OS 설치 시 루트 장치를 암호화할 수 있도록 지원한다. 이러한 설치 프로그램에는 칼라마레스, 유비퀴티, 데비안 인스톨러, 등이 포함된다.

 온디스크 포맷 

LUKS 헤더는 하위 호환성을 제공한다. 최신 버전의 LUKS는 이전 버전의 헤더를 읽을 수 있다.

 LUKS1 
+ LUKS1 헤더 오프셋  데이터 유형  설명 0    016진수  char[6]     매직 넘버 {'L', 'U', 'K', 'S', 0xBA, 0xBE } 6    616진수   uint16_t    LUKS 버전 (LUKS1의 경우 0x0001) 8    816진수   char[32]    암호 알고리즘 (예: "twofish", "aes") 40   2816진수   char[32]    암호 모드 (예: "cbc-essiv:sha256") 72   4816진수   char[32]    암호화 해시 함수 (예: "sha1", "ripemd160") 104  6816진수   uint32_t    페이로드 오프셋 (암호화된 데이터의 위치, 512바이트 오프셋 단위) 108  6C16진수   uint32_t    키 바이트 수 112  7016진수   char[20]    PBKDF2 마스터 키 체크섬 132  8416진수   char[32]    PBKDF2 마스터 키 솔트 매개변수 164  A416진수   uint32_t    PBKDF2 마스터 키 반복 횟수 (기본값: 10) 168  A816진수   char[40]    파티션의 UUID (예: "504c9fa7-d080-4acf-a829-73227b48fb89") 208  D016진수   (48 바이트)  키슬롯 1 … 544  22016진수   (48 바이트)  키슬롯 8 총 592 바이트

+ 각 키슬롯의 포맷 오프셋  데이터 유형  설명 0  uint32_t  키슬롯 상태: 활성=0x00AC71F3; 비활성=0x0000DEAD 4  uint32_t  PBKDF2 반복 매개변수 4  char[32]  PBKDF2 솔트 매개변수 40  uint32_t  키의 시작 섹터 44  uint32_t  안티 포렌식 스트라이프 수 (기본값: 4000) 총 48 바이트

 LUKS2 

LUKS2 헤더는 바이너리 영역과 JSON 영역, 두 번째 바이너리 및 JSON 영역, 그리고 키슬롯 영역으로 구성된다. 바이너리 및 JSON 영역은 약간의 차이를 두고 두 번 반복된다.

 바이너리 영역 

바이너리 영역의 크기는 항상 4kiB이다. 바이너리와 JSON 영역을 합친 크기는 16kiB에서 4MiB 사이의 2의 거듭제곱 바이트일 수 있으며, 이에 따라 각 JSON 영역의 크기는 12kiB에서 4092kiB 사이가 된다.

+ LUKS2 바이너리 및 JSON 헤더 영역 (각각 16kiB) 오프셋  데이터 유형  설명 첫 번째 바이너리 영역 0   016진수   char[6]     매직 넘버 {'L', 'U', 'K', 'S', 0xBA, 0xBE }, blkid를 통한 빠른 감지 허용 6   616진수   uint16_t    LUKS 버전 (LUKS2의 경우 0x0002) 8   816진수   uint64_t    바이너리 및 JSON 영역의 크기 (일반적으로 16kiB, 400016진수) 16   1016진수   uint64_t    에포크(Epoch), 헤더가 수정될 때마다 증가함 24   1816진수   char[48]    ASCII 파티션 레이블, 널 종료 72   4816진수   char[32]    체크섬 알고리즘을 결정하는 문자열 (일반적으로 "sha256"), 널 종료 104   6816진수   uint8_t[64]   솔트, 각 바이너리 영역마다 고유함 168   A816진수   char[40]    장치의 UUID, 널 종료 (예: "02f47c64-7e74-4711-8bd4-a37613d1ecd3") 208   D016진수   char[48]    보조 "서브시스템" 레이블, 널 종료 256   10016진수   uint64_t   장치 내 이 LUKS 영역의 오프셋 (일반적으로 0) 264   10816진수   _char[184]    패딩, 0으로 채워져야 함 448   1C016진수   uint8_t[64]   체크섬 알고리즘으로 계산된 첫 번째 바이너리 영역의 체크섬, 더 짧으면 0으로 패딩됨 512   20016진수   _char[3584]    패딩, 0으로 채워져야 함 첫 번째 JSON 영역 4096   100016진수   char[12288]    LUKS JSON 객체, 널 종료 두 번째 바이너리 영역 16384   400016진수   char[6]     두 번째 매직 넘버 {'S', 'K', 'U', 'L', 0xBA, 0xBE }   첫 번째 바이너리 영역과 동일 16488   406816진수   uint8_t[64]   솔트, 각 바이너리 영역마다 고유함   첫 번째 바이너리 영역과 동일 16640   410016진수   uint64_t   장치 내 이 LUKS 영역의 오프셋 (일반적으로 400016진수, 1638410진수) 16648   410816진수   _char[184]    패딩, 0으로 채워져야 함 16832   41C016진수   uint8_t[64]   체크섬 알고리즘으로 계산된 두 번째 바이너리 영역의 체크섬, 더 짧으면 0으로 패딩됨 16896   420016진수   _char[3584]    패딩, 0으로 채워져야 함 두 번째 JSON 영역   첫 번째 JSON 영역과 동일 총 32kiB

 JSON 영역 

기본 LUKS2 JSON 메타데이터 객체는 5개의 키를 가진다: config, keyslots, digests, segments, tokens.

Config에는 LUKS 헤더의 일반적인 설정과 정보, 그리고 영구적인 마운트 옵션이 포함된다.

Segments는 데이터를 포함하고 복호화할 수 있는 디스크 상의 영역을 기술한다. 또한 각 세그먼트가 어떤 알고리즘으로 암호화되었는지 기술한다.

Digests는 어떤 키슬롯이 어떤 세그먼트를 복호화할 수 있는 암호화된 키를 포함하는지 기술한다. 여기에는 체크섬 역할을 하며 비밀번호의 정확성을 검증하는 키슬롯의 복호화된 키의 해시가 포함된다.

Keyslots는 암호화된 키를 포함한다. 암호화 방식은 다양하며, 비밀번호, 키 파일, 하드웨어 키 및 기타 방법의 조합을 사용하여 포함된 마스터 키를 복호화할 수 있다.

Tokens 객체는 LUKS와 통합되는 외부 시스템을 위한 추가 정보를 보유할 수 있다.

사용자 데이터인 세그먼트는 커다란 마스터 키와 효율적인 암호화 알고리즘으로 암호화된다. 이 마스터 키는 더 비용이 많이 드는 알고리즘과 잠재적으로 약할 수 있는 사용자 제공 키로 다시 암호화될 수 있으며, 이렇게 암호화된 마스터 키들이 키슬롯에 저장된다. 이는 비밀번호를 추측하려는 무차별 대입 공격을 늦추며, 또한 키슬롯에 다르게 암호화된 동일한 마스터 키를 다시 쓰는 것만으로 전체 데이터 섹션을 재암호화할 필요 없이 복호화 방법과 비밀번호를 변경할 수 있게 해준다.

들여쓰기와 줄 바꿈이 추가된 전형적인 LUKS2 JSON 영역 예시는 다음과 같다.

{
	"keyslots": {
		"0": {
			"type": "luks2",
			"key_size": 64,
			"af": {
				"type": "luks1",
				"stripes": 4000,
				"hash": "sha256"
			},
			"area": {
				"type": "raw",
				"offset": "32768",
				"size": "258048",
				"encryption": "aes-xts-plain64",
				"key_size": 64
			},
			"kdf": {
				"type": "argon2id",
				"time": 4,
				"memory": 1048576,
				"cpus": 4,
				"salt": "YOvmrBmgFT7Ehm7ANZrn0quep1fUFisNCv4e+X8+CLk="
			}
		}
	},
	"tokens": {},
	"segments": {
		"0": {
			"type": "crypt",
			"offset": "16777216",
			"size": "dynamic",
			"iv_tweak": "0",
			"encryption": "aes-xts-plain64",
			"sector_size": 512
		}
	},
	"digests": {
		"0": {
			"type": "pbkdf2",
			"keyslots": [
				"0"
			],
			"segments": [
				"0"
			],
			"hash": "sha256",
			"iterations": 105703,
			"salt": "hrSZ0Sh6t3EVAyeH7XLSH1dEQrRmJwimbjHx85PLS/k=",
			"digest": "tXiDNw8fanGe8QcXewvtzF3AOTOqaIXBmhAGa8Kb42w="
		}
	},
	"config": {
		"json_size": "12288",
		"keyslots_size": "16744448",
		"flags": [
				"allow-discards"
		]
	}
}

 키슬롯 영역 

키슬롯은 두 개의 바이너리 및 JSON 영역 뒤의 장치 영역을 차지한다. 위에서 보여준 전형적인 사례에서 이는 32kiB에서 시작하여 4MiB 또는 16MiB까지 이어진다. 여기서는 16MiB를 예로 든다.

보통 하나의 장치는 오프셋이 16MiB로 설정되고 크기가 dynamic인 단일 세그먼트로 구성된다. 재암호화 중이거나 특이한 구성의 경우, 전체 장치를 빈틈이나 겹침 없이 덮는 여러 세그먼트가 있을 수 있다.

각 세그먼트는 하나의 연관된 다이제스트(digest)를 가져야 하며, 각 다이제스트는 하나 이상의 연관된 키슬롯을 가진다. 키슬롯은 다이제스트와 연관되지 않고 다른 목적으로 사용될 수도 있다.

키슬롯 자체는 키슬롯 영역에 매핑되며, 위의 예시에서는 헤더 바로 뒤인 32kiB에서 시작하는 단일 252kiB 영역으로 매핑된다. 이 영역은 LUKS1과 동일한 방식으로 안티 포렌식 스트라이프로 난독화된다.

다이제스트의 기본 알고리즘인 pbkdf2와 안티 포렌식 스트라이프 유형인 "luks1"은 LUKS1과 동일하다. 키 유도는 기본적으로 LUKS1에서 지원되지 않는 더 강력한 알고리즘을 사용하지만, 지원되는 pbkdf2로 설정할 수도 있다.

 예제 

Cryptsetup은 LUKS 프런트엔드의 참조 구현체이다.

경로가 /dev/sda1인 장치를 암호화하려면:
# cryptsetup luksFormat /dev/sda1

암호화된 장치를 열려면(여기서 name은 매핑된 장치 이름임):
# cryptsetup open /dev/sda1 name

 재암호화 

LUKS 컨테이너의 재암호화는 cryptsetup 도구 자체를 사용하거나 cryptsetup-reencrypt라는 기존 도구를 사용하여 수행할 수 있다. 이러한 도구는 기존의 암호화되지 않은 파일 시스템에 암호화를 추가하거나, 블록 장치에서 암호화를 제거하는 데에도 사용될 수 있다.

두 방법 모두 유사한 구문을 가진다:

# cryptsetup reencrypt /dev/sda1

# cryptsetup-reencrypt /dev/sda1

 같이 보기 
 디스크 암호화 소프트웨어 비교

 각주 

 외부 링크 

 자주 묻는 질문 (FAQ)
 LibreCrypt: 윈도우용 구현체
 LUKS1 사양
 LUKS2 사양

분류:암호 소프트웨어
분류:리눅스 보안 소프트웨어
분류:디스크 암호화