File: encrypt.go

package info (click to toggle)
golang-github-containers-luksy 0.0~git20240812.2e7307c%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 248 kB
  • sloc: makefile: 14
file content (131 lines) | stat: -rw-r--r-- 4,053 bytes parent folder | download
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
package main

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/containers/luksy"
	"github.com/spf13/cobra"
	"golang.org/x/term"
)

var (
	encryptPasswordFds   = []int{}
	encryptPasswordFiles = []string{}
	encryptSectorSize    = 0
	encryptCipher        = ""
	encryptv1            = false
	encryptForce         = false
)

func init() {
	encryptCommand := &cobra.Command{
		Use:   "encrypt",
		Short: "Create a LUKS-formatted file or device",
		RunE: func(cmd *cobra.Command, args []string) error {
			return encryptCmd(cmd, args)
		},
		Args:    cobra.ExactArgs(2),
		Example: `luksy - encrypt /tmp/plaintext.img /tmp/encrypted.img`,
	}

	flags := encryptCommand.Flags()
	flags.SetInterspersed(false)
	flags.IntSliceVar(&encryptPasswordFds, "password-fd", nil, "read password from file descriptor `number`s")
	flags.StringSliceVar(&encryptPasswordFiles, "password-file", nil, "read password from `file`s")
	flags.BoolVarP(&encryptv1, "luks1", "1", false, "create LUKSv1 instead of LUKSv2")
	flags.IntVar(&encryptSectorSize, "sector-size", 0, "sector size for LUKSv2")
	flags.StringVarP(&encryptCipher, "cipher", "c", "", "encryption algorithm")
	flags.BoolVarP(&encryptForce, "force-overwrite", "f", false, "forcibly overwrite existing output files")
	rootCmd.AddCommand(encryptCommand)
}

func encryptCmd(cmd *cobra.Command, args []string) error {
	_, err := os.Stat(args[1])
	if (err == nil || !os.IsNotExist(err)) && !encryptForce {
		if err != nil {
			return fmt.Errorf("checking if %q exists: %w", args[1], err)
		}
		return fmt.Errorf("-f not specified, and %q exists", args[1])
	}
	input, err := os.Open(args[0])
	if err != nil {
		return fmt.Errorf("open %q: %w", args[0], err)
	}
	defer input.Close()
	st, err := input.Stat()
	if err != nil {
		return err
	}
	if st.Size()%luksy.V1SectorSize != 0 {
		return fmt.Errorf("%q is not of a suitable size, expected a multiple of %d bytes", input.Name(), luksy.V1SectorSize)
	}
	var passwords []string
	for _, encryptPasswordFd := range encryptPasswordFds {
		passFile := os.NewFile(uintptr(encryptPasswordFd), fmt.Sprintf("FD %d", encryptPasswordFd))
		passBytes, err := io.ReadAll(passFile)
		if err != nil {
			return fmt.Errorf("reading from descriptor %d: %w", encryptPasswordFd, err)
		}
		passwords = append(passwords, string(passBytes))
	}
	for _, encryptPasswordFile := range encryptPasswordFiles {
		passBytes, err := os.ReadFile(encryptPasswordFile)
		if err != nil {
			return err
		}
		passwords = append(passwords, string(passBytes))
	}
	if len(passwords) == 0 {
		if term.IsTerminal(int(os.Stdin.Fd())) {
			fmt.Fprintf(os.Stdout, "Password: ")
			os.Stdout.Sync()
			passBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
			if err != nil {
				return fmt.Errorf("reading from stdin: %w", err)
			}
			passwords = append(passwords, string(passBytes))
			fmt.Fprintln(os.Stdout)
		} else {
			passBytes, err := io.ReadAll(os.Stdin)
			if err != nil {
				return fmt.Errorf("reading from stdin: %w", err)
			}
			passwords = append(passwords, string(passBytes))
		}
	}
	for i := range passwords {
		passwords[i] = strings.TrimRightFunc(passwords[i], func(r rune) bool { return r == '\r' || r == '\n' })
	}
	var header []byte
	var encryptStream func([]byte) ([]byte, error)
	if encryptv1 {
		header, encryptStream, encryptSectorSize, err = luksy.EncryptV1(passwords, encryptCipher)
		if err != nil {
			return fmt.Errorf("creating luksv1 data: %w", err)
		}
	} else {
		header, encryptStream, encryptSectorSize, err = luksy.EncryptV2(passwords, encryptCipher, encryptSectorSize)
		if err != nil {
			return fmt.Errorf("creating luksv2 data: %w", err)
		}
	}
	output, err := os.Create(args[1])
	if err != nil {
		return fmt.Errorf("create %q: %w", args[1], err)
	}
	defer output.Close()
	n, err := output.Write(header)
	if err != nil {
		return err
	}
	if n != len(header) {
		return fmt.Errorf("short write while writing header to %q", output.Name())
	}
	wc := luksy.EncryptWriter(encryptStream, output, encryptSectorSize)
	defer wc.Close()
	_, err = io.Copy(wc, input)
	return err
}