Skip to content

Commit

Permalink
WIP: AutoYaST add crypt_pbkdf, crypt_label, crypt_cipher and crypt_ke…
Browse files Browse the repository at this point in the history
…y_size
  • Loading branch information
ancorgs committed Sep 12, 2023
1 parent 8e00070 commit 3a6c84e
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 3 deletions.
20 changes: 20 additions & 0 deletions src/lib/y2storage/autoinst_profile/partition_section.rb
Expand Up @@ -65,6 +65,10 @@ class PartitionSection < ::Installation::AutoinstProfile::SectionWithAttributes
{ name: :loop_fs },
{ name: :crypt_method },
{ name: :crypt_key },
{ name: :crypt_pbkdf },
{ name: :crypt_label },
{ name: :crypt_cipher },
{ name: :crypt_key_size },
{ name: :raid_name },
{ name: :raid_options },
{ name: :mkfs_options },
Expand Down Expand Up @@ -104,6 +108,22 @@ def self.attributes
# @!attribute crypt_key
# @return [String] encryption key

# @!attribute crypt_pbkdf
# @return [Symbol,nil] password-based derivation function for LUKS2 (:pbkdf2, :argon2i,
# :argon2id). See {Y2Storage::PbkdFunction}.

# @!attribute crypt_label
# @return [String,nil] LUKS label if LUKS2 is going to be used

# @!attribute crypt_cipher
# @return [String,nil] specific cipher if LUKS is going to be used
#
# @!attribute crypt_key_size
# Specific key size (in bits) if LUKS is going to be used
#
# @return [Integer,nil] If nil, the default key size will be used. If an integer
# value is used, it has to be a multiple of 8.

# @!attribute filesystem
# @return [Symbol] file system type to use in the partition, it also
# influences other fields
Expand Down
39 changes: 38 additions & 1 deletion src/lib/y2storage/encryption.rb
Expand Up @@ -63,10 +63,26 @@ class Encryption < BlkDevice
storage_forward :key_file=

# @!attribute cipher
# @return [String] the encryption cipher
# The encryption cipher
#
# Currently only supported for LUKS
#
# @return [String] if empty, the default of cryptsetup will be used during creation
storage_forward :cipher
storage_forward :cipher=

# @!attribute key_size
# The key size in bytes
#
# Currently only supported for LUKS
#
# Note the value is expressed in bytes. That's dictated by libstorage-ng, even when cryptsetup
# and all the LUKS-related documentation use bits for expressing the key size.
#
# @return [Integer] if zero, the default of cryptsetup will be used during creation
storage_forward :key_size
storage_forward :key_size=

# @!attribute pbkdf_value
# String representation of {#pbkdf}, an empty string is equivalent to a nil value on {#pbkdf}
#
Expand Down Expand Up @@ -429,6 +445,27 @@ def supports_pbkdf?
type.is?(:luks2)
end

# Whether the attribute #label makes sense for this object
#
# @return [Boolean]
def supports_label?
type.is?(:luks2)
end

# Whether the attribute #cipher makes sense for this object
#
# @return [Boolean]
def supports_cipher?
type.is?(:luks1, :luks2)
end

# Whether the attribute #key_size makes sense for this object
#
# @return [Boolean]
def supports_key_size?
type.is?(:luks1, :luks2)
end

protected

# @see Device#is?
Expand Down
5 changes: 4 additions & 1 deletion src/lib/y2storage/encryption_method/luks2.rb
Expand Up @@ -23,6 +23,8 @@
require "y2storage/encryption_processes/luks"
require "y2storage/pbkd_function"

Yast.import "Mode"

module Y2Storage
module EncryptionMethod
# The encryption method that allows to create and identify an encrypted device using regular
Expand Down Expand Up @@ -60,7 +62,8 @@ def create_device(blk_device, dm_name, pbkdf: nil, label: "")

# @see Base#available?
def available?
StorageEnv.instance.luks2_available?
# jsc#PED-3878 and jsc#GEHC-6
Yast::Mode.auto || StorageEnv.instance.luks2_available?
end

private
Expand Down
34 changes: 33 additions & 1 deletion src/lib/y2storage/planned/can_be_encrypted.rb
Expand Up @@ -47,6 +47,28 @@ module CanBeEncrypted
# @return [PbkdFunction, nil] nil to use the default derivation function
attr_accessor :encryption_pbkdf

# LUKS label to use for the device if labels are supported (eg. LUKS2)
#
# @return [String, nil] nil or empty string to not set any label
attr_accessor :encryption_label

# Cipher to use when encrypting a LUKS device
#
# @return [String, nil] nil or empty string to use the default cipher
attr_accessor :encryption_cipher

# Key size (in bits) to use when encrypting a LUKS device
#
# Any positive value must be a multiple of 8.
#
# Note this uses bits since that's the standard unit for the key size in LUKS and is
# also the unit used by cryptsetup for all its inputs and outputs.
#
# Under the hood, this is translated to bytes because that's what libstorage-ng uses.
#
# @return [Integer, nil] nil or zero to use the default size
attr_accessor :encryption_key_size

# Initializations of the mixin, to be called from the class constructor.
def initialize_can_be_encrypted; end

Expand Down Expand Up @@ -81,7 +103,10 @@ def final_device!(plain_device)
if create_encryption?
method = encryption_method || EncryptionMethod.find(:luks1)
result = plain_device.encrypt(method: method, password: encryption_password)
result.pbkdf = encryption_pbkdf if encryption_pbkdf && result.supports_pbkdf?
assign_enc_attr(result, :pbkdf)
assign_enc_attr(result, :label)
assign_enc_attr(result, :cipher)
result.key_size = (encryption_key_size / 8) if encryption_key_size && result.supports_key_size?
log.info "Device encrypted. Returning the new device #{result.inspect}"
else
log.info "No need to encrypt. Returning the existing device #{result.inspect}"
Expand All @@ -103,6 +128,13 @@ def create_encryption?
false
end

def assign_enc_attr(encryption, attr)
return if send(:"encryption_#{attr}").nil?
return unless encryption.send(:"supports_#{attr}?")

encryption.send(:"#{attr}=", send(:"encryption_#{attr}"))
end

# Class methods for the mixin
module ClassMethods
# Space that will be used by the encryption data structures in a device.
Expand Down
14 changes: 14 additions & 0 deletions src/lib/y2storage/proposal/autoinst_drive_planner.rb
Expand Up @@ -143,6 +143,10 @@ def add_encryption_attrs(device, partition_section)
else
DEFAULT_ENCRYPTION_METHOD
end
device.encryption_pbkdf = find_encryption_pbkdf(partition_section)
device.encryption_label = partition_section.crypt_label
device.encryption_cipher = partition_section.crypt_cipher
device.encryption_key_size = partition_section.crypt_key_size&.to_i
return unless device.encryption_method&.password_required?

device.encryption_password = find_encryption_password(partition_section)
Expand Down Expand Up @@ -185,6 +189,16 @@ def find_encryption_password(partition_section)
partition_section.crypt_key
end

def find_encryption_pbkdf(partition_section)
return unless partition_section.crypt_pbkdf

# Possible errors we may want to check and report:
# - pbkdf for a non LUKS2 method
# - invalid pbkdf (not corresponding to a PbkdFunction)

Y2Storage::PbkdFunction.find(partition_section.crypt_pbkdf)
end

# Sets common filesystem attributes
#
# @param device [Planned::Device]
Expand Down
133 changes: 133 additions & 0 deletions test/y2storage/autoinst_proposal_encryption_test.rb
@@ -0,0 +1,133 @@
#!/usr/bin/env rspec

# Copyright (c) [2023] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program 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 this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require_relative "spec_helper"
require "y2storage"

describe Y2Storage::AutoinstProposal do
before do
fake_scenario(scenario)

allow(Yast::Mode).to receive(:auto).and_return(true)
end

subject(:proposal) do
described_class.new(
partitioning: partitioning, devicegraph: fake_devicegraph, issues_list: issues_list
)
end

let(:scenario) { "empty_disks" }
let(:issues_list) { ::Installation::AutoinstIssues::List.new }

let(:partitioning) do
[
{
"device" => "/dev/sda",
"type" => :CT_DISK, "use" => "all", "initialize" => true, "disklabel" => "gpt",
"partitions" => [partition]
}
]
end

describe "#propose" do
context "when creating a LUKS2 device with default options" do
let(:partition) do
{ "mount" => "/", "crypt_key" => "s3cr3t", "crypt_method" => :luks2 }
end

it "encrypts the device with LUKS2 as encryption method" do
proposal.propose
enc = proposal.devices.encryptions.first
expect(enc.method).to eq Y2Storage::EncryptionMethod::LUKS2
end

it "does not set any LUKS label" do
proposal.propose
enc = proposal.devices.encryptions.first
expect(enc.label).to eq ""
end

it "does not set any derivation function, cipher or key size" do
proposal.propose
enc = proposal.devices.encryptions.first
expect(enc.pbkdf).to be_nil
expect(enc.cipher).to eq ""
expect(enc.key_size).to be_zero
end
end

context "when creating a LUKS2 device with a given password derivation function" do
let(:partition) do
{ "mount" => "/", "crypt_key" => "s3cr3t", "crypt_method" => :luks2, "crypt_pbkdf" => :argon2i }
end

it "uses the corresponding derivation function" do
proposal.propose
enc = proposal.devices.encryptions.first
expect(enc.pbkdf).to eq Y2Storage::PbkdFunction::ARGON2I
end
end

context "when creating a LUKS2 device with given cipher and key size" do
let(:partition) do
{
"mount" => "/", "crypt_key" => "s3cr3t", "crypt_method" => :luks2,
"crypt_cipher" => "aes-xts-plain64", "crypt_key_size" => 512
}
end

it "uses the corresponding cipher and key size" do
proposal.propose
enc = proposal.devices.encryptions.first
expect(enc.cipher).to eq "aes-xts-plain64"
# libstorage-ng uses bytes instead of bits to represent the key size, contrary to all LUKS
# documentation and to cryptsetup
expect(enc.key_size).to eq 64
end
end

context "when creating a LUKS2 device with a given LUKS label" do
let(:partition) do
{ "mount" => "/", "crypt_key" => "s3cr3t", "crypt_method" => :luks2, "crypt_label" => "crpt" }
end

it "sets the label in the LUKS device" do
proposal.propose
enc = proposal.devices.encryptions.first
expect(enc.label).to eq "crpt"
end
end
=begin
context "when creating a LUKS1 device with a given password derivation function" do
end
context "when creating a LUKS1 device with a LUKS label" do
end
context "when creating a SECURE_SWAP device with default options" do
end
context "when creating a SECURE_SWAP device with given cipher and key size" do
end
=end
end
end

0 comments on commit 3a6c84e

Please sign in to comment.