Source code for insights.parsers.cryptsetup_luksDump

"""
LuksDump - command ``cryptsetup luksDump``
==========================================
This class provides parsing for the output of cryptsetup luksDump
<device_name>. Outputs from LUKS1 and LUKS2 are supported.
"""
import string

from insights.core import Parser
from insights.core.exceptions import ParseException, SkipComponent
from insights.core.plugins import parser
from insights.parsr import (AnyChar, Char, EOF, HangingString, Lift,
                            Literal, Many, Opt, String, WithIndent, WS)
from insights.specs import Specs


class DocParser(object):
    def __init__(self):
        value_chars = set(string.printable) - set("\n\r")

        FirstLine = Literal("LUKS header information", value="header") << AnyChar.until(Char("\n")) + Opt(Many(Char("\n")))
        FirstIndent = Literal("  ")
        # we need to replace the \t by 8 spaces in the input,
        # otherwise WithIndent does not work properly
        # SecondIndent = Literal("\t")
        SecondIndent = Literal(" " * 8)

        Key = String(value_chars - set(":")) << Char(":") % "Key"
        Value = WS >> HangingString(value_chars) % "Value"

        MultilineContinuation = Many((Char("\n") + Literal(" " * 15) + SecondIndent) >> String(value_chars))
        Value1 = WS >> String(value_chars) + Opt(MultilineContinuation).map(lambda x: "".join(x)) << Char("\n")
        Value1 = Value1.map(lambda x: ("".join(x)).strip())

        ZeroLevelKVPair = Key + Value1
        FirstLevelKVPair = FirstIndent >> Key + Value1
        SecondLevelKVPair = SecondIndent >> WithIndent(Key + Value) << Opt(Many(Char("\n")))

        Luks2SectionName = Key << Char("\n")
        Luks2SectionEntry = (FirstLevelKVPair + Many(SecondLevelKVPair).map(dict)).map(self.convert_type)
        Luks2Section = Luks2SectionName + Many(Luks2SectionEntry).map(dict) << Opt(Many(Char("\n")))
        Luks2Body = Many(Luks2Section, lower=1)

        Luks1Section = ZeroLevelKVPair + Many(SecondLevelKVPair).map(dict) << Opt(Many(Char("\n")))
        Luks1Body = Many(Luks1Section.map(self.convert_status), lower=1)

        KVBlock = Many(Key + Value1).map(dict)
        LuksHeader = (FirstLine + KVBlock) << Opt(Many(Char("\n")))

        # Luks2Body has to go first, because Luks1Body consumes also valid Luks2 bodies
        self.Top = Lift(self.join_header_and_body) * LuksHeader * (Luks2Body | Luks1Body) << EOF

    def join_header_and_body(self, header, body):
        return dict([header] + body)

    def convert_type(self, section):
        section[1]["type"] = section[0][1]
        return [section[0][0], section[1]]

    def convert_status(self, section):
        section[2]["status"] = section[1]
        return [section[0], section[2]]

    def __call__(self, content):
        try:
            return self.Top(content)
        except Exception:
            raise ParseException("There was an exception when parsing one of the outputs of cryptsetup luksDump commands.")


[docs] @parser(Specs.cryptsetup_luksDump) class LuksDump(Parser): """ Sample input data is in the format:: LUKS header information Version: 2 Epoch: 6 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: cfbcc942-e06b-4c4a-952f-e9c9b2011c27 Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 4096 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 7 Memory: 1048576 Threads: 4 Salt: 3d c4 1b 52 fe 1c 90 d8 2a 35 b2 62 34 e9 0a 59 e9 0e 48 57 b2 dd 45 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: 0: systemd-tpm2 Keyslot: 2 Digests: 0: pbkdf2 Hash: sha256 Iterations: 129774 Salt: e6 31 d5 74 e0 65 83 82 35 03 29 56 0e 80 36 5c 4d cd 4d f9 de 69 39 97 Digest: 21 aa b3 dc 9d 46 9b 0f 3a 0f 57 13 80 c6 0b bf 67 66 9e 73 ed 7d 09 2c Examples: >>> type(parsed_result) <class 'insights.parsers.cryptsetup_luksDump.LuksDump'> >>> from pprint import pprint >>> pprint(parsed_result.dump["header"]) {'Epoch': '6', 'Flags': '(no flags)', 'Keyslots area': '16744448 [bytes]', 'Label': '(no label)', 'Metadata area': '16384 [bytes]', 'Subsystem': '(no subsystem)', 'UUID': 'cfbcc942-e06b-4c4a-952f-e9c9b2011c27', 'Version': '2'} >>> pprint(parsed_result.dump["Keyslots"]["0"]) {'AF hash': 'sha256', 'AF stripes': '4000', 'Area length': '258048 [bytes]', 'Area offset': '32768 [bytes]', 'Cipher': 'aes-xts-plain64', 'Cipher key': '512 bits', 'Digest ID': '0', 'Key': '512 bits', 'Memory': '1048576', 'PBKDF': 'argon2id', 'Priority': 'normal', 'Salt': '3d c4 1b 52 fe 1c 90 d8 2a 35 b2 62 34 e9 0a 59 e9 0e 48 57 b2 dd 45', 'Threads': '4', 'Time cost': '7', 'type': 'luks2'} >>> parsed_result.dump["Tokens"]["0"]["type"] 'systemd-tpm2' Attributes: dump(dict of dicts): A top level dict containing the dictionaries representing the header, data segments, keyslots, digests and tokens. """ # noqa def __init__(self, context): self.parse_dump = DocParser() super(LuksDump, self).__init__(context)
[docs] def parse_content(self, content): if len(content) == 0 or (len(content) == 1 and "not a valid LUKS" in content[0]): raise SkipComponent self.dump = self.parse_dump("\n".join(content).replace("\t", " " * 8) + "\n")