diff --git a/blockdevice/encryption/luks/luks.go b/blockdevice/encryption/luks/luks.go index c0fb9d4..c7978a7 100644 --- a/blockdevice/encryption/luks/luks.go +++ b/blockdevice/encryption/luks/luks.go @@ -27,11 +27,21 @@ import ( // Cipher LUKS2 cipher type. type Cipher int +var keySizeDefaults = map[Cipher]uint{ + AESXTSPlain64Cipher: 512, + XChaCha12Cipher: 256, + XChaCha20Cipher: 256, +} + // String converts to command line string parameter value. func (c Cipher) String() (string, error) { switch c { case AESXTSPlain64Cipher: return AESXTSPlain64CipherString, nil + case XChaCha12Cipher: + return XChaCha12String, nil + case XChaCha20Cipher: + return XChaCha20String, nil default: return "", fmt.Errorf("unknown cipher kind %d", c) } @@ -44,6 +54,10 @@ func ParseCipherKind(s string) (Cipher, error) { fallthrough case AESXTSPlain64CipherString: return AESXTSPlain64Cipher, nil + case XChaCha12String: + return XChaCha12Cipher, nil + case XChaCha20String: + return XChaCha20Cipher, nil default: return 0, fmt.Errorf("unknown cipher kind %s", s) } @@ -52,16 +66,50 @@ func ParseCipherKind(s string) (Cipher, error) { const ( // AESXTSPlain64CipherString string representation of aes-xts-plain64 cipher. AESXTSPlain64CipherString = "aes-xts-plain64" + // XChaCha12String string representation of xchacha12 cipher. + XChaCha12String = "xchacha12,aes-adiantum-plain64" + // XChaCha20String string representation of xchacha20 cipher. + XChaCha20String = "xchacha20,aes-adiantum-plain64" // AESXTSPlain64Cipher represents aes-xts-plain64 encryption cipher. AESXTSPlain64Cipher Cipher = iota + // XChaCha12Cipher represents xchacha12 encryption cipher. + XChaCha12Cipher + // XChaCha20Cipher represents xchacha20 encryption cipher. + XChaCha20Cipher ) +const ( + // PerfNoReadWorkqueue sets --perf-no_read_workqueue. + PerfNoReadWorkqueue = "no_read_workqueue" + // PerfNoWriteWorkqueue sets --perf-no_write_workqueue. + PerfNoWriteWorkqueue = "no_write_workqueue" + // PerfSameCPUCrypt sets --perf-same_cpu_crypt. + PerfSameCPUCrypt = "same_cpu_crypt" +) + +// ValidatePerfOption checks that specified string is a valid perf option. +func ValidatePerfOption(value string) error { + switch value { + case PerfNoReadWorkqueue: + fallthrough + case PerfNoWriteWorkqueue: + fallthrough + case PerfSameCPUCrypt: + return nil + } + + return fmt.Errorf("invalid perf option %v", value) +} + // LUKS implements LUKS2 encryption provider. type LUKS struct { + perfOptions []string cipher Cipher iterTime time.Duration pbkdfForceIterations uint pbkdfMemory uint64 + blockSize uint64 + keySize uint } // New creates new LUKS2 encryption provider. @@ -74,6 +122,10 @@ func New(cipher Cipher, options ...Option) *LUKS { option(l) } + if l.keySize == 0 { + l.keySize = keySizeDefaults[cipher] + } + return l } @@ -86,6 +138,7 @@ func (l *LUKS) Open(deviceName string, key *encryption.Key) (string, error) { args := []string{"luksOpen", deviceName, mappedName, "--key-file=-"} args = append(args, keyslotArgs(key)...) + args = append(args, l.perfArgs()...) err := l.runCommand(args, key.Value) if err != nil { @@ -105,6 +158,11 @@ func (l *LUKS) Encrypt(deviceName string, key *encryption.Key) error { args := []string{"luksFormat", "--type", "luks2", "--key-file=-", "-c", cipher, deviceName} args = append(args, l.argonArgs()...) args = append(args, keyslotArgs(key)...) + args = append(args, l.encryptionArgs()...) + + if l.blockSize != 0 { + args = append(args, fmt.Sprintf("--sector-size=%d", l.blockSize)) + } err = l.runCommand(args, key.Value) if err != nil { @@ -134,6 +192,7 @@ func (l *LUKS) AddKey(devname string, key, newKey *encryption.Key) error { } args = append(args, l.argonArgs()...) + args = append(args, l.encryptionArgs()...) args = append(args, keyslotArgs(newKey)...) return l.runCommand(args, buffer.Bytes()) @@ -159,6 +218,7 @@ func (l *LUKS) SetKey(devname string, oldKey, newKey *encryption.Key) error { } args = append(args, l.argonArgs()...) + args = append(args, l.perfArgs()...) return l.runCommand(args, buffer.Bytes()) } @@ -223,8 +283,6 @@ func (l *LUKS) ReadKeyslots(deviceName string) (*encryption.Keyslots, error) { return keyslots, nil } -// CheckKey try using the key - // runCommand executes cryptsetup with arguments. func (l *LUKS) runCommand(args []string, stdin []byte) error { _, err := cmd.RunContext(cmd.WithStdin( @@ -270,6 +328,26 @@ func (l *LUKS) argonArgs() []string { return args } +func (l *LUKS) perfArgs() []string { + res := []string{} + + for _, o := range l.perfOptions { + res = append(res, fmt.Sprintf("--perf-%s", o)) + } + + return res +} + +func (l *LUKS) encryptionArgs() []string { + res := []string{} + + if l.keySize != 0 { + res = append(res, fmt.Sprintf("--key-size=%d", l.keySize)) + } + + return append(res, l.perfArgs()...) +} + func keyslotArgs(key *encryption.Key) []string { if key.Slot != encryption.AnyKeyslot { return []string{fmt.Sprintf("--key-slot=%d", key.Slot)} diff --git a/blockdevice/encryption/luks/luks_test.go b/blockdevice/encryption/luks/luks_test.go index e2bec31..9b72b48 100644 --- a/blockdevice/encryption/luks/luks_test.go +++ b/blockdevice/encryption/luks/luks_test.go @@ -59,6 +59,7 @@ func (suite *LUKSSuite) TestEncrypt() { provider := luks.New( luks.AESXTSPlain64Cipher, luks.WithIterTime(time.Millisecond*100), + luks.WithPerfOptions(luks.PerfSameCPUCrypt), ) _, err = g.Add(bootSize, gpt.WithPartitionName("boot")) @@ -153,9 +154,7 @@ func TestLUKSSuite(t *testing.T) { t.Skip("can't run the test as non-root") } - hostname, _ := os.Hostname() //nolint: errcheck - - if hostname == "buildkitsandbox" { + if hostname, _ := os.Hostname(); hostname == "buildkitsandbox" { //nolint:errcheck t.Skip("test not supported under buildkit as partition devices are not propagated from /dev") } diff --git a/blockdevice/encryption/luks/options.go b/blockdevice/encryption/luks/options.go index 2250c69..01b16d4 100644 --- a/blockdevice/encryption/luks/options.go +++ b/blockdevice/encryption/luks/options.go @@ -30,3 +30,24 @@ func WithPBKDFMemory(value uint64) Option { l.pbkdfMemory = value } } + +// WithKeySize sets generated key size. +func WithKeySize(value uint) Option { + return func(l *LUKS) { + l.keySize = value + } +} + +// WithBlockSize sets block size. +func WithBlockSize(value uint64) Option { + return func(l *LUKS) { + l.blockSize = value + } +} + +// WithPerfOptions enables encryption perf options. +func WithPerfOptions(options ...string) Option { + return func(l *LUKS) { + l.perfOptions = options + } +}