1
0
mirror of https://github.com/containers/buildah.git synced 2025-07-30 04:23:09 +03:00

Add buildah mkcw, add --cw to buildah commit and buildah build

Add a --cw option to `buildah build` and `buildah commit`, which takes a
comma-separated list of arguments and produces an image laid out for use
as a confidential workload:
  type: sev or snp
  attestation_url: location of a key broker server
  cpus: expected number of virtual CPUs to run with
  memory: expected megabytes of memory to run with
  workload_id: a distinguishing identifier for the key broker server
  ignore_attestation_errors: ignore errors registering the workload
  passphrase: for encrypting the disk image
  slop: extra space to allocate for the disk image

At least one of attestation_url and passphrase must be specified in
order for the encrypted disk image to be decryptable at run-time.  Other
arguments can be omitted.  ignore_attestation_errors is intentionally
undocumented, as it's mainly used to permit some amount of testing on
systems which don't have the required hardware.

Add an `mkcw` top-level command, for converting directly from an image
to a confidential workload.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai
2023-07-17 16:27:19 -04:00
parent 25473ec2ba
commit e89fac6c9b
75 changed files with 8680 additions and 20 deletions

View File

@ -39,7 +39,7 @@ LIBSECCOMP_COMMIT := release-2.3
EXTRA_LDFLAGS ?= EXTRA_LDFLAGS ?=
BUILDAH_LDFLAGS := $(GO_LDFLAGS) '-X main.GitCommit=$(GIT_COMMIT) -X main.buildInfo=$(SOURCE_DATE_EPOCH) -X main.cniVersion=$(CNI_COMMIT) $(EXTRA_LDFLAGS)' BUILDAH_LDFLAGS := $(GO_LDFLAGS) '-X main.GitCommit=$(GIT_COMMIT) -X main.buildInfo=$(SOURCE_DATE_EPOCH) -X main.cniVersion=$(CNI_COMMIT) $(EXTRA_LDFLAGS)'
SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go copier/*.go define/*.go docker/*.go internal/parse/*.go internal/source/*.go internal/util/*.go manifests/*.go pkg/chrootuser/*.go pkg/cli/*.go pkg/completion/*.go pkg/formats/*.go pkg/overlay/*.go pkg/parse/*.go pkg/rusage/*.go pkg/sshagent/*.go pkg/umask/*.go pkg/util/*.go util/*.go SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go copier/*.go define/*.go docker/*.go internal/mkcw/*.go internal/mkcw/types/*.go internal/parse/*.go internal/source/*.go internal/util/*.go manifests/*.go pkg/chrootuser/*.go pkg/cli/*.go pkg/completion/*.go pkg/formats/*.go pkg/overlay/*.go pkg/parse/*.go pkg/rusage/*.go pkg/sshagent/*.go pkg/umask/*.go pkg/util/*.go util/*.go
LINTFLAGS ?= LINTFLAGS ?=
@ -69,9 +69,22 @@ static:
mkdir -p ./bin mkdir -p ./bin
cp -rfp ./result/bin/* ./bin/ cp -rfp ./result/bin/* ./bin/
bin/buildah: $(SOURCES) cmd/buildah/*.go bin/buildah: $(SOURCES) cmd/buildah/*.go internal/mkcw/embed/entrypoint.gz
$(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah $(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah
ifneq ($(shell as --version | grep x86_64),)
internal/mkcw/embed/entrypoint: internal/mkcw/embed/entrypoint.s
$(AS) -o $(patsubst %.s,%.o,$^) $^
$(LD) -o $@ $(patsubst %.s,%.o,$^)
strip $@
else
.PHONY: internal/mkcw/embed/entrypoint
endif
internal/mkcw/embed/entrypoint.gz: internal/mkcw/embed/entrypoint
$(RM) $@
gzip -k $^
.PHONY: buildah .PHONY: buildah
buildah: bin/buildah buildah: bin/buildah

View File

@ -386,6 +386,11 @@ type ImportFromImageOptions struct {
SystemContext *types.SystemContext SystemContext *types.SystemContext
} }
// ConfidentialWorkloadOptions encapsulates options which control whether or not
// we output an image whose rootfs contains a LUKS-compatibly-encrypted disk image
// instead of the usual rootfs contents.
type ConfidentialWorkloadOptions = define.ConfidentialWorkloadOptions
// NewBuilder creates a new build container. // NewBuilder creates a new build container.
func NewBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) { func NewBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) {
if options.CommonBuildOpts == nil { if options.CommonBuildOpts == nil {

View File

@ -14,16 +14,22 @@ import (
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
var logLevel string
debug := false debug := false
if InitReexec() { if InitReexec() {
return return
} }
flag.BoolVar(&debug, "debug", false, "turn on debug logging") flag.BoolVar(&debug, "debug", false, "turn on debug logging")
flag.StringVar(&logLevel, "log-level", "error", "log level")
flag.Parse() flag.Parse()
logrus.SetLevel(logrus.ErrorLevel) level, err := logrus.ParseLevel(logLevel)
if debug { if err != nil {
logrus.SetLevel(logrus.DebugLevel) logrus.Fatalf("error parsing log level %q: %v", logLevel, err)
} }
if debug && level < logrus.DebugLevel {
level = logrus.DebugLevel
}
logrus.SetLevel(level)
os.Exit(m.Run()) os.Exit(m.Run())
} }

View File

@ -27,6 +27,7 @@ type commitInputOptions struct {
blobCache string blobCache string
certDir string certDir string
creds string creds string
cwOptions string
disableCompression bool disableCompression bool
format string format string
iidfile string iidfile string
@ -87,6 +88,7 @@ func commitListFlagSet(cmd *cobra.Command, opts *commitInputOptions) {
_ = cmd.RegisterFlagCompletionFunc("cert-dir", completion.AutocompleteDefault) _ = cmd.RegisterFlagCompletionFunc("cert-dir", completion.AutocompleteDefault)
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry") flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
_ = cmd.RegisterFlagCompletionFunc("creds", completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc("creds", completion.AutocompleteNone)
flags.StringVar(&opts.cwOptions, "cw", "", "confidential workload `options`")
flags.BoolVarP(&opts.disableCompression, "disable-compression", "D", true, "don't compress layers") flags.BoolVarP(&opts.disableCompression, "disable-compression", "D", true, "don't compress layers")
flags.StringVarP(&opts.format, "format", "f", defaultFormat(), "`format` of the image manifest and metadata") flags.StringVarP(&opts.format, "format", "f", defaultFormat(), "`format` of the image manifest and metadata")
_ = cmd.RegisterFlagCompletionFunc("format", completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc("format", completion.AutocompleteNone)
@ -239,6 +241,14 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error
options.HistoryTimestamp = &timestamp options.HistoryTimestamp = &timestamp
} }
if iopts.cwOptions != "" {
confidentialWorkloadOptions, err := parse.GetConfidentialWorkloadOptions(iopts.cwOptions)
if err != nil {
return fmt.Errorf("parsing --cw arguments: %w", err)
}
options.ConfidentialWorkloadOptions = confidentialWorkloadOptions
}
if exclusiveFlags > 1 { if exclusiveFlags > 1 {
return errors.New("can not use more then one timestamp option at at time") return errors.New("can not use more then one timestamp option at at time")
} }

View File

@ -224,8 +224,8 @@ func Tail(a []string) []string {
return []string{} return []string{}
} }
// UsageTemplate returns the usage template for podman commands // UsageTemplate returns the usage template for buildah commands
// This blocks the displaying of the global options. The main podman // This blocks the displaying of the global options. The main buildah
// command should not use this. // command should not use this.
func UsageTemplate() string { func UsageTemplate() string {
return `Usage:{{if .Runnable}} return `Usage:{{if .Runnable}}

View File

@ -104,7 +104,7 @@ func init() {
rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use") rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use")
rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use") rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use")
rootCmd.PersistentFlags().StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file") rootCmd.PersistentFlags().StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file")
rootCmd.PersistentFlags().StringVar(&globalFlagResults.LogLevel, logLevel, "warn", `The log level to be used. Either "trace", "debug", "info", "warn", "error", "fatal", or "panic".`) rootCmd.PersistentFlags().StringVar(&globalFlagResults.LogLevel, logLevel, "warn", `the log level to be used, one of "trace", "debug", "info", "warn", "error", "fatal", or "panic"`)
rootCmd.PersistentFlags().StringVar(&globalFlagResults.CPUProfile, "cpu-profile", "", "`file` to write CPU profile") rootCmd.PersistentFlags().StringVar(&globalFlagResults.CPUProfile, "cpu-profile", "", "`file` to write CPU profile")
rootCmd.PersistentFlags().StringVar(&globalFlagResults.MemoryProfile, "memory-profile", "", "`file` to write memory profile") rootCmd.PersistentFlags().StringVar(&globalFlagResults.MemoryProfile, "memory-profile", "", "`file` to write memory profile")

77
cmd/buildah/mkcw.go Normal file
View File

@ -0,0 +1,77 @@
package main
import (
"fmt"
"os"
"github.com/containers/buildah"
"github.com/containers/buildah/define"
"github.com/containers/buildah/pkg/parse"
"github.com/spf13/cobra"
)
func mkcwCmd(c *cobra.Command, args []string, options buildah.CWConvertImageOptions) error {
ctx := getContext()
systemContext, err := parse.SystemContextFromOptions(c)
if err != nil {
return err
}
if options.AttestationURL == "" && options.DiskEncryptionPassphrase == "" {
return fmt.Errorf("neither --attestation-url nor --passphrase flags provided, disk would not be decryptable")
}
store, err := getStore(c)
if err != nil {
return err
}
options.InputImage = args[0]
options.Tag = args[1]
options.ReportWriter = os.Stderr
imageID, _, _, err := buildah.CWConvertImage(ctx, systemContext, store, options)
if err == nil {
fmt.Printf("%s\n", imageID)
}
return err
}
func init() {
var teeType string
var options buildah.CWConvertImageOptions
mkcwDescription := `Convert a conventional image to a confidential workload image.`
mkcwCommand := &cobra.Command{
Use: "mkcw",
Short: "Convert a conventional image to a confidential workload image",
Long: mkcwDescription,
RunE: func(cmd *cobra.Command, args []string) error {
options.TeeType = define.TeeType(teeType)
return mkcwCmd(cmd, args, options)
},
Example: `buildah mkcw localhost/repository:typical localhost/repository:cw`,
Args: cobra.ExactArgs(2),
}
mkcwCommand.SetUsageTemplate(UsageTemplate())
rootCmd.AddCommand(mkcwCommand)
flags := mkcwCommand.Flags()
flags.SetInterspersed(false)
flags.StringVarP(&teeType, "type", "t", "", "TEE (trusted execution environment) type: SEV,SNP (default: SNP)")
flags.StringVarP(&options.AttestationURL, "attestation-url", "u", "", "attestation server URL")
flags.StringVarP(&options.BaseImage, "base-image", "b", "", "alternate base image (default: scratch)")
flags.StringVarP(&options.DiskEncryptionPassphrase, "passphrase", "p", "", "disk encryption passphrase")
flags.IntVarP(&options.CPUs, "cpus", "c", 0, "number of CPUs to expect")
flags.IntVarP(&options.Memory, "memory", "m", 0, "amount of memory to expect (MB)")
flags.StringVarP(&options.WorkloadID, "workload-id", "w", "", "workload ID")
flags.StringVarP(&options.Slop, "slop", "s", "25%", "extra space needed for converting a container rootfs to a disk image")
flags.StringVarP(&options.FirmwareLibrary, "firmware-library", "f", "", "location of libkrunfw-sev.so")
flags.BoolVarP(&options.IgnoreAttestationErrors, "ignore-attestation-errors", "", false, "ignore attestation errors")
if err := flags.MarkHidden("ignore-attestation-errors"); err != nil {
panic(fmt.Sprintf("error marking ignore-attestation-errors as hidden: %v", err))
}
flags.String("signature-policy", "", "`pathname` of signature policy file (not usually used)")
if err := flags.MarkHidden("signature-policy"); err != nil {
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
}
}

View File

@ -105,6 +105,10 @@ type CommitOptions struct {
// integers in the slice represent 0-indexed layer indices, with support for negative // integers in the slice represent 0-indexed layer indices, with support for negative
// indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer. // indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer.
OciEncryptLayers *[]int OciEncryptLayers *[]int
// ConfidentialWorkloadOptions is used to force the output image's rootfs to contain a
// LUKS-compatibly encrypted disk image (for use with krun) instead of the usual
// contents of a rootfs.
ConfidentialWorkloadOptions ConfidentialWorkloadOptions
// UnsetEnvs is a list of environments to not add to final image. // UnsetEnvs is a list of environments to not add to final image.
// Deprecated: use UnsetEnv() before committing instead. // Deprecated: use UnsetEnv() before committing instead.
UnsetEnvs []string UnsetEnvs []string

217
convertcw.go Normal file
View File

@ -0,0 +1,217 @@
package buildah
import (
"context"
"fmt"
"io"
"time"
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal/mkcw"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
encconfig "github.com/containers/ocicrypt/config"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
)
// CWConvertImageOptions provides both required and optional bits of
// configuration for CWConvertImage().
type CWConvertImageOptions struct {
// Required parameters.
InputImage string
// If supplied, we'll tag the resulting image with the specified name.
Tag string
OutputImage types.ImageReference
// If supplied, we'll register the workload with this server.
// Practically necessary if DiskEncryptionPassphrase is not set, in
// which case we'll generate one and throw it away after.
AttestationURL string
// Used to measure the environment. If left unset (0), defaults will be applied.
CPUs int
Memory int
// Can be manually set. If left unset ("", false, nil), reasonable values will be used.
TeeType define.TeeType
IgnoreAttestationErrors bool
WorkloadID string
DiskEncryptionPassphrase string
Slop string
FirmwareLibrary string
BaseImage string
Logger *logrus.Logger
// Passed through to BuilderOptions. Most settings won't make
// sense to be made available here because we don't launch a process.
ContainerSuffix string
PullPolicy PullPolicy
BlobDirectory string
SignaturePolicyPath string
ReportWriter io.Writer
IDMappingOptions *IDMappingOptions
Format string
MaxPullRetries int
PullRetryDelay time.Duration
OciDecryptConfig *encconfig.DecryptConfig
MountLabel string
}
// CWConvertImage takes the rootfs and configuration from one image, generates a
// LUKS-encrypted disk image that more or less includes them both, and puts the
// result into a new container image.
// Returns the new image's ID and digest on success, along with a canonical
// reference for it if a repository name was specified.
func CWConvertImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options CWConvertImageOptions) (string, reference.Canonical, digest.Digest, error) {
// Apply our defaults if some options aren't set.
logger := options.Logger
if logger == nil {
logger = logrus.StandardLogger()
}
// Now create the target working container, pulling the base image if
// there is one and it isn't present.
builderOptions := BuilderOptions{
FromImage: options.BaseImage,
SystemContext: systemContext,
Logger: logger,
ContainerSuffix: options.ContainerSuffix,
PullPolicy: options.PullPolicy,
BlobDirectory: options.BlobDirectory,
SignaturePolicyPath: options.SignaturePolicyPath,
ReportWriter: options.ReportWriter,
IDMappingOptions: options.IDMappingOptions,
Format: options.Format,
MaxPullRetries: options.MaxPullRetries,
PullRetryDelay: options.PullRetryDelay,
OciDecryptConfig: options.OciDecryptConfig,
MountLabel: options.MountLabel,
}
target, err := NewBuilder(ctx, store, builderOptions)
if err != nil {
return "", nil, "", fmt.Errorf("creating container from target image: %w", err)
}
defer func() {
if err := target.Delete(); err != nil {
logrus.Warnf("deleting target container: %v", err)
}
}()
targetDir, err := target.Mount("")
if err != nil {
return "", nil, "", fmt.Errorf("mounting target container: %w", err)
}
defer func() {
if err := target.Unmount(); err != nil {
logrus.Warnf("unmounting target container: %v", err)
}
}()
// Mount the source image, pulling it first if necessary.
builderOptions = BuilderOptions{
FromImage: options.InputImage,
SystemContext: systemContext,
Logger: logger,
ContainerSuffix: options.ContainerSuffix,
PullPolicy: options.PullPolicy,
BlobDirectory: options.BlobDirectory,
SignaturePolicyPath: options.SignaturePolicyPath,
ReportWriter: options.ReportWriter,
IDMappingOptions: options.IDMappingOptions,
Format: options.Format,
MaxPullRetries: options.MaxPullRetries,
PullRetryDelay: options.PullRetryDelay,
OciDecryptConfig: options.OciDecryptConfig,
MountLabel: options.MountLabel,
}
source, err := NewBuilder(ctx, store, builderOptions)
if err != nil {
return "", nil, "", fmt.Errorf("creating container from source image: %w", err)
}
defer func() {
if err := source.Delete(); err != nil {
logrus.Warnf("deleting source container: %v", err)
}
}()
sourceInfo := GetBuildInfo(source)
if err != nil {
return "", nil, "", fmt.Errorf("retrieving info about source image: %w", err)
}
sourceImageID := sourceInfo.FromImageID
sourceSize, err := store.ImageSize(sourceImageID)
if err != nil {
return "", nil, "", fmt.Errorf("computing size of source image: %w", err)
}
sourceDir, err := source.Mount("")
if err != nil {
return "", nil, "", fmt.Errorf("mounting source container: %w", err)
}
defer func() {
if err := source.Unmount(); err != nil {
logrus.Warnf("unmounting source container: %v", err)
}
}()
// Generate the image contents.
archiveOptions := mkcw.ArchiveOptions{
AttestationURL: options.AttestationURL,
CPUs: options.CPUs,
Memory: options.Memory,
TempDir: targetDir,
TeeType: options.TeeType,
IgnoreAttestationErrors: options.IgnoreAttestationErrors,
ImageSize: sourceSize,
WorkloadID: options.WorkloadID,
DiskEncryptionPassphrase: options.DiskEncryptionPassphrase,
Slop: options.Slop,
FirmwareLibrary: options.FirmwareLibrary,
Logger: logger,
}
rc, workloadConfig, err := mkcw.Archive(sourceDir, &source.OCIv1, archiveOptions)
if err != nil {
return "", nil, "", fmt.Errorf("generating encrypted image content: %w", err)
}
if err = archive.Untar(rc, targetDir, &archive.TarOptions{}); err != nil {
if err = rc.Close(); err != nil {
logger.Warnf("cleaning up: %v", err)
}
return "", nil, "", fmt.Errorf("saving encrypted image content: %w", err)
}
if err = rc.Close(); err != nil {
return "", nil, "", fmt.Errorf("cleaning up: %w", err)
}
// Commit the image. Clear out most of the configuration (if there is any — we default
// to scratch as a base) so that an engine that doesn't or can't set up a TEE will just
// run the static entrypoint. The rest of the configuration which the runtime consults
// is in the .krun_config.json file in the encrypted filesystem.
logger.Log(logrus.DebugLevel, "committing disk image")
target.ClearAnnotations()
target.ClearEnv()
target.ClearLabels()
target.ClearOnBuild()
target.ClearPorts()
target.ClearVolumes()
target.SetCmd(nil)
target.SetCreatedBy(fmt.Sprintf(": convert %q for use with %q", sourceImageID, workloadConfig.Type))
target.SetDomainname("")
target.SetEntrypoint([]string{"/entrypoint"})
target.SetHealthcheck(nil)
target.SetHostname("")
target.SetMaintainer("")
target.SetShell(nil)
target.SetUser("")
target.SetWorkDir("")
commitOptions := CommitOptions{
SystemContext: systemContext,
}
if options.Tag != "" {
commitOptions.AdditionalTags = append(commitOptions.AdditionalTags, options.Tag)
}
return target.Commit(ctx, options.OutputImage, commitOptions)
}

163
convertcw_test.go Normal file
View File

@ -0,0 +1,163 @@
package buildah
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"sync"
"testing"
"github.com/containers/buildah/internal/mkcw"
mkcwtypes "github.com/containers/buildah/internal/mkcw/types"
"github.com/containers/image/v5/types"
"github.com/containers/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// dummyAttestationHandler replies with a fixed response code to requests to
// the right path, and caches passphrases indexed by workload ID
type dummyAttestationHandler struct {
t *testing.T
status int
passphrases map[string]string
passphrasesLock sync.Mutex
}
func (d *dummyAttestationHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
var body bytes.Buffer
if req.Body != nil {
if _, err := io.Copy(&body, req.Body); err != nil {
d.t.Logf("reading request body: %v", err)
return
}
req.Body.Close()
}
if req.URL != nil && req.URL.Path == "/kbs/v0/register_workload" {
var registrationRequest mkcwtypes.RegistrationRequest
// if we can't decode the client request, bail
if err := json.Unmarshal(body.Bytes(), &registrationRequest); err != nil {
rw.WriteHeader(http.StatusInternalServerError)
return
}
// cache the passphrase
d.passphrasesLock.Lock()
if d.passphrases == nil {
d.passphrases = make(map[string]string)
}
d.passphrases[registrationRequest.WorkloadID] = registrationRequest.Passphrase
d.passphrasesLock.Unlock()
// return the predetermined status
status := d.status
if status == 0 {
status = http.StatusOK
}
rw.WriteHeader(status)
return
}
// no such handler
rw.WriteHeader(http.StatusInternalServerError)
}
func TestCWConvertImage(t *testing.T) {
ctx := context.TODO()
systemContext := &types.SystemContext{}
for _, status := range []int{http.StatusOK, http.StatusInternalServerError} {
for _, ignoreChainRetrievalErrors := range []bool{false, true} {
for _, ignoreAttestationErrors := range []bool{false, true} {
t.Run(fmt.Sprintf("status=%d,ignoreChainRetrievalErrors=%v,ignoreAttestationErrors=%v", status, ignoreChainRetrievalErrors, ignoreAttestationErrors), func(t *testing.T) {
// create a per-test Store object
storeOptions := storage.StoreOptions{
GraphRoot: t.TempDir(),
RunRoot: t.TempDir(),
GraphDriverName: "vfs",
}
store, err := storage.GetStore(storeOptions)
require.NoError(t, err)
t.Cleanup(func() {
if _, err := store.Shutdown(true); err != nil {
t.Logf("store.Shutdown(%q): %v", t.Name(), err)
}
})
// listen on a system-assigned port
listener, err := net.Listen("tcp", ":0")
require.NoError(t, err)
// keep track of our listener address
addr := listener.Addr()
// serve requests on that listener
handler := &dummyAttestationHandler{t: t, status: status}
server := http.Server{
Handler: handler,
}
go func() {
if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
t.Logf("serve: %v", err)
}
}()
// clean up at the end of this test
t.Cleanup(func() { assert.NoError(t, server.Close()) })
// convert an image
options := CWConvertImageOptions{
InputImage: "docker.io/library/busybox",
Tag: "localhost/busybox:encrypted",
AttestationURL: "http://" + addr.String(),
IgnoreAttestationErrors: ignoreAttestationErrors,
Slop: "16MB",
}
id, _, _, err := CWConvertImage(ctx, systemContext, store, options)
if status != http.StatusOK && !ignoreAttestationErrors {
assert.Error(t, err)
return
}
if ignoreChainRetrievalErrors && ignoreAttestationErrors {
assert.NoError(t, err)
}
if err != nil {
t.Skipf("%s: %v", t.Name(), err)
return
}
// mount the image
path, err := store.MountImage(id, nil, "")
require.NoError(t, err)
t.Cleanup(func() {
if _, err := store.UnmountImage(id, true); err != nil {
t.Logf("store.UnmountImage(%q): %v", t.Name(), err)
}
})
// check that the image's contents look like what we expect: disk
disk := filepath.Join(path, "disk.img")
require.FileExists(t, disk)
workloadConfig, err := mkcw.ReadWorkloadConfigFromImage(disk)
require.NoError(t, err)
handler.passphrasesLock.Lock()
decryptionPassphrase := handler.passphrases[workloadConfig.WorkloadID]
handler.passphrasesLock.Unlock()
err = mkcw.CheckLUKSPassphrase(disk, decryptionPassphrase)
assert.NoError(t, err)
// check that the image's contents look like what we expect: config file
config := filepath.Join(path, "krun-sev.json")
require.FileExists(t, config)
workloadConfigBytes, err := os.ReadFile(config)
require.NoError(t, err)
var workloadConfigTwo mkcwtypes.WorkloadConfig
err = json.Unmarshal(workloadConfigBytes, &workloadConfigTwo)
require.NoError(t, err)
assert.Equal(t, workloadConfig, workloadConfigTwo)
// check that the image's contents look like what we expect: an executable entry point
entrypoint := filepath.Join(path, "entrypoint")
require.FileExists(t, entrypoint)
st, err := os.Stat(entrypoint)
require.NoError(t, err)
assert.Equal(t, st.Mode().Type(), os.FileMode(0)) // regular file
})
}
}
}
}

View File

@ -163,6 +163,10 @@ type BuildOptions struct {
// It allows end user to export recently built rootfs into a directory or tar. // It allows end user to export recently built rootfs into a directory or tar.
// See the documentation of 'buildah build --output' for the details of the format. // See the documentation of 'buildah build --output' for the details of the format.
BuildOutput string BuildOutput string
// ConfidentialWorkload controls whether or not, and if so, how, we produce an
// image that's meant to be run using krun as a VM instead of a conventional
// process-type container.
ConfidentialWorkload ConfidentialWorkloadOptions
// Additional tags to add to the image that we write, if we know of a // Additional tags to add to the image that we write, if we know of a
// way to add them. // way to add them.
AdditionalTags []string AdditionalTags []string

View File

@ -47,8 +47,16 @@ const (
OCI = "oci" OCI = "oci"
// DOCKER used to define the "docker" image format // DOCKER used to define the "docker" image format
DOCKER = "docker" DOCKER = "docker"
// SEV is a known trusted execution environment type: AMD-SEV (secure encrypted virtualization using encrypted state, requires epyc 1000 "naples")
SEV TeeType = "sev"
// SNP is a known trusted execution environment type: AMD-SNP (SEV secure nested pages) (requires epyc 3000 "milan")
SNP TeeType = "snp"
) )
// TeeType is a supported trusted execution environment type.
type TeeType string
var ( var (
// DefaultCapabilities is the list of capabilities which we grant by // DefaultCapabilities is the list of capabilities which we grant by
// default to containers which are running under UID 0. // default to containers which are running under UID 0.
@ -105,6 +113,23 @@ type BuildOutputOption struct {
IsStdout bool IsStdout bool
} }
// ConfidentialWorkloadOptions encapsulates options which control whether or not
// we output an image whose rootfs contains a LUKS-compatibly-encrypted disk image
// instead of the usual rootfs contents.
type ConfidentialWorkloadOptions struct {
Convert bool
AttestationURL string
CPUs int
Memory int
TempDir string
TeeType TeeType
IgnoreAttestationErrors bool
WorkloadID string
DiskEncryptionPassphrase string
Slop string
FirmwareLibrary string
}
// TempDirForURL checks if the passed-in string looks like a URL or -. If it is, // TempDirForURL checks if the passed-in string looks like a URL or -. If it is,
// TempDirForURL creates a temporary directory, arranges for its contents to be // TempDirForURL creates a temporary directory, arranges for its contents to be
// the contents of that URL, and returns the temporary directory's path, along // the contents of that URL, and returns the temporary directory's path, along

2
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/containernetworking/plugins v1.3.0 github.com/containernetworking/plugins v1.3.0
github.com/containers/common v0.55.1-0.20230830075933-12405381ff45 github.com/containers/common v0.55.1-0.20230830075933-12405381ff45
github.com/containers/image/v5 v5.26.1-0.20230807184415-3fb422379cfa github.com/containers/image/v5 v5.26.1-0.20230807184415-3fb422379cfa
github.com/containers/luksy v0.0.0-20230808154129-d2d74a56682f
github.com/containers/ocicrypt v1.1.8 github.com/containers/ocicrypt v1.1.8
github.com/containers/storage v1.49.0 github.com/containers/storage v1.49.0
github.com/cyphar/filepath-securejoin v0.2.4 github.com/cyphar/filepath-securejoin v0.2.4
@ -48,6 +49,7 @@ require (
github.com/Microsoft/hcsshim v0.10.0 // indirect github.com/Microsoft/hcsshim v0.10.0 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/chzyer/readline v1.5.1 // indirect github.com/chzyer/readline v1.5.1 // indirect
github.com/container-orchestrated-devices/container-device-interface v0.6.0 // indirect github.com/container-orchestrated-devices/container-device-interface v0.6.0 // indirect

4
go.sum
View File

@ -19,6 +19,8 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpYk2gxGJnDjsYuboNTcRmbtGKGs=
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
@ -56,6 +58,8 @@ github.com/containers/image/v5 v5.26.1-0.20230807184415-3fb422379cfa h1:wDfVQtc6
github.com/containers/image/v5 v5.26.1-0.20230807184415-3fb422379cfa/go.mod h1:apL4qwq31NV0gsSZQJPxYyTH0yzWavmMCjT8vsQaXSk= github.com/containers/image/v5 v5.26.1-0.20230807184415-3fb422379cfa/go.mod h1:apL4qwq31NV0gsSZQJPxYyTH0yzWavmMCjT8vsQaXSk=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/luksy v0.0.0-20230808154129-d2d74a56682f h1:/HjLNYkVoUJNT4mm2dzGl63x7nD6YHxxI/k1kR0TkzA=
github.com/containers/luksy v0.0.0-20230808154129-d2d74a56682f/go.mod h1:hEjwW0sePqkTahMzbzeDsQEXN2zdF2VAccqSj5vb1NY=
github.com/containers/ocicrypt v1.1.8 h1:saSBF0/8DyPUjzcxMVzL2OBUWCkvRvqIm75pu0ADSZk= github.com/containers/ocicrypt v1.1.8 h1:saSBF0/8DyPUjzcxMVzL2OBUWCkvRvqIm75pu0ADSZk=
github.com/containers/ocicrypt v1.1.8/go.mod h1:jM362hyBtbwLMWzXQZTlkjKGAQf/BN/LFMtH0FIRt34= github.com/containers/ocicrypt v1.1.8/go.mod h1:jM362hyBtbwLMWzXQZTlkjKGAQf/BN/LFMtH0FIRt34=
github.com/containers/storage v1.49.0 h1:7Vqj8OKlwlcWZ4U61+eS2bNwyIG/qXQo/UZVW5kXn74= github.com/containers/storage v1.49.0 h1:7Vqj8OKlwlcWZ4U61+eS2bNwyIG/qXQo/UZVW5kXn74=

View File

@ -16,6 +16,7 @@ import (
"github.com/containers/buildah/copier" "github.com/containers/buildah/copier"
"github.com/containers/buildah/define" "github.com/containers/buildah/define"
"github.com/containers/buildah/docker" "github.com/containers/buildah/docker"
"github.com/containers/buildah/internal/mkcw"
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/image" "github.com/containers/image/v5/image"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
@ -69,6 +70,7 @@ type containerImageRef struct {
annotations map[string]string annotations map[string]string
preferredManifestType string preferredManifestType string
squash bool squash bool
confidentialWorkload ConfidentialWorkloadOptions
omitHistory bool omitHistory bool
emptyLayer bool emptyLayer bool
idMappingOptions *define.IDMappingOptions idMappingOptions *define.IDMappingOptions
@ -158,6 +160,52 @@ func computeLayerMIMEType(what string, layerCompression archive.Compression) (om
return omediaType, dmediaType, nil return omediaType, dmediaType, nil
} }
// Extract the container's whole filesystem as a filesystem image, wrapped
// in LUKS-compatible encryption.
func (i *containerImageRef) extractConfidentialWorkloadFS(options ConfidentialWorkloadOptions) (io.ReadCloser, error) {
var image v1.Image
if err := json.Unmarshal(i.oconfig, &image); err != nil {
return nil, fmt.Errorf("recreating OCI configuration for %q: %w", i.containerID, err)
}
mountPoint, err := i.store.Mount(i.containerID, i.mountLabel)
if err != nil {
return nil, fmt.Errorf("mounting container %q: %w", i.containerID, err)
}
archiveOptions := mkcw.ArchiveOptions{
AttestationURL: options.AttestationURL,
CPUs: options.CPUs,
Memory: options.Memory,
TempDir: options.TempDir,
TeeType: options.TeeType,
IgnoreAttestationErrors: options.IgnoreAttestationErrors,
WorkloadID: options.WorkloadID,
DiskEncryptionPassphrase: options.DiskEncryptionPassphrase,
Slop: options.Slop,
FirmwareLibrary: options.FirmwareLibrary,
}
rc, _, err := mkcw.Archive(mountPoint, &image, archiveOptions)
if err != nil {
if _, err2 := i.store.Unmount(i.containerID, false); err2 != nil {
logrus.Debugf("unmounting container %q: %v", i.containerID, err2)
}
return nil, fmt.Errorf("converting rootfs %q: %w", i.containerID, err)
}
return ioutils.NewReadCloserWrapper(rc, func() error {
if err = rc.Close(); err != nil {
err = fmt.Errorf("closing tar archive of container %q: %w", i.containerID, err)
}
if _, err2 := i.store.Unmount(i.containerID, false); err == nil {
if err2 != nil {
err2 = fmt.Errorf("unmounting container %q: %w", i.containerID, err2)
}
err = err2
} else {
logrus.Debugf("unmounting container %q: %v", i.containerID, err2)
}
return err
}), nil
}
// Extract the container's whole filesystem as if it were a single layer. // Extract the container's whole filesystem as if it were a single layer.
// Takes ExtractRootfsOptions as argument which allows caller to configure // Takes ExtractRootfsOptions as argument which allows caller to configure
// preserve nature of setuid,setgid,sticky and extended attributes // preserve nature of setuid,setgid,sticky and extended attributes
@ -221,7 +269,7 @@ func (i *containerImageRef) createConfigsAndManifests() (v1.Image, v1.Manifest,
oimage.RootFS.DiffIDs = []digest.Digest{} oimage.RootFS.DiffIDs = []digest.Digest{}
// Only clear the history if we're squashing, otherwise leave it be so that we can append // Only clear the history if we're squashing, otherwise leave it be so that we can append
// entries to it. // entries to it.
if i.squash || i.omitHistory { if i.confidentialWorkload.Convert || i.squash || i.omitHistory {
oimage.History = []v1.History{} oimage.History = []v1.History{}
} }
@ -237,6 +285,24 @@ func (i *containerImageRef) createConfigsAndManifests() (v1.Image, v1.Manifest,
} }
// Always replace this value, since we're newer than our base image. // Always replace this value, since we're newer than our base image.
dimage.Created = created dimage.Created = created
// If we're producing a confidential workload, override the command and
// assorted other settings that aren't expected to work correctly.
if i.confidentialWorkload.Convert {
dimage.Config.Entrypoint = []string{"/entrypoint"}
oimage.Config.Entrypoint = []string{"/entrypoint"}
dimage.Config.Cmd = nil
oimage.Config.Cmd = nil
dimage.Config.User = ""
oimage.Config.User = ""
dimage.Config.WorkingDir = ""
oimage.Config.WorkingDir = ""
dimage.Config.Healthcheck = nil
dimage.Config.Shell = nil
dimage.Config.Volumes = nil
oimage.Config.Volumes = nil
dimage.Config.ExposedPorts = nil
oimage.Config.ExposedPorts = nil
}
// Clear the list of diffIDs, since we always repopulate it. // Clear the list of diffIDs, since we always repopulate it.
dimage.RootFS = &docker.V2S2RootFS{} dimage.RootFS = &docker.V2S2RootFS{}
dimage.RootFS.Type = docker.TypeLayers dimage.RootFS.Type = docker.TypeLayers
@ -244,7 +310,7 @@ func (i *containerImageRef) createConfigsAndManifests() (v1.Image, v1.Manifest,
// Only clear the history if we're squashing, otherwise leave it be so // Only clear the history if we're squashing, otherwise leave it be so
// that we can append entries to it. Clear the parent, too, we no // that we can append entries to it. Clear the parent, too, we no
// longer include its layers and history. // longer include its layers and history.
if i.squash || i.omitHistory { if i.confidentialWorkload.Convert || i.squash || i.omitHistory {
dimage.Parent = "" dimage.Parent = ""
dimage.History = []docker.V2S2History{} dimage.History = []docker.V2S2History{}
} }
@ -296,7 +362,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
for layer != nil { for layer != nil {
layers = append(append([]string{}, layerID), layers...) layers = append(append([]string{}, layerID), layers...)
layerID = layer.Parent layerID = layer.Parent
if layerID == "" || i.squash { if layerID == "" || i.confidentialWorkload.Convert || i.squash {
err = nil err = nil
break break
} }
@ -333,7 +399,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
blobLayers := make(map[digest.Digest]blobLayerInfo) blobLayers := make(map[digest.Digest]blobLayerInfo)
for _, layerID := range layers { for _, layerID := range layers {
what := fmt.Sprintf("layer %q", layerID) what := fmt.Sprintf("layer %q", layerID)
if i.squash { if i.confidentialWorkload.Convert || i.squash {
what = fmt.Sprintf("container %q", i.containerID) what = fmt.Sprintf("container %q", i.containerID)
} }
// The default layer media type assumes no compression. // The default layer media type assumes no compression.
@ -351,7 +417,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
} }
// If we already know the digest of the contents of parent // If we already know the digest of the contents of parent
// layers, reuse their blobsums, diff IDs, and sizes. // layers, reuse their blobsums, diff IDs, and sizes.
if !i.squash && layerID != i.layerID && layer.UncompressedDigest != "" { if !i.confidentialWorkload.Convert && !i.squash && layerID != i.layerID && layer.UncompressedDigest != "" {
layerBlobSum := layer.UncompressedDigest layerBlobSum := layer.UncompressedDigest
layerBlobSize := layer.UncompressedSize layerBlobSize := layer.UncompressedSize
diffID := layer.UncompressedDigest diffID := layer.UncompressedDigest
@ -389,7 +455,13 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
} }
var rc io.ReadCloser var rc io.ReadCloser
var errChan chan error var errChan chan error
if i.squash { if i.confidentialWorkload.Convert {
// Convert the root filesystem into an encrypted disk image.
rc, err = i.extractConfidentialWorkloadFS(i.confidentialWorkload)
if err != nil {
return nil, err
}
} else if i.squash {
// Extract the root filesystem as a single layer. // Extract the root filesystem as a single layer.
rc, errChan, err = i.extractRootfs(ExtractRootfsOptions{}) rc, errChan, err = i.extractRootfs(ExtractRootfsOptions{})
if err != nil { if err != nil {
@ -842,6 +914,7 @@ func (b *Builder) makeContainerImageRef(options CommitOptions) (*containerImageR
annotations: b.Annotations(), annotations: b.Annotations(),
preferredManifestType: manifestType, preferredManifestType: manifestType,
squash: options.Squash, squash: options.Squash,
confidentialWorkload: options.ConfidentialWorkloadOptions,
omitHistory: options.OmitHistory, omitHistory: options.OmitHistory,
emptyLayer: options.EmptyLayer && !options.Squash, emptyLayer: options.EmptyLayer && !options.Squash,
idMappingOptions: &b.IDMappingOptions, idMappingOptions: &b.IDMappingOptions,

View File

@ -148,6 +148,7 @@ type Executor struct {
osVersion string osVersion string
osFeatures []string osFeatures []string
envs []string envs []string
confidentialWorkload define.ConfidentialWorkloadOptions
} }
type imageTypeAndHistoryAndDiffIDs struct { type imageTypeAndHistoryAndDiffIDs struct {
@ -303,6 +304,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
osVersion: options.OSVersion, osVersion: options.OSVersion,
osFeatures: append([]string{}, options.OSFeatures...), osFeatures: append([]string{}, options.OSFeatures...),
envs: append([]string{}, options.Envs...), envs: append([]string{}, options.Envs...),
confidentialWorkload: options.ConfidentialWorkload,
} }
if exec.err == nil { if exec.err == nil {
exec.err = os.Stderr exec.err = os.Stderr

View File

@ -1032,7 +1032,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// squash the contents of the base image. Whichever is // squash the contents of the base image. Whichever is
// the case, we need to commit() to create a new image. // the case, we need to commit() to create a new image.
logCommit(s.output, -1) logCommit(s.output, -1)
if imgID, ref, err = s.commit(ctx, s.getCreatedBy(nil, ""), false, s.output, s.executor.squash); err != nil { if imgID, ref, err = s.commit(ctx, s.getCreatedBy(nil, ""), false, s.output, s.executor.squash, lastStage); err != nil {
return "", nil, fmt.Errorf("committing base container: %w", err) return "", nil, fmt.Errorf("committing base container: %w", err)
} }
// Generate build output if needed. // Generate build output if needed.
@ -1045,7 +1045,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// The image would be modified by the labels passed // The image would be modified by the labels passed
// via the command line, so we need to commit. // via the command line, so we need to commit.
logCommit(s.output, -1) logCommit(s.output, -1)
if imgID, ref, err = s.commit(ctx, s.getCreatedBy(stage.Node, ""), true, s.output, s.executor.squash); err != nil { if imgID, ref, err = s.commit(ctx, s.getCreatedBy(stage.Node, ""), true, s.output, s.executor.squash, lastStage); err != nil {
return "", nil, err return "", nil, err
} }
// Generate build output if needed. // Generate build output if needed.
@ -1193,7 +1193,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// stage. // stage.
if lastStage || imageIsUsedLater { if lastStage || imageIsUsedLater {
logCommit(s.output, i) logCommit(s.output, i)
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), false, s.output, s.executor.squash) imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), false, s.output, s.executor.squash, lastStage && lastInstruction)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("committing container for step %+v: %w", *step, err) return "", nil, fmt.Errorf("committing container for step %+v: %w", *step, err)
} }
@ -1420,7 +1420,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// because at this point we want to save history for // because at this point we want to save history for
// layers even if its a squashed build so that they // layers even if its a squashed build so that they
// can be part of build-cache. // can be part of build-cache.
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, false) imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, false, lastStage && lastInstruction)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("committing container for step %+v: %w", *step, err) return "", nil, fmt.Errorf("committing container for step %+v: %w", *step, err)
} }
@ -1454,7 +1454,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// Create a squashed version of this image // Create a squashed version of this image
// if we're supposed to create one and this // if we're supposed to create one and this
// is the last instruction of the last stage. // is the last instruction of the last stage.
imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, true) imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, true, lastStage && lastInstruction)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("committing final squash step %+v: %w", *step, err) return "", nil, fmt.Errorf("committing final squash step %+v: %w", *step, err)
} }
@ -1941,7 +1941,7 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p
// commit writes the container's contents to an image, using a passed-in tag as // commit writes the container's contents to an image, using a passed-in tag as
// the name if there is one, generating a unique ID-based one otherwise. // the name if there is one, generating a unique ID-based one otherwise.
// or commit via any custom exporter if specified. // or commit via any custom exporter if specified.
func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string, squash bool) (string, reference.Canonical, error) { func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string, squash, finalInstruction bool) (string, reference.Canonical, error) {
ib := s.stage.Builder ib := s.stage.Builder
var imageRef types.ImageReference var imageRef types.ImageReference
if output != "" { if output != "" {
@ -2069,6 +2069,9 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
HistoryTimestamp: s.executor.timestamp, HistoryTimestamp: s.executor.timestamp,
Manifest: s.executor.manifest, Manifest: s.executor.manifest,
} }
if finalInstruction {
options.ConfidentialWorkloadOptions = s.executor.confidentialWorkload
}
imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options) imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options)
if err != nil { if err != nil {
return "", nil, err return "", nil, err

464
internal/mkcw/archive.go Normal file
View File

@ -0,0 +1,464 @@
package mkcw
import (
"archive/tar"
"bytes"
"compress/gzip"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/containers/luksy"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)
const minimumImageSize = 10 * 1024 * 1024
// ArchiveOptions includes optional settings for generating an archive.
type ArchiveOptions struct {
// If supplied, we'll register the workload with this server.
// Practically necessary if DiskEncryptionPassphrase is not set, in
// which case we'll generate one and throw it away after.
AttestationURL string
// Used to measure the environment. If left unset (0, ""), defaults will be applied.
CPUs int
Memory int
// Can be manually set. If left unset ("", false, nil), reasonable values will be used.
TempDir string
TeeType TeeType
IgnoreAttestationErrors bool
ImageSize int64
WorkloadID string
Slop string
DiskEncryptionPassphrase string
FirmwareLibrary string
Logger *logrus.Logger
}
type chainRetrievalError struct {
stderr string
err error
}
func (c chainRetrievalError) Error() string {
if trimmed := strings.TrimSpace(c.stderr); trimmed != "" {
return fmt.Sprintf("retrieving SEV certificate chain: sevctl: %v: %v", strings.TrimSpace(c.stderr), c.err)
}
return fmt.Sprintf("retrieving SEV certificate chain: sevctl: %v", c.err)
}
// Archive generates a WorkloadConfig for a specified directory and produces a
// tar archive of a container image's rootfs with the expected contents.
// The input directory will have a ".krun_config.json" file added to it while
// this function is running, but it will be removed on completion.
func Archive(path string, ociConfig *v1.Image, options ArchiveOptions) (io.ReadCloser, WorkloadConfig, error) {
const (
teeDefaultCPUs = 2
teeDefaultMemory = 512
teeDefaultFilesystem = "ext4"
teeDefaultTeeType = SNP
)
if path == "" {
return nil, WorkloadConfig{}, fmt.Errorf("required path not specified")
}
logger := options.Logger
if logger == nil {
logger = logrus.StandardLogger()
}
teeType := options.TeeType
if teeType == "" {
teeType = teeDefaultTeeType
}
cpus := options.CPUs
if cpus == 0 {
cpus = teeDefaultCPUs
}
memory := options.Memory
if memory == 0 {
memory = teeDefaultMemory
}
filesystem := teeDefaultFilesystem
workloadID := options.WorkloadID
if workloadID == "" {
digestInput := path + filesystem + time.Now().String()
workloadID = digest.Canonical.FromString(digestInput).Encoded()
}
workloadConfig := WorkloadConfig{
Type: teeType,
WorkloadID: workloadID,
CPUs: cpus,
Memory: memory,
AttestationURL: options.AttestationURL,
}
// Do things which are specific to the type of TEE we're building for.
var chainBytes []byte
var chainBytesFile string
var chainInfo fs.FileInfo
switch teeType {
default:
return nil, WorkloadConfig{}, fmt.Errorf("don't know how to generate TeeData for TEE type %q", teeType)
case SEV, SEV_NO_ES:
// If we need a certificate chain, get it.
chain, err := os.CreateTemp(options.TempDir, "chain")
if err != nil {
return nil, WorkloadConfig{}, err
}
chain.Close()
defer func() {
if err := os.Remove(chain.Name()); err != nil {
logger.Warnf("error removing temporary file %q: %v", chain.Name(), err)
}
}()
logrus.Debugf("sevctl export -f %s", chain.Name())
cmd := exec.Command("sevctl", "export", "-f", chain.Name())
var stdout, stderr bytes.Buffer
cmd.Stdout, cmd.Stderr = &stdout, &stderr
if err := cmd.Run(); err != nil {
if !options.IgnoreAttestationErrors {
return nil, WorkloadConfig{}, chainRetrievalError{stderr.String(), err}
}
logger.Warn(chainRetrievalError{stderr.String(), err}.Error())
}
if chainBytes, err = os.ReadFile(chain.Name()); err != nil {
chainBytes = []byte{}
}
var teeData SevWorkloadData
if len(chainBytes) > 0 {
chainBytesFile = "sev.chain"
chainInfo, err = os.Stat(chain.Name())
if err != nil {
return nil, WorkloadConfig{}, err
}
teeData.VendorChain = "/" + chainBytesFile
}
encodedTeeData, err := json.Marshal(teeData)
if err != nil {
return nil, WorkloadConfig{}, fmt.Errorf("encoding tee data: %w", err)
}
workloadConfig.TeeData = string(encodedTeeData)
case SNP:
teeData := SnpWorkloadData{
Generation: "milan",
}
encodedTeeData, err := json.Marshal(teeData)
if err != nil {
return nil, WorkloadConfig{}, fmt.Errorf("encoding tee data: %w", err)
}
workloadConfig.TeeData = string(encodedTeeData)
}
// Write part of the config blob where the krun init process will be
// looking for it. The oci2cw tool used `buildah inspect` output, but
// init is just looking for fields that have the right names in any
// object, and the image's config will have that, so let's try encoding
// it directly.
krunConfigPath := filepath.Join(path, ".krun_config.json")
krunConfigBytes, err := json.Marshal(ociConfig)
if err != nil {
return nil, WorkloadConfig{}, fmt.Errorf("creating .krun_config from image configuration: %w", err)
}
if err := ioutils.AtomicWriteFile(krunConfigPath, krunConfigBytes, 0o600); err != nil {
return nil, WorkloadConfig{}, fmt.Errorf("saving krun config: %w", err)
}
defer func() {
if err := os.Remove(krunConfigPath); err != nil {
logger.Warnf("removing krun configuration file: %v", err)
}
}()
// Encode the workload config, in case it fails for any reason.
cleanedUpWorkloadConfig := workloadConfig
switch cleanedUpWorkloadConfig.Type {
default:
return nil, WorkloadConfig{}, fmt.Errorf("don't know how to canonicalize TEE type %q", cleanedUpWorkloadConfig.Type)
case SEV, SEV_NO_ES:
cleanedUpWorkloadConfig.Type = SEV
case SNP:
cleanedUpWorkloadConfig.Type = SNP
}
workloadConfigBytes, err := json.Marshal(cleanedUpWorkloadConfig)
if err != nil {
return nil, WorkloadConfig{}, err
}
// Make sure we have the passphrase to use for encrypting the disk image.
diskEncryptionPassphrase := options.DiskEncryptionPassphrase
if diskEncryptionPassphrase == "" {
diskEncryptionPassphrase, err = GenerateDiskEncryptionPassphrase()
if err != nil {
return nil, WorkloadConfig{}, err
}
}
// If we weren't told how big the image should be, get a rough estimate
// of the input data size, then add a hedge to it.
imageSize := slop(options.ImageSize, options.Slop)
if imageSize == 0 {
var sourceSize int64
if err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, os.ErrPermission) {
return err
}
info, err := d.Info()
if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, os.ErrPermission) {
return err
}
sourceSize += info.Size()
return nil
}); err != nil {
return nil, WorkloadConfig{}, err
}
imageSize = slop(sourceSize, options.Slop)
}
if imageSize%4096 != 0 {
imageSize += (4096 - (imageSize % 4096))
}
if imageSize < minimumImageSize {
imageSize = minimumImageSize
}
// Create a file to use as the unencrypted version of the disk image.
plain, err := os.CreateTemp(options.TempDir, "plain.img")
if err != nil {
return nil, WorkloadConfig{}, err
}
removePlain := true
defer func() {
if removePlain {
if err := os.Remove(plain.Name()); err != nil {
logger.Warnf("removing temporary file %q: %v", plain.Name(), err)
}
}
}()
// Lengthen the plaintext disk image file.
if err := plain.Truncate(imageSize); err != nil {
plain.Close()
return nil, WorkloadConfig{}, err
}
plainInfo, err := plain.Stat()
plain.Close()
if err != nil {
return nil, WorkloadConfig{}, err
}
// Format the disk image with the filesystem contents.
if _, stderr, err := MakeFS(path, plain.Name(), filesystem); err != nil {
if strings.TrimSpace(stderr) != "" {
return nil, WorkloadConfig{}, fmt.Errorf("%s: %w", strings.TrimSpace(stderr), err)
}
return nil, WorkloadConfig{}, err
}
// If we're registering the workload, we can do that now.
if workloadConfig.AttestationURL != "" {
if err := SendRegistrationRequest(workloadConfig, diskEncryptionPassphrase, options.FirmwareLibrary, options.IgnoreAttestationErrors, logger); err != nil {
return nil, WorkloadConfig{}, err
}
}
// Try to encrypt on the fly.
pipeReader, pipeWriter := io.Pipe()
removePlain = false
go func() {
var err error
defer func() {
if err := os.Remove(plain.Name()); err != nil {
logger.Warnf("removing temporary file %q: %v", plain.Name(), err)
}
if err != nil {
pipeWriter.CloseWithError(err)
} else {
pipeWriter.Close()
}
}()
plain, err := os.Open(plain.Name())
if err != nil {
logrus.Errorf("opening unencrypted disk image %q: %v", plain.Name(), err)
return
}
defer plain.Close()
tw := tar.NewWriter(pipeWriter)
defer tw.Flush()
// Write /entrypoint
var decompressedEntrypoint bytes.Buffer
decompressor, err := gzip.NewReader(bytes.NewReader(entrypointCompressedBytes))
if err != nil {
logrus.Errorf("decompressing copy of entrypoint: %v", err)
return
}
defer decompressor.Close()
if _, err = io.Copy(&decompressedEntrypoint, decompressor); err != nil {
logrus.Errorf("decompressing copy of entrypoint: %v", err)
return
}
entrypointHeader, err := tar.FileInfoHeader(plainInfo, "")
if err != nil {
logrus.Errorf("building header for entrypoint: %v", err)
return
}
entrypointHeader.Name = "entrypoint"
entrypointHeader.Mode = 0o755
entrypointHeader.Uname, entrypointHeader.Gname = "", ""
entrypointHeader.Uid, entrypointHeader.Gid = 0, 0
entrypointHeader.Size = int64(decompressedEntrypoint.Len())
if err = tw.WriteHeader(entrypointHeader); err != nil {
logrus.Errorf("writing header for %q: %v", entrypointHeader.Name, err)
return
}
if _, err = io.Copy(tw, &decompressedEntrypoint); err != nil {
logrus.Errorf("writing %q: %v", entrypointHeader.Name, err)
return
}
// Write /sev.chain
if chainInfo != nil {
chainHeader, err := tar.FileInfoHeader(chainInfo, "")
if err != nil {
logrus.Errorf("building header for %q: %v", chainInfo.Name(), err)
return
}
chainHeader.Name = chainBytesFile
chainHeader.Mode = 0o600
chainHeader.Uname, chainHeader.Gname = "", ""
chainHeader.Uid, chainHeader.Gid = 0, 0
chainHeader.Size = int64(len(chainBytes))
if err = tw.WriteHeader(chainHeader); err != nil {
logrus.Errorf("writing header for %q: %v", chainHeader.Name, err)
return
}
if _, err = tw.Write(chainBytes); err != nil {
logrus.Errorf("writing %q: %v", chainHeader.Name, err)
return
}
}
// Write /krun-sev.json.
workloadConfigHeader, err := tar.FileInfoHeader(plainInfo, "")
if err != nil {
logrus.Errorf("building header for %q: %v", plainInfo.Name(), err)
return
}
workloadConfigHeader.Name = "krun-sev.json"
workloadConfigHeader.Mode = 0o600
workloadConfigHeader.Uname, workloadConfigHeader.Gname = "", ""
workloadConfigHeader.Uid, workloadConfigHeader.Gid = 0, 0
workloadConfigHeader.Size = int64(len(workloadConfigBytes))
if err = tw.WriteHeader(workloadConfigHeader); err != nil {
logrus.Errorf("writing header for %q: %v", workloadConfigHeader.Name, err)
return
}
if _, err = tw.Write(workloadConfigBytes); err != nil {
logrus.Errorf("writing %q: %v", workloadConfigHeader.Name, err)
return
}
// Write /tmp.
tmpHeader, err := tar.FileInfoHeader(plainInfo, "")
if err != nil {
logrus.Errorf("building header for %q: %v", plainInfo.Name(), err)
return
}
tmpHeader.Name = "tmp/"
tmpHeader.Typeflag = tar.TypeDir
tmpHeader.Mode = 0o1777
tmpHeader.Uname, workloadConfigHeader.Gname = "", ""
tmpHeader.Uid, workloadConfigHeader.Gid = 0, 0
tmpHeader.Size = 0
if err = tw.WriteHeader(tmpHeader); err != nil {
logrus.Errorf("writing header for %q: %v", tmpHeader.Name, err)
return
}
// Now figure out the footer that we'll append to the encrypted disk.
var footer bytes.Buffer
lengthBuffer := make([]byte, 8)
footer.Write(workloadConfigBytes)
footer.WriteString("KRUN")
binary.LittleEndian.PutUint64(lengthBuffer, uint64(len(workloadConfigBytes)))
footer.Write(lengthBuffer)
// Start encrypting and write /disk.img.
header, encrypt, blockSize, err := luksy.EncryptV1([]string{diskEncryptionPassphrase}, "")
paddingBoundary := int64(4096)
paddingNeeded := (paddingBoundary - ((int64(len(header)) + imageSize + int64(footer.Len())) % paddingBoundary)) % paddingBoundary
diskHeader := workloadConfigHeader
diskHeader.Name = "disk.img"
diskHeader.Mode = 0o600
diskHeader.Size = int64(len(header)) + imageSize + paddingNeeded + int64(footer.Len())
if err = tw.WriteHeader(diskHeader); err != nil {
logrus.Errorf("writing archive header for disk.img: %v", err)
return
}
if _, err = io.Copy(tw, bytes.NewReader(header)); err != nil {
logrus.Errorf("writing encryption header for disk.img: %v", err)
return
}
encryptWrapper := luksy.EncryptWriter(encrypt, tw, blockSize)
if _, err = io.Copy(encryptWrapper, plain); err != nil {
logrus.Errorf("encrypting disk.img: %v", err)
return
}
encryptWrapper.Close()
if _, err = tw.Write(make([]byte, paddingNeeded)); err != nil {
logrus.Errorf("writing padding for disk.img: %v", err)
return
}
if _, err = io.Copy(tw, &footer); err != nil {
logrus.Errorf("writing footer for disk.img: %v", err)
return
}
tw.Close()
}()
return pipeReader, workloadConfig, nil
}
func slop(size int64, slop string) int64 {
if slop == "" {
return size * 5 / 4
}
for _, factor := range strings.Split(slop, "+") {
factor = strings.TrimSpace(factor)
if factor == "" {
continue
}
if strings.HasSuffix(factor, "%") {
percentage := strings.TrimSuffix(factor, "%")
percent, err := strconv.ParseInt(percentage, 10, 8)
if err != nil {
logrus.Warnf("parsing percentage %q: %v", factor, err)
} else {
size *= (percent + 100)
size /= 100
}
} else {
more, err := units.RAMInBytes(factor)
if err != nil {
logrus.Warnf("parsing %q as a size: %v", factor, err)
} else {
size += more
}
}
}
return size
}

View File

@ -0,0 +1,181 @@
package mkcw
import (
"archive/tar"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"sync"
"testing"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSlop(t *testing.T) {
testCases := []struct {
input int64
slop string
output int64
}{
{100, "", 125},
{100, "10%", 110},
{100, "100%", 200},
{100, "10GB", 10*1024*1024*1024 + 100},
{100, "10%+10GB", 10*1024*1024*1024 + 110},
{100, "10% + 10GB", 10*1024*1024*1024 + 110},
}
for _, testCase := range testCases {
t.Run(testCase.slop, func(t *testing.T) {
assert.Equal(t, testCase.output, slop(testCase.input, testCase.slop))
})
}
}
// dummyAttestationHandler replies with a fixed response code to requests to
// the right path, and caches passphrases indexed by workload ID
type dummyAttestationHandler struct {
t *testing.T
status int
passphrases map[string]string
passphrasesLock sync.Mutex
}
func (d *dummyAttestationHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
var body bytes.Buffer
if req.Body != nil {
if _, err := io.Copy(&body, req.Body); err != nil {
d.t.Logf("reading request body: %v", err)
return
}
req.Body.Close()
}
if req.URL != nil && req.URL.Path == "/kbs/v0/register_workload" {
var registrationRequest RegistrationRequest
// if we can't decode the client request, bail
if err := json.Unmarshal(body.Bytes(), &registrationRequest); err != nil {
rw.WriteHeader(http.StatusInternalServerError)
return
}
// cache the passphrase
d.passphrasesLock.Lock()
if d.passphrases == nil {
d.passphrases = make(map[string]string)
}
d.passphrases[registrationRequest.WorkloadID] = registrationRequest.Passphrase
d.passphrasesLock.Unlock()
// return the predetermined status
status := d.status
if status == 0 {
status = http.StatusOK
}
rw.WriteHeader(status)
return
}
// no such handler
rw.WriteHeader(http.StatusInternalServerError)
}
func TestArchive(t *testing.T) {
ociConfig := &v1.Image{
Config: v1.ImageConfig{
User: "root",
Env: []string{"PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/usr/sbin:/sbin:/usr/sbin:/sbin"},
Cmd: []string{"/bin/bash"},
WorkingDir: "/root",
Labels: map[string]string{
"label_a": "b",
"label_c": "d",
},
},
}
for _, status := range []int{http.StatusOK, http.StatusInternalServerError} {
for _, ignoreChainRetrievalErrors := range []bool{false, true} {
for _, ignoreAttestationErrors := range []bool{false, true} {
t.Run(fmt.Sprintf("status=%d,ignoreChainRetrievalErrors=%v,ignoreAttestationErrors=%v", status, ignoreChainRetrievalErrors, ignoreAttestationErrors), func(t *testing.T) {
// listen on a system-assigned port
listener, err := net.Listen("tcp", ":0")
require.NoError(t, err)
// keep track of our listener address
addr := listener.Addr()
// serve requests on that listener
handler := &dummyAttestationHandler{t: t, status: status}
server := http.Server{
Handler: handler,
}
go func() {
if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
t.Logf("serve: %v", err)
}
}()
// clean up at the end of this test
t.Cleanup(func() { assert.NoError(t, server.Close()) })
// generate the container rootfs using a temporary empty directory
archiveOptions := ArchiveOptions{
CPUs: 4,
Memory: 256,
TempDir: t.TempDir(),
AttestationURL: "http://" + addr.String(),
IgnoreAttestationErrors: ignoreAttestationErrors,
}
inputPath := t.TempDir()
rc, workloadConfig, err := Archive(inputPath, ociConfig, archiveOptions)
// bail now if we got an error we didn't expect
if err != nil {
if errors.As(err, &chainRetrievalError{}) {
if !ignoreChainRetrievalErrors {
return
}
}
if errors.As(err, &attestationError{}) {
if !ignoreAttestationErrors {
require.NoError(t, err)
}
}
return
}
if err == nil {
defer rc.Close()
}
// read each archive entry's contents into a map
contents := make(map[string][]byte)
tr := tar.NewReader(rc)
hdr, err := tr.Next()
for hdr != nil {
contents[hdr.Name], err = io.ReadAll(tr)
require.NoError(t, err)
hdr, err = tr.Next()
}
if err != nil {
require.ErrorIs(t, err, io.EOF)
}
// check that krun-sev.json is a JSON-encoded copy of the workload config
var writtenWorkloadConfig WorkloadConfig
err = json.Unmarshal(contents["krun-sev.json"], &writtenWorkloadConfig)
require.NoError(t, err)
assert.Equal(t, workloadConfig, writtenWorkloadConfig)
// save the disk image to a file
encryptedFile := filepath.Join(t.TempDir(), "encrypted.img")
err = os.WriteFile(encryptedFile, contents["disk.img"], 0o600)
require.NoError(t, err)
// check that we have a configuration footer in there
_, err = ReadWorkloadConfigFromImage(encryptedFile)
require.NoError(t, err)
// check that the attestation server got the encryption passphrase
handler.passphrasesLock.Lock()
passphrase := handler.passphrases[workloadConfig.WorkloadID]
handler.passphrasesLock.Unlock()
err = CheckLUKSPassphrase(encryptedFile, passphrase)
require.NoError(t, err)
})
}
}
}
}

250
internal/mkcw/attest.go Normal file
View File

@ -0,0 +1,250 @@
package mkcw
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"github.com/containers/buildah/internal/mkcw/types"
"github.com/sirupsen/logrus"
)
type (
RegistrationRequest = types.RegistrationRequest
TeeConfig = types.TeeConfig
TeeConfigFlags = types.TeeConfigFlags
TeeConfigMinFW = types.TeeConfigMinFW
)
type measurementError struct {
err error
}
func (m measurementError) Error() string {
return fmt.Sprintf("generating measurement for attestation: %v", m.err)
}
type attestationError struct {
err error
}
func (a attestationError) Error() string {
return fmt.Sprintf("registering workload: %v", a.err)
}
type httpError struct {
statusCode int
}
func (h httpError) Error() string {
if statusText := http.StatusText(h.statusCode); statusText != "" {
return fmt.Sprintf("received server status %d (%q)", h.statusCode, statusText)
}
return fmt.Sprintf("received server status %d", h.statusCode)
}
// SendRegistrationRequest registers a workload with the specified decryption
// passphrase with the service whose location is part of the WorkloadConfig.
func SendRegistrationRequest(workloadConfig WorkloadConfig, diskEncryptionPassphrase, firmwareLibrary string, ignoreAttestationErrors bool, logger *logrus.Logger) error {
if workloadConfig.AttestationURL == "" {
return errors.New("attestation URL not provided")
}
// Measure the execution environment.
measurement, err := GenerateMeasurement(workloadConfig, firmwareLibrary)
if err != nil {
if !ignoreAttestationErrors {
return &measurementError{err}
}
logger.Warnf("generating measurement for attestation: %v", err)
}
// Build the workload registration (attestation) request body.
var teeConfigBytes []byte
switch workloadConfig.Type {
case SEV, SEV_NO_ES, SNP:
var cbits types.TeeConfigFlagBits
switch workloadConfig.Type {
case SEV:
cbits = types.SEV_CONFIG_NO_DEBUG |
types.SEV_CONFIG_NO_KEY_SHARING |
types.SEV_CONFIG_ENCRYPTED_STATE |
types.SEV_CONFIG_NO_SEND |
types.SEV_CONFIG_DOMAIN |
types.SEV_CONFIG_SEV
case SEV_NO_ES:
cbits = types.SEV_CONFIG_NO_DEBUG |
types.SEV_CONFIG_NO_KEY_SHARING |
types.SEV_CONFIG_NO_SEND |
types.SEV_CONFIG_DOMAIN |
types.SEV_CONFIG_SEV
case SNP:
cbits = types.SNP_CONFIG_SMT |
types.SNP_CONFIG_MANDATORY |
types.SNP_CONFIG_MIGRATE_MA |
types.SNP_CONFIG_DEBUG
default:
panic("internal error") // shouldn't happen
}
teeConfig := TeeConfig{
Flags: TeeConfigFlags{
Bits: cbits,
},
MinFW: TeeConfigMinFW{
Major: 0,
Minor: 0,
},
}
teeConfigBytes, err = json.Marshal(teeConfig)
if err != nil {
return err
}
default:
return fmt.Errorf("don't know how to generate tee_config for %q TEEs", workloadConfig.Type)
}
registrationRequest := RegistrationRequest{
WorkloadID: workloadConfig.WorkloadID,
LaunchMeasurement: measurement,
TeeConfig: string(teeConfigBytes),
Passphrase: diskEncryptionPassphrase,
}
registrationRequestBytes, err := json.Marshal(registrationRequest)
if err != nil {
return err
}
// Register the workload.
parsedURL, err := url.Parse(workloadConfig.AttestationURL)
if err != nil {
return err
}
parsedURL.Path = path.Join(parsedURL.Path, "/kbs/v0/register_workload")
if err != nil {
return err
}
url := parsedURL.String()
requestContentType := "application/json"
requestBody := bytes.NewReader(registrationRequestBytes)
defer http.DefaultClient.CloseIdleConnections()
resp, err := http.Post(url, requestContentType, requestBody)
if resp != nil {
if resp.Body != nil {
resp.Body.Close()
}
switch resp.StatusCode {
default:
if !ignoreAttestationErrors {
return &attestationError{&httpError{resp.StatusCode}}
}
logger.Warn(attestationError{&httpError{resp.StatusCode}}.Error())
case http.StatusOK, http.StatusAccepted:
// great!
}
}
if err != nil {
if !ignoreAttestationErrors {
return &attestationError{err}
}
logger.Warn(attestationError{err}.Error())
}
return nil
}
// GenerateMeasurement generates the runtime measurement using the CPU count,
// memory size, and the firmware shared library, whatever it's called, wherever
// it is.
// If firmwareLibrary is a path, it will be the only one checked.
// If firmwareLibrary is a filename, it will be checked for in a hard-coded set
// of directories.
// If firmwareLibrary is empty, both the filename and the directory it is in
// will be taken from a hard-coded set of candidates.
func GenerateMeasurement(workloadConfig WorkloadConfig, firmwareLibrary string) (string, error) {
cpuString := fmt.Sprintf("%d", workloadConfig.CPUs)
memoryString := fmt.Sprintf("%d", workloadConfig.Memory)
var prefix string
switch workloadConfig.Type {
case SEV:
prefix = "SEV-ES"
case SEV_NO_ES:
prefix = "SEV"
case SNP:
prefix = "SNP"
default:
return "", fmt.Errorf("don't know which measurement to use for TEE type %q", workloadConfig.Type)
}
sharedLibraryDirs := []string{
"/usr/local/lib64",
"/usr/local/lib",
"/lib64",
"/lib",
"/usr/lib64",
"/usr/lib",
}
if llp, ok := os.LookupEnv("LD_LIBRARY_PATH"); ok {
sharedLibraryDirs = append(sharedLibraryDirs, strings.Split(llp, ":")...)
}
libkrunfwNames := []string{
"libkrunfw-sev.so.4",
"libkrunfw-sev.so.3",
"libkrunfw-sev.so",
}
var pathsToCheck []string
if firmwareLibrary == "" {
for _, sharedLibraryDir := range sharedLibraryDirs {
if sharedLibraryDir == "" {
continue
}
for _, libkrunfw := range libkrunfwNames {
candidate := filepath.Join(sharedLibraryDir, libkrunfw)
pathsToCheck = append(pathsToCheck, candidate)
}
}
} else {
if filepath.IsAbs(firmwareLibrary) {
pathsToCheck = append(pathsToCheck, firmwareLibrary)
} else {
for _, sharedLibraryDir := range sharedLibraryDirs {
if sharedLibraryDir == "" {
continue
}
candidate := filepath.Join(sharedLibraryDir, firmwareLibrary)
pathsToCheck = append(pathsToCheck, candidate)
}
}
}
for _, candidate := range pathsToCheck {
if _, err := os.Lstat(candidate); err == nil {
var stdout, stderr bytes.Buffer
logrus.Debugf("krunfw_measurement -c %s -m %s %s", cpuString, memoryString, candidate)
cmd := exec.Command("krunfw_measurement", "-c", cpuString, "-m", memoryString, candidate)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
if stderr.Len() > 0 {
err = fmt.Errorf("krunfw_measurement: %s: %w", strings.TrimSpace(stderr.String()), err)
}
return "", err
}
scanner := bufio.NewScanner(&stdout)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, prefix+":") {
return strings.TrimSpace(strings.TrimPrefix(line, prefix+":")), nil
}
}
return "", fmt.Errorf("generating measurement: no line starting with %q found in output from krunfw_measurement", prefix+":")
}
}
return "", fmt.Errorf("generating measurement: none of %v found: %w", pathsToCheck, os.ErrNotExist)
}

BIN
internal/mkcw/embed/entrypoint Executable file

Binary file not shown.

BIN
internal/mkcw/embed/entrypoint.gz Executable file

Binary file not shown.

View File

@ -0,0 +1,16 @@
.section .rodata.1,"aMS",@progbits,1
msg:
.string "This image is designed to be run as a confidential workload using libkrun.\n"
.section .text._start,"ax",@progbits
.globl _start
.type _start,@function
_start:
movq $1, %rax # write
movq $2, %rdi # fd=stderr_fileno
movq $msg, %rsi # message
movq $75, %rdx # length
syscall
movq $60, %rax # exit
movq $1, %rdi # status=1
syscall
.section .note.GNU-stack,"",@progbits

View File

@ -0,0 +1,6 @@
package mkcw
import _ "embed"
//go:embed "embed/entrypoint.gz"
var entrypointCompressedBytes []byte

51
internal/mkcw/luks.go Normal file
View File

@ -0,0 +1,51 @@
package mkcw
import (
"crypto/rand"
"encoding/hex"
"fmt"
"os"
"github.com/containers/luksy"
)
// CheckLUKSPassphrase checks that the specified LUKS-encrypted file can be
// decrypted using the specified passphrase.
func CheckLUKSPassphrase(path, decryptionPassphrase string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
v1header, v2headerA, v2headerB, v2json, err := luksy.ReadHeaders(f, luksy.ReadHeaderOptions{})
if err != nil {
return err
}
if v1header != nil {
_, _, _, _, err = v1header.Decrypt(decryptionPassphrase, f)
return err
}
if v2headerA == nil && v2headerB == nil {
return fmt.Errorf("no LUKS headers read from %q", path)
}
if v2headerA != nil {
if _, _, _, _, err = v2headerA.Decrypt(decryptionPassphrase, f, *v2json); err != nil {
return err
}
}
if v2headerB != nil {
if _, _, _, _, err = v2headerB.Decrypt(decryptionPassphrase, f, *v2json); err != nil {
return err
}
}
return nil
}
// GenerateDiskEncryptionPassphrase generates a random disk encryption password
func GenerateDiskEncryptionPassphrase() (string, error) {
randomizedBytes := make([]byte, 32)
if _, err := rand.Read(randomizedBytes); err != nil {
return "", err
}
return hex.EncodeToString(randomizedBytes), nil
}

View File

@ -0,0 +1,66 @@
package mkcw
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/containers/luksy"
"github.com/stretchr/testify/require"
)
func TestCheckLUKSPassphrase(t *testing.T) {
passphrase, err := GenerateDiskEncryptionPassphrase()
require.NoError(t, err)
secondPassphrase, err := GenerateDiskEncryptionPassphrase()
require.NoError(t, err)
t.Run("v1", func(t *testing.T) {
header, encrypter, blockSize, err := luksy.EncryptV1([]string{secondPassphrase, passphrase}, "")
require.NoError(t, err)
f, err := os.Create(filepath.Join(t.TempDir(), "v1"))
require.NoError(t, err)
n, err := f.Write(header)
require.NoError(t, err)
require.Equal(t, len(header), n)
wrapper := luksy.EncryptWriter(encrypter, f, blockSize)
_, err = wrapper.Write(make([]byte, blockSize*10))
require.NoError(t, err)
wrapper.Close()
f.Close()
err = CheckLUKSPassphrase(f.Name(), passphrase)
require.NoError(t, err)
err = CheckLUKSPassphrase(f.Name(), secondPassphrase)
require.NoError(t, err)
err = CheckLUKSPassphrase(f.Name(), "nope, this is not a correct passphrase")
require.Error(t, err)
})
t.Run("v2", func(t *testing.T) {
for _, sectorSize := range []int{512, 1024, 2048, 4096} {
t.Run(fmt.Sprintf("sectorSize=%d", sectorSize), func(t *testing.T) {
header, encrypter, blockSize, err := luksy.EncryptV2([]string{secondPassphrase, passphrase}, "", sectorSize)
require.NoError(t, err)
f, err := os.Create(filepath.Join(t.TempDir(), "v2"))
require.NoError(t, err)
n, err := f.Write(header)
require.NoError(t, err)
require.Equal(t, len(header), n)
wrapper := luksy.EncryptWriter(encrypter, f, blockSize)
_, err = wrapper.Write(make([]byte, blockSize*10))
require.NoError(t, err)
wrapper.Close()
f.Close()
err = CheckLUKSPassphrase(f.Name(), passphrase)
require.NoError(t, err)
err = CheckLUKSPassphrase(f.Name(), secondPassphrase)
require.NoError(t, err)
err = CheckLUKSPassphrase(f.Name(), "nope, this is not one of the correct passphrases")
require.Error(t, err)
})
}
})
}

38
internal/mkcw/makefs.go Normal file
View File

@ -0,0 +1,38 @@
package mkcw
import (
"fmt"
"os/exec"
"strings"
"github.com/sirupsen/logrus"
)
// MakeFS formats the imageFile as a filesystem of the specified type,
// populating it with the contents of the directory at sourcePath.
// Recognized filesystem types are "ext2", "ext3", "ext4", and "btrfs".
// Note that krun's init is currently hard-wired to assume "ext4".
// Returns the stdout, stderr, and any error returned by the mkfs command.
func MakeFS(sourcePath, imageFile, filesystem string) (string, string, error) {
var stdout, stderr strings.Builder
// N.B. mkfs.xfs can accept a protofile via its -p option, but the
// protofile format doesn't allow us to supply timestamp information or
// specify that files are hard linked
switch filesystem {
case "ext2", "ext3", "ext4":
logrus.Debugf("mkfs -t %s --rootdir %q %q", filesystem, sourcePath, imageFile)
cmd := exec.Command("mkfs", "-t", filesystem, "-d", sourcePath, imageFile)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
return stdout.String(), stderr.String(), err
case "btrfs":
logrus.Debugf("mkfs -t %s --rootdir %q %q", filesystem, sourcePath, imageFile)
cmd := exec.Command("mkfs", "-t", filesystem, "--rootdir", sourcePath, imageFile)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
return stdout.String(), stderr.String(), err
}
return "", "", fmt.Errorf("don't know how to make a %q filesystem with contents", filesystem)
}

View File

@ -0,0 +1,47 @@
package types
// RegistrationRequest is the body of the request which we use for registering
// this confidential workload with the attestation server.
// https://github.com/virtee/reference-kbs/blob/10b2a4c0f8caf78a077210b172863bbae54f66aa/src/main.rs#L83
type RegistrationRequest struct {
WorkloadID string `json:"workload_id"`
LaunchMeasurement string `json:"launch_measurement"`
Passphrase string `json:"passphrase"`
TeeConfig string `json:"tee_config"` // JSON-encoded teeConfig? or specific to the type of TEE?
}
// TeeConfig contains information about a trusted execution environment.
type TeeConfig struct {
Flags TeeConfigFlags `json:"flags"` // runtime requirement bits
MinFW TeeConfigMinFW `json:"minfw"` // minimum platform firmware version
}
// TeeConfigFlags is a bit field containing policy flags specific to the environment.
// https://github.com/virtee/sev/blob/d3e40917fd8531c69f47c2498e9667fe8a5303aa/src/launch/sev.rs#L172
// https://github.com/virtee/sev/blob/d3e40917fd8531c69f47c2498e9667fe8a5303aa/src/launch/snp.rs#L114
type TeeConfigFlags struct {
Bits TeeConfigFlagBits `json:"bits"`
}
// TeeConfigFlagBits are bits representing run-time expectations.
type TeeConfigFlagBits int
const (
SEV_CONFIG_NO_DEBUG TeeConfigFlagBits = 0b00000001 //revive:disable-line:var-naming no debugging of guests
SEV_CONFIG_NO_KEY_SHARING TeeConfigFlagBits = 0b00000010 //revive:disable-line:var-naming no sharing keys between guests
SEV_CONFIG_ENCRYPTED_STATE TeeConfigFlagBits = 0b00000100 //revive:disable-line:var-naming requires SEV-ES
SEV_CONFIG_NO_SEND TeeConfigFlagBits = 0b00001000 //revive:disable-line:var-naming no transferring the guest to another platform
SEV_CONFIG_DOMAIN TeeConfigFlagBits = 0b00010000 //revive:disable-line:var-naming no transferring the guest out of the domain (?)
SEV_CONFIG_SEV TeeConfigFlagBits = 0b00100000 //revive:disable-line:var-naming no transferring the guest to non-SEV platforms
SNP_CONFIG_SMT TeeConfigFlagBits = 0b00000001 //revive:disable-line:var-naming SMT is enabled on the host machine
SNP_CONFIG_MANDATORY TeeConfigFlagBits = 0b00000010 //revive:disable-line:var-naming reserved bit which should always be set
SNP_CONFIG_MIGRATE_MA TeeConfigFlagBits = 0b00000100 //revive:disable-line:var-naming allowed to use a migration agent
SNP_CONFIG_DEBUG TeeConfigFlagBits = 0b00001000 //revive:disable-line:var-naming allow debugging
)
// TeeConfigFlagMinFW corresponds to a minimum version of the kernel+initrd
// combination that should be booted.
type TeeConfigMinFW struct {
Major int `json:"major"`
Minor int `json:"minor"`
}

View File

@ -0,0 +1,34 @@
package types
import "github.com/containers/buildah/define"
// WorkloadConfig is the data type which is encoded and stored in /krun-sev.json in a container
// image, and included directly in the disk image.
// https://github.com/containers/libkrun/blob/57c59dc5359bdeeb8260b3493e9f63d3708f9ab9/src/vmm/src/resources.rs#L57
type WorkloadConfig struct {
Type define.TeeType `json:"tee"`
TeeData string `json:"tee_data"` // Type == SEV: JSON-encoded SevWorkloadData, SNP: JSON-encoded SnpWorkloadData, others?
WorkloadID string `json:"workload_id"`
CPUs int `json:"cpus"`
Memory int `json:"ram_mib"`
AttestationURL string `json:"attestation_url"`
}
// SevWorkloadData contains the path to the SEV certificate chain and optionally,
// the attestation server's public key(?)
// https://github.com/containers/libkrun/blob/d31747aa92cf83df2abaeb87e2a83311c135d003/src/vmm/src/linux/tee/amdsev.rs#L222
type SevWorkloadData struct {
VendorChain string `json:"vendor_chain"`
AttestationServerPubkey string `json:"attestation_server_pubkey"`
}
// SnpWorkloadData contains the required CPU generation name.
// https://github.com/virtee/oci2cw/blob/1502d5be33c2fa82d49aaa95781bbab2aa932781/examples/tee-config-snp.json
type SnpWorkloadData struct {
Generation string `json:"gen"` // "milan" (naples=1, rome=2, milan=3, genoa/bergamo=4)
}
const (
// SEV_NO_ES is a known trusted execution environment type: AMD-SEV (secure encrypted virtualization without encrypted state, requires epyc 1000 "naples")
SEV_NO_ES define.TeeType = "sev_no_es" //revive:disable-line:var-naming
)

223
internal/mkcw/workload.go Normal file
View File

@ -0,0 +1,223 @@
package mkcw
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal/mkcw/types"
)
type (
// WorkloadConfig is the data type which is encoded and stored in an image.
WorkloadConfig = types.WorkloadConfig
// SevWorkloadData is the type of data in WorkloadConfig.TeeData when the type is SEV.
SevWorkloadData = types.SevWorkloadData
// SnpWorkloadData is the type of data in WorkloadConfig.TeeData when the type is SNP.
SnpWorkloadData = types.SnpWorkloadData
// TeeType is one of the known types of trusted execution environments for which we
// can generate suitable image contents.
TeeType = define.TeeType
)
const (
maxWorkloadConfigSize = 1024 * 1024
preferredPaddingBoundary = 4096
// SEV is a known trusted execution environment type: AMD-SEV
SEV = define.SEV
// SEV_NO_ES is a known trusted execution environment type: AMD-SEV without encrypted state
SEV_NO_ES = types.SEV_NO_ES //revive:disable-line:var-naming
// SNP is a known trusted execution environment type: AMD-SNP
SNP = define.SNP
// krun looks for its configuration JSON directly in a disk image if the last twelve bytes
// of the disk image are this magic value followed by a little-endian 64-bit
// length-of-the-configuration
krunMagic = "KRUN"
)
// ReadWorkloadConfigFromImage reads the workload configuration from the
// specified disk image file
func ReadWorkloadConfigFromImage(path string) (WorkloadConfig, error) {
// Read the last 12 bytes, which should be "KRUN" followed by a 64-bit
// little-endian length. The (length) bytes immediately preceding
// these hold the JSON-encoded workloadConfig.
var wc WorkloadConfig
f, err := os.Open(path)
if err != nil {
return wc, err
}
defer f.Close()
// Read those last 12 bytes.
finalTwelve := make([]byte, 12)
if _, err = f.Seek(-12, io.SeekEnd); err != nil {
return wc, fmt.Errorf("checking for workload config signature: %w", err)
}
if n, err := f.Read(finalTwelve); err != nil || n != len(finalTwelve) {
if err != nil && !errors.Is(err, io.EOF) {
return wc, fmt.Errorf("reading workload config signature (%d bytes read): %w", n, err)
}
if n != len(finalTwelve) {
return wc, fmt.Errorf("short read (expected 12 bytes at the end of %q, got %d)", path, n)
}
}
if magic := string(finalTwelve[0:4]); magic != "KRUN" {
return wc, fmt.Errorf("expected magic string KRUN in %q, found %q)", path, magic)
}
length := binary.LittleEndian.Uint64(finalTwelve[4:])
if length > maxWorkloadConfigSize {
return wc, fmt.Errorf("workload config in %q is %d bytes long, which seems unreasonable (max allowed %d)", path, length, maxWorkloadConfigSize)
}
// Read and decode the config.
configBytes := make([]byte, length)
if _, err = f.Seek(-(int64(length) + 12), io.SeekEnd); err != nil {
return wc, fmt.Errorf("looking for workload config from disk image: %w", err)
}
if n, err := f.Read(configBytes); err != nil || n != len(configBytes) {
if err != nil {
return wc, fmt.Errorf("reading workload config from disk image: %w", err)
}
return wc, fmt.Errorf("short read (expected %d bytes near the end of %q, got %d)", len(configBytes), path, n)
}
err = json.Unmarshal(configBytes, &wc)
if err != nil {
err = fmt.Errorf("unmarshaling configuration %q: %w", string(configBytes), err)
}
return wc, err
}
// WriteWorkloadConfigToImage writes the workload configuration to the
// specified disk image file, overwriting a previous configuration if it's
// asked to and it finds one
func WriteWorkloadConfigToImage(imageFile *os.File, workloadConfigBytes []byte, overwrite bool) error {
// Read those last 12 bytes to check if there's a configuration there already, which we should overwrite.
var overwriteOffset int64
if overwrite {
finalTwelve := make([]byte, 12)
if _, err := imageFile.Seek(-12, io.SeekEnd); err != nil {
return fmt.Errorf("checking for workload config signature: %w", err)
}
if n, err := imageFile.Read(finalTwelve); err != nil || n != len(finalTwelve) {
if err != nil && !errors.Is(err, io.EOF) {
return fmt.Errorf("reading workload config signature (%d bytes read): %w", n, err)
}
if n != len(finalTwelve) {
return fmt.Errorf("short read (expected 12 bytes at the end of %q, got %d)", imageFile.Name(), n)
}
}
if magic := string(finalTwelve[0:4]); magic == "KRUN" {
length := binary.LittleEndian.Uint64(finalTwelve[4:])
if length < maxWorkloadConfigSize {
overwriteOffset = int64(length + 12)
}
}
}
// If we found a configuration in the file, try to figure out how much padding was used.
paddingSize := int64(preferredPaddingBoundary)
if overwriteOffset != 0 {
st, err := imageFile.Stat()
if err != nil {
return err
}
for _, possiblePaddingLength := range []int64{0x100000, 0x10000, 0x1000, 0x200, 0x100} {
if overwriteOffset > possiblePaddingLength {
continue
}
if st.Size()%possiblePaddingLength != 0 {
continue
}
if _, err := imageFile.Seek(-possiblePaddingLength, io.SeekEnd); err != nil {
return fmt.Errorf("checking size of padding at end of file: %w", err)
}
buf := make([]byte, possiblePaddingLength)
n, err := imageFile.Read(buf)
if err != nil {
return fmt.Errorf("reading possible padding at end of file: %w", err)
}
if n != len(buf) {
return fmt.Errorf("short read checking size of padding at end of file: %d != %d", n, len(buf))
}
if bytes.Equal(buf[:possiblePaddingLength-overwriteOffset], make([]byte, possiblePaddingLength-overwriteOffset)) {
// everything up to the configuration was zero bytes, so it was padding
overwriteOffset = possiblePaddingLength
paddingSize = possiblePaddingLength
break
}
}
}
// Append the krun configuration to a new buffer.
var formatted bytes.Buffer
nWritten, err := formatted.Write(workloadConfigBytes)
if err != nil {
return fmt.Errorf("building workload config: %w", err)
}
if nWritten != len(workloadConfigBytes) {
return fmt.Errorf("short write appending configuration to buffer: %d != %d", nWritten, len(workloadConfigBytes))
}
// Append the magic string to the buffer.
nWritten, err = formatted.WriteString(krunMagic)
if err != nil {
return fmt.Errorf("building workload config signature: %w", err)
}
if nWritten != len(krunMagic) {
return fmt.Errorf("short write appending krun magic to buffer: %d != %d", nWritten, len(krunMagic))
}
// Append the 64-bit little-endian length of the workload configuration to the buffer.
workloadConfigLengthBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(workloadConfigLengthBytes, uint64(len(workloadConfigBytes)))
nWritten, err = formatted.Write(workloadConfigLengthBytes)
if err != nil {
return fmt.Errorf("building workload config signature size: %w", err)
}
if nWritten != len(workloadConfigLengthBytes) {
return fmt.Errorf("short write appending configuration length to buffer: %d != %d", nWritten, len(workloadConfigLengthBytes))
}
// Build a copy of that data, with padding preceding it.
var padded bytes.Buffer
if int64(formatted.Len())%paddingSize != 0 {
extra := paddingSize - (int64(formatted.Len()) % paddingSize)
nWritten, err := padded.Write(make([]byte, extra))
if err != nil {
return fmt.Errorf("buffering padding: %w", err)
}
if int64(nWritten) != extra {
return fmt.Errorf("short write buffering padding for disk image: %d != %d", nWritten, extra)
}
}
extra := int64(formatted.Len())
nWritten, err = padded.Write(formatted.Bytes())
if err != nil {
return fmt.Errorf("buffering workload config: %w", err)
}
if int64(nWritten) != extra {
return fmt.Errorf("short write buffering workload config: %d != %d", nWritten, extra)
}
// Write the buffer to the file, starting with padding.
if _, err = imageFile.Seek(-overwriteOffset, io.SeekEnd); err != nil {
return fmt.Errorf("preparing to write workload config: %w", err)
}
nWritten, err = imageFile.Write(padded.Bytes())
if err != nil {
return fmt.Errorf("writing workload config: %w", err)
}
if nWritten != padded.Len() {
return fmt.Errorf("short write writing configuration to disk image: %d != %d", nWritten, padded.Len())
}
offset, err := imageFile.Seek(0, io.SeekCurrent)
if err != nil {
return fmt.Errorf("preparing mark end of disk image: %w", err)
}
if err = imageFile.Truncate(offset); err != nil {
return fmt.Errorf("marking end of disk image: %w", err)
}
return nil
}

View File

@ -0,0 +1,62 @@
package mkcw
import (
"crypto/rand"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestReadWriteWorkloadConfig(t *testing.T) {
// Create a temporary file to stand in for a disk image.
temp := filepath.Join(t.TempDir(), "disk.img")
f, err := os.OpenFile(temp, os.O_CREATE|os.O_RDWR, 0o600)
require.NoError(t, err)
err = f.Truncate(0x1000000)
require.NoError(t, err)
defer f.Close()
// Generate a random "encoded workload config".
workloadConfig := make([]byte, 0x100)
n, err := rand.Read(workloadConfig)
require.NoError(t, err)
require.Equal(t, len(workloadConfig), n)
// Read the size of our temporary file.
st, err := f.Stat()
require.NoError(t, err)
originalSize := st.Size()
// Should get an error, since there's no workloadConfig in there to read.
_, err = ReadWorkloadConfigFromImage(f.Name())
require.Error(t, err)
// File should grow, even though we looked for an old config to overwrite.
err = WriteWorkloadConfigToImage(f, workloadConfig, true)
require.NoError(t, err)
st, err = f.Stat()
require.NoError(t, err)
require.Greater(t, st.Size(), originalSize)
originalSize = st.Size()
// File shouldn't grow, even overwriting the config with a slightly larger one.
err = WriteWorkloadConfigToImage(f, append([]byte("slightly longer"), workloadConfig...), true)
require.NoError(t, err)
st, err = f.Stat()
require.NoError(t, err)
require.Equal(t, originalSize, st.Size())
originalSize = st.Size()
// File should grow if we're not trying to replace an old one config with a new one.
err = WriteWorkloadConfigToImage(f, []byte("{\"comment\":\"quite a bit shorter\"}"), false)
require.NoError(t, err)
st, err = f.Stat()
require.NoError(t, err)
require.Greater(t, st.Size(), originalSize)
// Should read successfully.
_, err = ReadWorkloadConfigFromImage(f.Name())
require.NoError(t, err)
}

View File

@ -296,6 +296,13 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
iopts.Quiet = true iopts.Quiet = true
} }
} }
var confidentialWorkloadOptions define.ConfidentialWorkloadOptions
if c.Flag("cw").Changed {
confidentialWorkloadOptions, err = parse.GetConfidentialWorkloadOptions(iopts.CWOptions)
if err != nil {
return options, nil, nil, err
}
}
var cacheTo []reference.Named var cacheTo []reference.Named
var cacheFrom []reference.Named var cacheFrom []reference.Named
cacheTo = nil cacheTo = nil
@ -364,6 +371,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
CacheTTL: cacheTTL, CacheTTL: cacheTTL,
CNIConfigDir: iopts.CNIConfigDir, CNIConfigDir: iopts.CNIConfigDir,
CNIPluginPath: iopts.CNIPlugInPath, CNIPluginPath: iopts.CNIPlugInPath,
ConfidentialWorkload: confidentialWorkloadOptions,
CPPFlags: iopts.CPPFlags, CPPFlags: iopts.CPPFlags,
CommonBuildOpts: commonOpts, CommonBuildOpts: commonOpts,
Compression: compression, Compression: compression,

View File

@ -107,6 +107,7 @@ type BudResults struct {
Envs []string Envs []string
OSFeatures []string OSFeatures []string
OSVersion string OSVersion string
CWOptions string
} }
// FromAndBugResults represents the results for common flags // FromAndBugResults represents the results for common flags
@ -217,6 +218,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.BoolVar(&flags.Compress, "compress", false, "this is a legacy option, which has no effect on the image") fs.BoolVar(&flags.Compress, "compress", false, "this is a legacy option, which has no effect on the image")
fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)") fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)")
fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry") fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
fs.StringVarP(&flags.CWOptions, "cw", "", "", "confidential workload `options`")
fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default") fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default")
fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "this is a Docker specific option and is a NOOP") fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "this is a Docker specific option and is a NOOP")
fs.StringArrayVar(&flags.Envs, "env", []string{}, "set environment variable for the image") fs.StringArrayVar(&flags.Envs, "env", []string{}, "set environment variable for the image")
@ -299,6 +301,7 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions {
flagCompletion["cert-dir"] = commonComp.AutocompleteDefault flagCompletion["cert-dir"] = commonComp.AutocompleteDefault
flagCompletion["cpp-flag"] = commonComp.AutocompleteNone flagCompletion["cpp-flag"] = commonComp.AutocompleteNone
flagCompletion["creds"] = commonComp.AutocompleteNone flagCompletion["creds"] = commonComp.AutocompleteNone
flagCompletion["cw"] = commonComp.AutocompleteNone
flagCompletion["env"] = commonComp.AutocompleteNone flagCompletion["env"] = commonComp.AutocompleteNone
flagCompletion["file"] = commonComp.AutocompleteDefault flagCompletion["file"] = commonComp.AutocompleteDefault
flagCompletion["format"] = commonComp.AutocompleteNone flagCompletion["format"] = commonComp.AutocompleteNone

View File

@ -16,6 +16,7 @@ import (
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containers/buildah/define" "github.com/containers/buildah/define"
mkcwtypes "github.com/containers/buildah/internal/mkcw/types"
internalParse "github.com/containers/buildah/internal/parse" internalParse "github.com/containers/buildah/internal/parse"
internalUtil "github.com/containers/buildah/internal/util" internalUtil "github.com/containers/buildah/internal/util"
"github.com/containers/buildah/pkg/sshagent" "github.com/containers/buildah/pkg/sshagent"
@ -640,6 +641,76 @@ func GetBuildOutput(buildOutput string) (define.BuildOutputOption, error) {
return define.BuildOutputOption{Path: path, IsDir: isDir, IsStdout: isStdout}, nil return define.BuildOutputOption{Path: path, IsDir: isDir, IsStdout: isStdout}, nil
} }
// GetConfidentialWorkloadOptions parses a confidential workload settings
// argument, which controls both whether or not we produce an image that
// expects to be run using krun, and how we handle things like encrypting
// the disk image that the container image will contain.
func GetConfidentialWorkloadOptions(arg string) (define.ConfidentialWorkloadOptions, error) {
options := define.ConfidentialWorkloadOptions{
TempDir: GetTempDir(),
}
defaults := options
for _, option := range strings.Split(arg, ",") {
var err error
switch {
case strings.HasPrefix(option, "type="):
options.TeeType = define.TeeType(strings.ToLower(strings.TrimPrefix(option, "type=")))
switch options.TeeType {
case define.SEV, define.SNP, mkcwtypes.SEV_NO_ES:
default:
return options, fmt.Errorf("parsing type= value %q: unrecognized value", options.TeeType)
}
case strings.HasPrefix(option, "attestation_url="), strings.HasPrefix(option, "attestation-url="):
options.Convert = true
options.AttestationURL = strings.TrimPrefix(option, "attestation_url=")
if options.AttestationURL == option {
options.AttestationURL = strings.TrimPrefix(option, "attestation-url=")
}
case strings.HasPrefix(option, "passphrase="), strings.HasPrefix(option, "passphrase="):
options.Convert = true
options.DiskEncryptionPassphrase = strings.TrimPrefix(option, "passphrase=")
case strings.HasPrefix(option, "workload_id="), strings.HasPrefix(option, "workload-id="):
options.WorkloadID = strings.TrimPrefix(option, "workload_id=")
if options.WorkloadID == option {
options.WorkloadID = strings.TrimPrefix(option, "workload-id=")
}
case strings.HasPrefix(option, "cpus="):
options.CPUs, err = strconv.Atoi(strings.TrimPrefix(option, "cpus="))
if err != nil {
return options, fmt.Errorf("parsing cpus= value %q: %w", strings.TrimPrefix(option, "cpus="), err)
}
case strings.HasPrefix(option, "memory="):
options.Memory, err = strconv.Atoi(strings.TrimPrefix(option, "memory="))
if err != nil {
return options, fmt.Errorf("parsing memory= value %q: %w", strings.TrimPrefix(option, "memorys"), err)
}
case option == "ignore_attestation_errors", option == "ignore-attestation-errors":
options.IgnoreAttestationErrors = true
case strings.HasPrefix(option, "ignore_attestation_errors="), strings.HasPrefix(option, "ignore-attestation-errors="):
val := strings.TrimPrefix(option, "ignore_attestation_errors=")
if val == option {
val = strings.TrimPrefix(option, "ignore-attestation-errors=")
}
options.IgnoreAttestationErrors = val == "true" || val == "yes" || val == "on" || val == "1"
case strings.HasPrefix(option, "firmware-library="), strings.HasPrefix(option, "firmware_library="):
val := strings.TrimPrefix(option, "firmware-library=")
if val == option {
val = strings.TrimPrefix(option, "firmware_library=")
}
options.FirmwareLibrary = val
case strings.HasPrefix(option, "slop="):
options.Slop = strings.TrimPrefix(option, "slop=")
default:
knownOptions := []string{"type", "attestation_url", "passphrase", "workload_id", "cpus", "memory", "firmware_library", "slop"}
return options, fmt.Errorf("expected one or more of %q as arguments for --cw, not %q", knownOptions, option)
}
}
if options != defaults && !options.Convert {
return options, fmt.Errorf("--cw arguments missing one or more of (%q, %q)", "passphrase", "attestation_url")
}
return options, nil
}
// IDMappingOptions parses the build options related to user namespaces and ID mapping. // IDMappingOptions parses the build options related to user namespaces and ID mapping.
func IDMappingOptions(c *cobra.Command, isolation define.Isolation) (usernsOptions define.NamespaceOptions, idmapOptions *define.IDMappingOptions, err error) { func IDMappingOptions(c *cobra.Command, isolation define.Isolation) (usernsOptions define.NamespaceOptions, idmapOptions *define.IDMappingOptions, err error) {
return IDMappingOptionsFromFlagSet(c.Flags(), c.PersistentFlags(), c.Flag) return IDMappingOptionsFromFlagSet(c.Flags(), c.PersistentFlags(), c.Flag)

82
tests/mkcw.bats Normal file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env bats
load helpers
function mkcw_check_image() {
local imageID="$1"
local expectedEnv="$2"
# Mount the container and take a look at what it got from the image.
run_buildah from "$imageID"
ctrID="$output"
run_buildah mount "$ctrID"
mountpoint="$output"
# Should have a /disk.img file.
test -s "$mountpoint"/disk.img
# Should have a krun-sev.json file.
test -s "$mountpoint"/krun-sev.json
# Should have an executable entrypoint binary.
test -s "$mountpoint"/entrypoint
test -x "$mountpoint"/entrypoint
# Should have a sticky /tmp directory.
test -d "$mountpoint"/tmp
test -k "$mountpoint"/tmp
# Decrypt, mount, and take a look around.
uuid=$(cryptsetup luksUUID "$mountpoint"/disk.img)
cryptsetup luksOpen --key-file "$TEST_SCRATCH_DIR"/key "$mountpoint"/disk.img "$uuid"
mkdir -p "$TEST_SCRATCH_DIR"/mount
mount /dev/mapper/"$uuid" "$TEST_SCRATCH_DIR"/mount
# Should have a not-empty config file with parts of an image's config.
test -s "$TEST_SCRATCH_DIR"/mount/.krun_config.json
if test -n "$expectedEnv" ; then
grep -q "expectedEnv" "$TEST_SCRATCH_DIR"/mount/.krun_config.json
fi
# Should have a /tmp directory, at least.
test -d "$TEST_SCRATCH_DIR"/mount/tmp
# Clean up.
umount "$TEST_SCRATCH_DIR"/mount
cryptsetup luksClose "$uuid"
buildah umount "$ctrID"
}
@test "mkcw-convert" {
skip_if_in_container
skip_if_rootless_environment
if ! which cryptsetup > /dev/null 2> /dev/null ; then
skip "cryptsetup not found"
fi
_prefetch busybox
echo -n mkcw-convert > "$TEST_SCRATCH_DIR"/key
run_buildah mkcw --ignore-attestation-errors --passphrase=mkcw-convert busybox busybox-cw
mkcw_check_image busybox-cw
}
@test "mkcw-commit" {
skip_if_in_container
skip_if_rootless_environment
if ! which cryptsetup > /dev/null 2> /dev/null ; then
skip "cryptsetup not found"
fi
_prefetch busybox
echo -n "mkcw commit" > "$TEST_SCRATCH_DIR"/key
run_buildah from busybox
ctrID="$output"
run_buildah commit --iidfile "$TEST_SCRATCH_DIR"/iid --cw type=SEV,ignore_attestation_errors,passphrase="mkcw commit" "$ctrID"
mkcw_check_image $(cat "$TEST_SCRATCH_DIR"/iid)
}
@test "mkcw build" {
skip_if_in_container
skip_if_rootless_environment
if ! which cryptsetup > /dev/null 2> /dev/null ; then
skip "cryptsetup not found"
fi
_prefetch alpine
echo -n "mkcw build" > "$TEST_SCRATCH_DIR"/key
run_buildah build --iidfile "$TEST_SCRATCH_DIR"/iid --cw type=SEV,ignore_attestation_errors,passphrase="mkcw build" -f bud/env/Dockerfile.check-env bud/env
mkcw_check_image $(cat "$TEST_SCRATCH_DIR"/iid)
}

25
vendor/github.com/aead/serpent/.gitignore generated vendored Normal file
View File

@ -0,0 +1,25 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
.vscode
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

21
vendor/github.com/aead/serpent/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Andreas Auernhammer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

9
vendor/github.com/aead/serpent/README.md generated vendored Normal file
View File

@ -0,0 +1,9 @@
[![Godoc Reference](https://godoc.org/github.com/aead/serpent?status.svg)](https://godoc.org/github.com/aead/serpent)
## The Serpent block cipher
Serpent is a symmetric key block cipher that was a finalist in the Advanced Encryption Standard (AES) contest,
where it was ranked second to Rijndael. Serpent was designed by Ross Anderson, Eli Biham, and Lars Knudsen.
### Installation
Install in your GOPATH: `go get -u github.com/aead/serpent`

316
vendor/github.com/aead/serpent/sbox_ref.go generated vendored Normal file
View File

@ -0,0 +1,316 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package serpent
// The linear transformation of serpent
// This version, tries not to minimize the
// number of registers, but maximize parallism.
func linear(v0, v1, v2, v3 *uint32) {
t0 := ((*v0 << 13) | (*v0 >> (32 - 13)))
t2 := ((*v2 << 3) | (*v2 >> (32 - 3)))
t1 := *v1 ^ t0 ^ t2
t3 := *v3 ^ t2 ^ (t0 << 3)
*v1 = (t1 << 1) | (t1 >> (32 - 1))
*v3 = (t3 << 7) | (t3 >> (32 - 7))
t0 ^= *v1 ^ *v3
t2 ^= *v3 ^ (*v1 << 7)
*v0 = (t0 << 5) | (t0 >> (32 - 5))
*v2 = (t2 << 22) | (t2 >> (32 - 22))
}
// The inverse linear transformation of serpent
// This version, tries not to minimize the
// number of registers, but maximize parallism.
func linearInv(v0, v1, v2, v3 *uint32) {
t2 := (*v2 >> 22) | (*v2 << (32 - 22))
t0 := (*v0 >> 5) | (*v0 << (32 - 5))
t2 ^= *v3 ^ (*v1 << 7)
t0 ^= *v1 ^ *v3
t3 := (*v3 >> 7) | (*v3 << (32 - 7))
t1 := (*v1 >> 1) | (*v1 << (32 - 1))
*v3 = t3 ^ t2 ^ (t0 << 3)
*v1 = t1 ^ t0 ^ t2
*v2 = (t2 >> 3) | (t2 << (32 - 3))
*v0 = (t0 >> 13) | (t0 << (32 - 13))
}
// The following functions sb0,sb1, ..., sb7 represent the 8 Serpent S-Boxes.
// sb0Inv til sb7Inv are the inverse functions (e.g. sb0Inv is the Inverse to sb0
// and vice versa).
// The S-Boxes differ from the original Serpent definitions. This is for
// optimisation. The functions use the Serpent S-Box improvements for (non x86)
// from Dr. B. R. Gladman and Sam Simpson.
// S-Box 0
func sb0(r0, r1, r2, r3 *uint32) {
t0 := *r0 ^ *r3
t1 := *r2 ^ t0
t2 := *r1 ^ t1
*r3 = (*r0 & *r3) ^ t2
t3 := *r0 ^ (*r1 & t0)
*r2 = t2 ^ (*r2 | t3)
t4 := *r3 & (t1 ^ t3)
*r1 = (^t1) ^ t4
*r0 = t4 ^ (^t3)
}
// Inverse S-Box 0
func sb0Inv(r0, r1, r2, r3 *uint32) {
t0 := ^(*r0)
t1 := *r0 ^ *r1
t2 := *r3 ^ (t0 | t1)
t3 := *r2 ^ t2
*r2 = t1 ^ t3
t4 := t0 ^ (*r3 & t1)
*r1 = t2 ^ (*r2 & t4)
*r3 = (*r0 & t2) ^ (t3 | *r1)
*r0 = *r3 ^ (t3 ^ t4)
}
// S-Box 1
func sb1(r0, r1, r2, r3 *uint32) {
t0 := *r1 ^ (^(*r0))
t1 := *r2 ^ (*r0 | t0)
*r2 = *r3 ^ t1
t2 := *r1 ^ (*r3 | t0)
t3 := t0 ^ *r2
*r3 = t3 ^ (t1 & t2)
t4 := t1 ^ t2
*r1 = *r3 ^ t4
*r0 = t1 ^ (t3 & t4)
}
// Inverse S-Box 1
func sb1Inv(r0, r1, r2, r3 *uint32) {
t0 := *r1 ^ *r3
t1 := *r0 ^ (*r1 & t0)
t2 := t0 ^ t1
*r3 = *r2 ^ t2
t3 := *r1 ^ (t0 & t1)
t4 := *r3 | t3
*r1 = t1 ^ t4
t5 := ^(*r1)
t6 := *r3 ^ t3
*r0 = t5 ^ t6
*r2 = t2 ^ (t5 | t6)
}
// S-Box 2
func sb2(r0, r1, r2, r3 *uint32) {
v0 := *r0 // save r0
v3 := *r3 // save r3
t0 := ^v0
t1 := *r1 ^ v3
t2 := *r2 & t0
*r0 = t1 ^ t2
t3 := *r2 ^ t0
t4 := *r2 ^ *r0
t5 := *r1 & t4
*r3 = t3 ^ t5
*r2 = v0 ^ ((v3 | t5) & (*r0 | t3))
*r1 = (t1 ^ *r3) ^ (*r2 ^ (v3 | t0))
}
// Inverse S-Box 2
func sb2Inv(r0, r1, r2, r3 *uint32) {
v0 := *r0 // save r0
v3 := *r3 // save r3
t0 := *r1 ^ v3
t1 := ^t0
t2 := v0 ^ *r2
t3 := *r2 ^ t0
t4 := *r1 & t3
*r0 = t2 ^ t4
t5 := v0 | t1
t6 := v3 ^ t5
t7 := t2 | t6
*r3 = t0 ^ t7
t8 := ^t3
t9 := *r0 | *r3
*r1 = t8 ^ t9
*r2 = (v3 & t8) ^ (t2 ^ t9)
}
// S-Box 3
func sb3(r0, r1, r2, r3 *uint32) {
v1 := *r1 // save r1
v3 := *r3 // save r3
t0 := *r0 ^ *r1
t1 := *r0 & *r2
t2 := *r0 | *r3
t3 := *r2 ^ *r3
t4 := t0 & t2
t5 := t1 | t4
*r2 = t3 ^ t5
t6 := *r1 ^ t2
t7 := t5 ^ t6
t8 := t3 & t7
*r0 = t0 ^ t8
t9 := *r2 & *r0
*r1 = t7 ^ t9
*r3 = (v1 | v3) ^ (t3 ^ t9)
}
// Inverse S-Box 3
func sb3Inv(r0, r1, r2, r3 *uint32) {
t0 := *r0 | *r1
t1 := *r1 ^ *r2
t2 := *r1 & t1
t3 := *r0 ^ t2
t4 := *r2 ^ t3
t5 := *r3 | t3
*r0 = t1 ^ t5
t6 := t1 | t5
t7 := *r3 ^ t6
*r2 = t4 ^ t7
t8 := t0 ^ t7
t9 := *r0 & t8
*r3 = t3 ^ t9
*r1 = *r3 ^ (*r0 ^ t8)
}
// S-Box 4
func sb4(r0, r1, r2, r3 *uint32) {
v0 := *r0 // save r0
t0 := v0 ^ *r3
t1 := *r3 & t0
t2 := *r2 ^ t1
t3 := *r1 | t2
*r3 = t0 ^ t3
t4 := ^(*r1)
t5 := t0 | t4
*r0 = t2 ^ t5
t6 := v0 & *r0
t7 := t0 ^ t4
t8 := t3 & t7
*r2 = t6 ^ t8
*r1 = (v0 ^ t2) ^ (t7 & *r2)
}
// Inverse S-Box 4
func sb4Inv(r0, r1, r2, r3 *uint32) {
v3 := *r3 // save r3
t0 := *r2 | v3
t1 := *r0 & t0
t2 := *r1 ^ t1
t3 := *r0 & t2
t4 := *r2 ^ t3
*r1 = v3 ^ t4
t5 := ^(*r0)
t6 := t4 & *r1
*r3 = t2 ^ t6
t7 := *r1 | t5
t8 := v3 ^ t7
*r0 = *r3 ^ t8
*r2 = (t2 & t8) ^ (*r1 ^ t5)
}
// S-Box 5
func sb5(r0, r1, r2, r3 *uint32) {
v1 := *r1 // save r1
t0 := ^(*r0)
t1 := *r0 ^ v1
t2 := *r0 ^ *r3
t3 := *r2 ^ t0
t4 := t1 | t2
*r0 = t3 ^ t4
t5 := *r3 & *r0
t6 := t1 ^ *r0
*r1 = t5 ^ t6
t7 := t0 | *r0
t8 := t1 | t5
t9 := t2 ^ t7
*r2 = t8 ^ t9
*r3 = (v1 ^ t5) ^ (*r1 & t9)
}
// Inverse S-Box 5
func sb5Inv(r0, r1, r2, r3 *uint32) {
v0 := *r0 // save r0
v1 := *r1 // save r1
v3 := *r3 // save r3
t0 := ^(*r2)
t1 := v1 & t0
t2 := v3 ^ t1
t3 := v0 & t2
t4 := v1 ^ t0
*r3 = t3 ^ t4
t5 := v1 | *r3
t6 := v0 & t5
*r1 = t2 ^ t6
t7 := v0 | v3
t8 := t0 ^ t5
*r0 = t7 ^ t8
*r2 = (v1 & t7) ^ (t3 | (v0 ^ *r2))
}
// S-Box 6
func sb6(r0, r1, r2, r3 *uint32) {
t0 := ^(*r0)
t1 := *r0 ^ *r3
t2 := *r1 ^ t1
t3 := t0 | t1
t4 := *r2 ^ t3
*r1 = *r1 ^ t4
t5 := t1 | *r1
t6 := *r3 ^ t5
t7 := t4 & t6
*r2 = t2 ^ t7
t8 := t4 ^ t6
*r0 = *r2 ^ t8
*r3 = (^t4) ^ (t2 & t8)
}
// Inverse S-Box 6
func sb6Inv(r0, r1, r2, r3 *uint32) {
v1 := *r1 // save r1
v3 := *r3 // save r3
t0 := ^(*r0)
t1 := *r0 ^ v1
t2 := *r2 ^ t1
t3 := *r2 | t0
t4 := v3 ^ t3
*r1 = t2 ^ t4
t5 := t2 & t4
t6 := t1 ^ t5
t7 := v1 | t6
*r3 = t4 ^ t7
t8 := v1 | *r3
*r0 = t6 ^ t8
*r2 = (v3 & t0) ^ (t2 ^ t8)
}
// S-Box 7
func sb7(r0, r1, r2, r3 *uint32) {
t0 := *r1 ^ *r2
t1 := *r2 & t0
t2 := *r3 ^ t1
t3 := *r0 ^ t2
t4 := *r3 | t0
t5 := t3 & t4
*r1 = *r1 ^ t5
t6 := t2 | *r1
t7 := *r0 & t3
*r3 = t0 ^ t7
t8 := t3 ^ t6
t9 := *r3 & t8
*r2 = t2 ^ t9
*r0 = (^t8) ^ (*r3 & *r2)
}
// Inverse S-Box 7
func sb7Inv(r0, r1, r2, r3 *uint32) {
v0 := *r0 // save r0
v3 := *r3 // save r3
t0 := *r2 | (v0 & *r1)
t1 := v3 & (v0 | *r1)
*r3 = t0 ^ t1
t2 := ^v3
t3 := *r1 ^ t1
t4 := t3 | (*r3 ^ t2)
*r1 = v0 ^ t4
*r0 = (*r2 ^ t3) ^ (v3 | *r1)
*r2 = (t0 ^ *r1) ^ (*r0 ^ (v0 & *r3))
}

119
vendor/github.com/aead/serpent/serpent.go generated vendored Normal file
View File

@ -0,0 +1,119 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// Package serpent implements the Serpent block cipher
// submitted to the AES challenge. Serpent was designed by
// Ross Anderson, Eli Biham und Lars Knudsen.
// The block cipher takes a 128, 192 or 256 bit key and
// has a block size of 128 bit.
package serpent // import "github.com/aead/serpent"
import (
"crypto/cipher"
"errors"
)
// BlockSize is the serpent block size in bytes.
const BlockSize = 16
const phi = 0x9e3779b9 // The Serpent phi constant (sqrt(5) - 1) * 2**31
var errKeySize = errors.New("invalid key size")
// NewCipher returns a new cipher.Block implementing the serpent block cipher.
// The key argument must be 128, 192 or 256 bit (16, 24, 32 byte).
func NewCipher(key []byte) (cipher.Block, error) {
if k := len(key); k != 16 && k != 24 && k != 32 {
return nil, errKeySize
}
s := &subkeys{}
s.keySchedule(key)
return s, nil
}
// The 132 32 bit subkeys of serpent
type subkeys [132]uint32
func (s *subkeys) BlockSize() int { return BlockSize }
func (s *subkeys) Encrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("src buffer to small")
}
if len(dst) < BlockSize {
panic("dst buffer to small")
}
encryptBlock(dst, src, s)
}
func (s *subkeys) Decrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("src buffer to small")
}
if len(dst) < BlockSize {
panic("dst buffer to small")
}
decryptBlock(dst, src, s)
}
// The key schedule of serpent.
func (s *subkeys) keySchedule(key []byte) {
var k [16]uint32
j := 0
for i := 0; i+4 <= len(key); i += 4 {
k[j] = uint32(key[i]) | uint32(key[i+1])<<8 | uint32(key[i+2])<<16 | uint32(key[i+3])<<24
j++
}
if j < 8 {
k[j] = 1
}
for i := 8; i < 16; i++ {
x := k[i-8] ^ k[i-5] ^ k[i-3] ^ k[i-1] ^ phi ^ uint32(i-8)
k[i] = (x << 11) | (x >> 21)
s[i-8] = k[i]
}
for i := 8; i < 132; i++ {
x := s[i-8] ^ s[i-5] ^ s[i-3] ^ s[i-1] ^ phi ^ uint32(i)
s[i] = (x << 11) | (x >> 21)
}
sb3(&s[0], &s[1], &s[2], &s[3])
sb2(&s[4], &s[5], &s[6], &s[7])
sb1(&s[8], &s[9], &s[10], &s[11])
sb0(&s[12], &s[13], &s[14], &s[15])
sb7(&s[16], &s[17], &s[18], &s[19])
sb6(&s[20], &s[21], &s[22], &s[23])
sb5(&s[24], &s[25], &s[26], &s[27])
sb4(&s[28], &s[29], &s[30], &s[31])
sb3(&s[32], &s[33], &s[34], &s[35])
sb2(&s[36], &s[37], &s[38], &s[39])
sb1(&s[40], &s[41], &s[42], &s[43])
sb0(&s[44], &s[45], &s[46], &s[47])
sb7(&s[48], &s[49], &s[50], &s[51])
sb6(&s[52], &s[53], &s[54], &s[55])
sb5(&s[56], &s[57], &s[58], &s[59])
sb4(&s[60], &s[61], &s[62], &s[63])
sb3(&s[64], &s[65], &s[66], &s[67])
sb2(&s[68], &s[69], &s[70], &s[71])
sb1(&s[72], &s[73], &s[74], &s[75])
sb0(&s[76], &s[77], &s[78], &s[79])
sb7(&s[80], &s[81], &s[82], &s[83])
sb6(&s[84], &s[85], &s[86], &s[87])
sb5(&s[88], &s[89], &s[90], &s[91])
sb4(&s[92], &s[93], &s[94], &s[95])
sb3(&s[96], &s[97], &s[98], &s[99])
sb2(&s[100], &s[101], &s[102], &s[103])
sb1(&s[104], &s[105], &s[106], &s[107])
sb0(&s[108], &s[109], &s[110], &s[111])
sb7(&s[112], &s[113], &s[114], &s[115])
sb6(&s[116], &s[117], &s[118], &s[119])
sb5(&s[120], &s[121], &s[122], &s[123])
sb4(&s[124], &s[125], &s[126], &s[127])
sb3(&s[128], &s[129], &s[130], &s[131])
}

276
vendor/github.com/aead/serpent/serpent_ref.go generated vendored Normal file
View File

@ -0,0 +1,276 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package serpent
// Encrypts one block with the given 132 sub-keys sk.
func encryptBlock(dst, src []byte, sk *subkeys) {
// Transform the input block to 4 x 32 bit registers
r0 := uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
r1 := uint32(src[4]) | uint32(src[5])<<8 | uint32(src[6])<<16 | uint32(src[7])<<24
r2 := uint32(src[8]) | uint32(src[9])<<8 | uint32(src[10])<<16 | uint32(src[11])<<24
r3 := uint32(src[12]) | uint32(src[13])<<8 | uint32(src[14])<<16 | uint32(src[15])<<24
// Encrypt the block with the 132 sub-keys and 8 S-Boxes
r0, r1, r2, r3 = r0^sk[0], r1^sk[1], r2^sk[2], r3^sk[3]
sb0(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[4], r1^sk[5], r2^sk[6], r3^sk[7]
sb1(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[8], r1^sk[9], r2^sk[10], r3^sk[11]
sb2(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[12], r1^sk[13], r2^sk[14], r3^sk[15]
sb3(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[16], r1^sk[17], r2^sk[18], r3^sk[19]
sb4(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[20], r1^sk[21], r2^sk[22], r3^sk[23]
sb5(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[24], r1^sk[25], r2^sk[26], r3^sk[27]
sb6(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[28], r1^sk[29], r2^sk[30], r3^sk[31]
sb7(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[32], r1^sk[33], r2^sk[34], r3^sk[35]
sb0(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[36], r1^sk[37], r2^sk[38], r3^sk[39]
sb1(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[40], r1^sk[41], r2^sk[42], r3^sk[43]
sb2(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[44], r1^sk[45], r2^sk[46], r3^sk[47]
sb3(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[48], r1^sk[49], r2^sk[50], r3^sk[51]
sb4(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[52], r1^sk[53], r2^sk[54], r3^sk[55]
sb5(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[56], r1^sk[57], r2^sk[58], r3^sk[59]
sb6(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[60], r1^sk[61], r2^sk[62], r3^sk[63]
sb7(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[64], r1^sk[65], r2^sk[66], r3^sk[67]
sb0(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[68], r1^sk[69], r2^sk[70], r3^sk[71]
sb1(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[72], r1^sk[73], r2^sk[74], r3^sk[75]
sb2(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[76], r1^sk[77], r2^sk[78], r3^sk[79]
sb3(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[80], r1^sk[81], r2^sk[82], r3^sk[83]
sb4(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[84], r1^sk[85], r2^sk[86], r3^sk[87]
sb5(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[88], r1^sk[89], r2^sk[90], r3^sk[91]
sb6(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[92], r1^sk[93], r2^sk[94], r3^sk[95]
sb7(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[96], r1^sk[97], r2^sk[98], r3^sk[99]
sb0(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[100], r1^sk[101], r2^sk[102], r3^sk[103]
sb1(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[104], r1^sk[105], r2^sk[106], r3^sk[107]
sb2(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[108], r1^sk[109], r2^sk[110], r3^sk[111]
sb3(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[112], r1^sk[113], r2^sk[114], r3^sk[115]
sb4(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[116], r1^sk[117], r2^sk[118], r3^sk[119]
sb5(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[120], r1^sk[121], r2^sk[122], r3^sk[123]
sb6(&r0, &r1, &r2, &r3)
linear(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[124], r1^sk[125], r2^sk[126], r3^sk[127]
sb7(&r0, &r1, &r2, &r3)
// whitening
r0 ^= sk[128]
r1 ^= sk[129]
r2 ^= sk[130]
r3 ^= sk[131]
// write the encrypted block to the output
dst[0] = byte(r0)
dst[1] = byte(r0 >> 8)
dst[2] = byte(r0 >> 16)
dst[3] = byte(r0 >> 24)
dst[4] = byte(r1)
dst[5] = byte(r1 >> 8)
dst[6] = byte(r1 >> 16)
dst[7] = byte(r1 >> 24)
dst[8] = byte(r2)
dst[9] = byte(r2 >> 8)
dst[10] = byte(r2 >> 16)
dst[11] = byte(r2 >> 24)
dst[12] = byte(r3)
dst[13] = byte(r3 >> 8)
dst[14] = byte(r3 >> 16)
dst[15] = byte(r3 >> 24)
}
// Decrypts one block with the given 132 sub-keys sk.
func decryptBlock(dst, src []byte, sk *subkeys) {
// Transform the input block to 4 x 32 bit registers
r0 := uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
r1 := uint32(src[4]) | uint32(src[5])<<8 | uint32(src[6])<<16 | uint32(src[7])<<24
r2 := uint32(src[8]) | uint32(src[9])<<8 | uint32(src[10])<<16 | uint32(src[11])<<24
r3 := uint32(src[12]) | uint32(src[13])<<8 | uint32(src[14])<<16 | uint32(src[15])<<24
// undo whitening
r0 ^= sk[128]
r1 ^= sk[129]
r2 ^= sk[130]
r3 ^= sk[131]
// Decrypt the block with the 132 sub-keys and 8 S-Boxes
sb7Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[124], r1^sk[125], r2^sk[126], r3^sk[127]
linearInv(&r0, &r1, &r2, &r3)
sb6Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[120], r1^sk[121], r2^sk[122], r3^sk[123]
linearInv(&r0, &r1, &r2, &r3)
sb5Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[116], r1^sk[117], r2^sk[118], r3^sk[119]
linearInv(&r0, &r1, &r2, &r3)
sb4Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[112], r1^sk[113], r2^sk[114], r3^sk[115]
linearInv(&r0, &r1, &r2, &r3)
sb3Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[108], r1^sk[109], r2^sk[110], r3^sk[111]
linearInv(&r0, &r1, &r2, &r3)
sb2Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[104], r1^sk[105], r2^sk[106], r3^sk[107]
linearInv(&r0, &r1, &r2, &r3)
sb1Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[100], r1^sk[101], r2^sk[102], r3^sk[103]
linearInv(&r0, &r1, &r2, &r3)
sb0Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[96], r1^sk[97], r2^sk[98], r3^sk[99]
linearInv(&r0, &r1, &r2, &r3)
sb7Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[92], r1^sk[93], r2^sk[94], r3^sk[95]
linearInv(&r0, &r1, &r2, &r3)
sb6Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[88], r1^sk[89], r2^sk[90], r3^sk[91]
linearInv(&r0, &r1, &r2, &r3)
sb5Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[84], r1^sk[85], r2^sk[86], r3^sk[87]
linearInv(&r0, &r1, &r2, &r3)
sb4Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[80], r1^sk[81], r2^sk[82], r3^sk[83]
linearInv(&r0, &r1, &r2, &r3)
sb3Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[76], r1^sk[77], r2^sk[78], r3^sk[79]
linearInv(&r0, &r1, &r2, &r3)
sb2Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[72], r1^sk[73], r2^sk[74], r3^sk[75]
linearInv(&r0, &r1, &r2, &r3)
sb1Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[68], r1^sk[69], r2^sk[70], r3^sk[71]
linearInv(&r0, &r1, &r2, &r3)
sb0Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[64], r1^sk[65], r2^sk[66], r3^sk[67]
linearInv(&r0, &r1, &r2, &r3)
sb7Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[60], r1^sk[61], r2^sk[62], r3^sk[63]
linearInv(&r0, &r1, &r2, &r3)
sb6Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[56], r1^sk[57], r2^sk[58], r3^sk[59]
linearInv(&r0, &r1, &r2, &r3)
sb5Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[52], r1^sk[53], r2^sk[54], r3^sk[55]
linearInv(&r0, &r1, &r2, &r3)
sb4Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[48], r1^sk[49], r2^sk[50], r3^sk[51]
linearInv(&r0, &r1, &r2, &r3)
sb3Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[44], r1^sk[45], r2^sk[46], r3^sk[47]
linearInv(&r0, &r1, &r2, &r3)
sb2Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[40], r1^sk[41], r2^sk[42], r3^sk[43]
linearInv(&r0, &r1, &r2, &r3)
sb1Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[36], r1^sk[37], r2^sk[38], r3^sk[39]
linearInv(&r0, &r1, &r2, &r3)
sb0Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[32], r1^sk[33], r2^sk[34], r3^sk[35]
linearInv(&r0, &r1, &r2, &r3)
sb7Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[28], r1^sk[29], r2^sk[30], r3^sk[31]
linearInv(&r0, &r1, &r2, &r3)
sb6Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[24], r1^sk[25], r2^sk[26], r3^sk[27]
linearInv(&r0, &r1, &r2, &r3)
sb5Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[20], r1^sk[21], r2^sk[22], r3^sk[23]
linearInv(&r0, &r1, &r2, &r3)
sb4Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[16], r1^sk[17], r2^sk[18], r3^sk[19]
linearInv(&r0, &r1, &r2, &r3)
sb3Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[12], r1^sk[13], r2^sk[14], r3^sk[15]
linearInv(&r0, &r1, &r2, &r3)
sb2Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[8], r1^sk[9], r2^sk[10], r3^sk[11]
linearInv(&r0, &r1, &r2, &r3)
sb1Inv(&r0, &r1, &r2, &r3)
r0, r1, r2, r3 = r0^sk[4], r1^sk[5], r2^sk[6], r3^sk[7]
linearInv(&r0, &r1, &r2, &r3)
sb0Inv(&r0, &r1, &r2, &r3)
r0 ^= sk[0]
r1 ^= sk[1]
r2 ^= sk[2]
r3 ^= sk[3]
// write the decrypted block to the output
dst[0] = byte(r0)
dst[1] = byte(r0 >> 8)
dst[2] = byte(r0 >> 16)
dst[3] = byte(r0 >> 24)
dst[4] = byte(r1)
dst[5] = byte(r1 >> 8)
dst[6] = byte(r1 >> 16)
dst[7] = byte(r1 >> 24)
dst[8] = byte(r2)
dst[9] = byte(r2 >> 8)
dst[10] = byte(r2 >> 16)
dst[11] = byte(r2 >> 24)
dst[12] = byte(r3)
dst[13] = byte(r3 >> 8)
dst[14] = byte(r3 >> 16)
dst[15] = byte(r3 >> 24)
}

16
vendor/github.com/containers/luksy/.cirrus.yml generated vendored Normal file
View File

@ -0,0 +1,16 @@
docker_builder:
name: CI
env:
HOME: /root
DEBIAN_FRONTEND: noninteractive
setup_script: |
apt-get -q update
apt-get -q install -y bats cryptsetup golang
go version
make
unit_test_script:
go test -v -cover
defaults_script: |
bats -f defaults ./tests
aes_script: |
bats -f aes ./tests

2
vendor/github.com/containers/luksy/.dockerignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
lukstool
lukstool.test

21
vendor/github.com/containers/luksy/.gitignore generated vendored Normal file
View File

@ -0,0 +1,21 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work

201
vendor/github.com/containers/luksy/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

14
vendor/github.com/containers/luksy/Makefile generated vendored Normal file
View File

@ -0,0 +1,14 @@
GO = go
BATS = bats
all: luksy
luksy: cmd/luksy/*.go *.go
$(GO) build -o luksy ./cmd/luksy
clean:
$(RM) luksy luksy.test
test:
$(GO) test
$(BATS) ./tests

10
vendor/github.com/containers/luksy/README.md generated vendored Normal file
View File

@ -0,0 +1,10 @@
luksy: offline encryption/decryption using LUKS formats [![Cirrus CI Status](https://img.shields.io/cirrus/github/containers/luksy/main)](https://cirrus-ci.com/github/containers/luksy/main)
-
luksy implements encryption and decryption using LUKSv1 and LUKSv2 formats.
Think of it as a clunkier cousin of gzip/bzip2/xz that doesn't actually produce
smaller output than input, but it encrypts, and that's nice.
* The main goal is to be able to encrypt/decrypt when we don't have access to
the Linux device mapper. Duplicating functions of cryptsetup that it can
perform without accessing the Linux device mapper is not a priority.
* If you can use cryptsetup instead, use cryptsetup instead.

244
vendor/github.com/containers/luksy/decrypt.go generated vendored Normal file
View File

@ -0,0 +1,244 @@
package luksy
import (
"bytes"
"errors"
"fmt"
"os"
"strconv"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/pbkdf2"
)
// Decrypt attempts to verify the specified password using information from the
// header and read from the specified file.
//
// Returns a function which will decrypt payload blocks in succession, the size
// of chunks of data that the function expects, the offset in the file where
// the payload begins, and the size of the payload.
func (h V1Header) Decrypt(password string, f *os.File) (func([]byte) ([]byte, error), int, int64, int64, error) {
st, err := f.Stat()
if err != nil {
return nil, -1, -1, -1, err
}
hasher, err := hasherByName(h.HashSpec())
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("unsupported digest algorithm %q: %w", h.HashSpec(), err)
}
activeKeys := 0
for k := 0; k < v1NumKeys; k++ {
keyslot, err := h.KeySlot(k)
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("reading key slot %d: %w", k, err)
}
active, err := keyslot.Active()
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("checking if key slot %d is active: %w", k, err)
}
if !active {
continue
}
activeKeys++
passwordDerived := pbkdf2.Key([]byte(password), keyslot.KeySlotSalt(), int(keyslot.Iterations()), int(h.KeyBytes()), hasher)
striped := make([]byte, h.KeyBytes()*keyslot.Stripes())
n, err := f.ReadAt(striped, int64(keyslot.KeyMaterialOffset())*V1SectorSize)
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("reading diffuse material for keyslot %d: %w", k, err)
}
if n != len(striped) {
return nil, -1, -1, -1, fmt.Errorf("short read while reading diffuse material for keyslot %d: expected %d, got %d", k, len(striped), n)
}
splitKey, err := v1decrypt(h.CipherName(), h.CipherMode(), 0, passwordDerived, striped, V1SectorSize, false)
if err != nil {
fmt.Fprintf(os.Stderr, "error attempting to decrypt main key: %v\n", err)
continue
}
mkCandidate, err := afMerge(splitKey, hasher(), int(h.KeyBytes()), int(keyslot.Stripes()))
if err != nil {
fmt.Fprintf(os.Stderr, "error attempting to compute main key: %v\n", err)
continue
}
mkcandidateDerived := pbkdf2.Key(mkCandidate, h.MKDigestSalt(), int(h.MKDigestIter()), v1DigestSize, hasher)
ivTweak := 0
decryptStream := func(ciphertext []byte) ([]byte, error) {
plaintext, err := v1decrypt(h.CipherName(), h.CipherMode(), ivTweak, mkCandidate, ciphertext, V1SectorSize, false)
ivTweak += len(ciphertext) / V1SectorSize
return plaintext, err
}
if bytes.Equal(mkcandidateDerived, h.MKDigest()) {
payloadOffset := int64(h.PayloadOffset() * V1SectorSize)
return decryptStream, V1SectorSize, payloadOffset, st.Size() - payloadOffset, nil
}
}
if activeKeys == 0 {
return nil, -1, -1, -1, errors.New("no passwords set on LUKS1 volume")
}
return nil, -1, -1, -1, errors.New("decryption error: incorrect password")
}
// Decrypt attempts to verify the specified password using information from the
// header, JSON block, and read from the specified file.
//
// Returns a function which will decrypt payload blocks in succession, the size
// of chunks of data that the function expects, the offset in the file where
// the payload begins, and the size of the payload.
func (h V2Header) Decrypt(password string, f *os.File, j V2JSON) (func([]byte) ([]byte, error), int, int64, int64, error) {
foundDigests := 0
for d, digest := range j.Digests {
if digest.Type != "pbkdf2" {
continue
}
if digest.V2JSONDigestPbkdf2 == nil {
return nil, -1, -1, -1, fmt.Errorf("digest %q is corrupt: no pbkdf2 parameters", d)
}
foundDigests++
if len(digest.Segments) == 0 || len(digest.Digest) == 0 {
continue
}
payloadOffset := int64(-1)
payloadSectorSize := V1SectorSize
payloadEncryption := ""
payloadSize := int64(0)
ivTweak := 0
for _, segmentID := range digest.Segments {
segment, ok := j.Segments[segmentID]
if !ok {
continue // well, that was misleading
}
if segment.Type != "crypt" {
continue
}
tmp, err := strconv.ParseInt(segment.Offset, 10, 64)
if err != nil {
continue
}
payloadOffset = tmp
if segment.Size == "dynamic" {
st, err := f.Stat()
if err != nil {
continue
}
payloadSize = st.Size() - payloadOffset
} else {
payloadSize, err = strconv.ParseInt(segment.Size, 10, 64)
if err != nil {
continue
}
}
payloadSectorSize = segment.SectorSize
payloadEncryption = segment.Encryption
ivTweak = segment.IVTweak
break
}
if payloadEncryption == "" {
continue
}
activeKeys := 0
for k, keyslot := range j.Keyslots {
if keyslot.Priority != nil && *keyslot.Priority == V2JSONKeyslotPriorityIgnore {
continue
}
applicable := true
if len(digest.Keyslots) > 0 {
applicable = false
for i := 0; i < len(digest.Keyslots); i++ {
if k == digest.Keyslots[i] {
applicable = true
break
}
}
}
if !applicable {
continue
}
if keyslot.Type != "luks2" {
continue
}
if keyslot.V2JSONKeyslotLUKS2 == nil {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt", k)
}
if keyslot.V2JSONKeyslotLUKS2.AF.Type != "luks1" {
continue
}
if keyslot.V2JSONKeyslotLUKS2.AF.V2JSONAFLUKS1 == nil {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt: no AF parameters", k)
}
if keyslot.Area.Type != "raw" {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt: key data area is not raw", k)
}
if keyslot.Area.KeySize*V2SectorSize < keyslot.KeySize*keyslot.AF.Stripes {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt: key data area is too small (%d < %d)", k, keyslot.Area.KeySize*V2SectorSize, keyslot.KeySize*keyslot.AF.Stripes)
}
var passwordDerived []byte
switch keyslot.V2JSONKeyslotLUKS2.Kdf.Type {
default:
continue
case "pbkdf2":
if keyslot.V2JSONKeyslotLUKS2.Kdf.V2JSONKdfPbkdf2 == nil {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt: no pbkdf2 parameters", k)
}
hasher, err := hasherByName(keyslot.Kdf.Hash)
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("unsupported digest algorithm %q: %w", keyslot.Kdf.Hash, err)
}
passwordDerived = pbkdf2.Key([]byte(password), keyslot.Kdf.Salt, keyslot.Kdf.Iterations, keyslot.KeySize, hasher)
case "argon2i":
if keyslot.V2JSONKeyslotLUKS2.Kdf.V2JSONKdfArgon2i == nil {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt: no argon2i parameters", k)
}
passwordDerived = argon2.Key([]byte(password), keyslot.Kdf.Salt, uint32(keyslot.Kdf.Time), uint32(keyslot.Kdf.Memory), uint8(keyslot.Kdf.CPUs), uint32(keyslot.KeySize))
case "argon2id":
if keyslot.V2JSONKeyslotLUKS2.Kdf.V2JSONKdfArgon2i == nil {
return nil, -1, -1, -1, fmt.Errorf("key slot %q is corrupt: no argon2id parameters", k)
}
passwordDerived = argon2.IDKey([]byte(password), keyslot.Kdf.Salt, uint32(keyslot.Kdf.Time), uint32(keyslot.Kdf.Memory), uint8(keyslot.Kdf.CPUs), uint32(keyslot.KeySize))
}
striped := make([]byte, keyslot.KeySize*keyslot.AF.Stripes)
n, err := f.ReadAt(striped, int64(keyslot.Area.Offset))
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("reading diffuse material for keyslot %q: %w", k, err)
}
if n != len(striped) {
return nil, -1, -1, -1, fmt.Errorf("short read while reading diffuse material for keyslot %q: expected %d, got %d", k, len(striped), n)
}
splitKey, err := v2decrypt(keyslot.Area.Encryption, 0, passwordDerived, striped, V1SectorSize, false)
if err != nil {
fmt.Fprintf(os.Stderr, "error attempting to decrypt main key: %v\n", err)
continue
}
afhasher, err := hasherByName(keyslot.AF.Hash)
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("unsupported digest algorithm %q: %w", keyslot.AF.Hash, err)
}
mkCandidate, err := afMerge(splitKey, afhasher(), int(keyslot.KeySize), int(keyslot.AF.Stripes))
if err != nil {
fmt.Fprintf(os.Stderr, "error attempting to compute main key: %v\n", err)
continue
}
digester, err := hasherByName(digest.Hash)
if err != nil {
return nil, -1, -1, -1, fmt.Errorf("unsupported digest algorithm %q: %w", digest.Hash, err)
}
mkcandidateDerived := pbkdf2.Key(mkCandidate, digest.Salt, digest.Iterations, len(digest.Digest), digester)
decryptStream := func(ciphertext []byte) ([]byte, error) {
plaintext, err := v2decrypt(payloadEncryption, ivTweak, mkCandidate, ciphertext, payloadSectorSize, true)
ivTweak += len(ciphertext) / payloadSectorSize
return plaintext, err
}
if bytes.Equal(mkcandidateDerived, digest.Digest) {
return decryptStream, payloadSectorSize, payloadOffset, payloadSize, nil
}
activeKeys++
}
if activeKeys == 0 {
return nil, -1, -1, -1, fmt.Errorf("no passwords set on LUKS2 volume for digest %q", d)
}
}
if foundDigests == 0 {
return nil, -1, -1, -1, errors.New("no usable password-verification digests set on LUKS2 volume")
}
return nil, -1, -1, -1, errors.New("decryption error: incorrect password")
}

421
vendor/github.com/containers/luksy/encrypt.go generated vendored Normal file
View File

@ -0,0 +1,421 @@
package luksy
import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"github.com/google/uuid"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/pbkdf2"
)
// EncryptV1 prepares to encrypt data using one or more passwords and the
// specified cipher (or a default, if the specified cipher is "").
//
// Returns a fixed LUKSv1 header which contains keying information, a function
// which will encrypt blocks of data in succession, and the size of chunks of
// data that it expects.
func EncryptV1(password []string, cipher string) ([]byte, func([]byte) ([]byte, error), int, error) {
if len(password) == 0 {
return nil, nil, -1, errors.New("at least one password is required")
}
if len(password) > v1NumKeys {
return nil, nil, -1, fmt.Errorf("attempted to use %d passwords, only %d possible", len(password), v1NumKeys)
}
if cipher == "" {
cipher = "aes-xts-plain64"
}
salt := make([]byte, v1SaltSize)
n, err := rand.Read(salt)
if err != nil {
return nil, nil, -1, fmt.Errorf("reading random data: %w", err)
}
if n != len(salt) {
return nil, nil, -1, errors.New("short read")
}
cipherSpec := strings.SplitN(cipher, "-", 3)
if len(cipherSpec) != 3 || len(cipherSpec[0]) == 0 || len(cipherSpec[1]) == 0 || len(cipherSpec[2]) == 0 {
return nil, nil, -1, fmt.Errorf("invalid cipher %q", cipher)
}
var h V1Header
if err := h.SetMagic(V1Magic); err != nil {
return nil, nil, -1, fmt.Errorf("setting magic to v1: %w", err)
}
if err := h.SetVersion(1); err != nil {
return nil, nil, -1, fmt.Errorf("setting version to 1: %w", err)
}
h.SetCipherName(cipherSpec[0])
h.SetCipherMode(cipherSpec[1] + "-" + cipherSpec[2])
h.SetHashSpec("sha256")
h.SetKeyBytes(32)
if cipherSpec[1] == "xts" {
h.SetKeyBytes(64)
}
h.SetMKDigestSalt(salt)
h.SetMKDigestIter(V1Stripes)
h.SetUUID(uuid.NewString())
mkey := make([]byte, h.KeyBytes())
n, err = rand.Read(mkey)
if err != nil {
return nil, nil, -1, fmt.Errorf("reading random data: %w", err)
}
if n != len(mkey) {
return nil, nil, -1, errors.New("short read")
}
hasher, err := hasherByName(h.HashSpec())
if err != nil {
return nil, nil, -1, errors.New("internal error")
}
mkdigest := pbkdf2.Key(mkey, h.MKDigestSalt(), int(h.MKDigestIter()), v1DigestSize, hasher)
h.SetMKDigest(mkdigest)
headerLength := roundUpToMultiple(v1HeaderStructSize, V1AlignKeyslots)
iterations := IterationsPBKDF2(salt, int(h.KeyBytes()), hasher)
var stripes [][]byte
ksSalt := make([]byte, v1KeySlotSaltLength)
for i := 0; i < v1NumKeys; i++ {
n, err = rand.Read(ksSalt)
if err != nil {
return nil, nil, -1, fmt.Errorf("reading random data: %w", err)
}
if n != len(ksSalt) {
return nil, nil, -1, errors.New("short read")
}
var keyslot V1KeySlot
keyslot.SetActive(i < len(password))
keyslot.SetIterations(uint32(iterations))
keyslot.SetStripes(V1Stripes)
keyslot.SetKeySlotSalt(ksSalt)
if i < len(password) {
splitKey, err := afSplit(mkey, hasher(), int(h.MKDigestIter()))
if err != nil {
return nil, nil, -1, fmt.Errorf("splitting key: %w", err)
}
passwordDerived := pbkdf2.Key([]byte(password[i]), keyslot.KeySlotSalt(), int(keyslot.Iterations()), int(h.KeyBytes()), hasher)
striped, err := v1encrypt(h.CipherName(), h.CipherMode(), 0, passwordDerived, splitKey, V1SectorSize, false)
if err != nil {
return nil, nil, -1, fmt.Errorf("encrypting split key with password: %w", err)
}
if len(striped) != len(mkey)*int(keyslot.Stripes()) {
return nil, nil, -1, fmt.Errorf("internal error: got %d stripe bytes, expected %d", len(striped), len(mkey)*int(keyslot.Stripes()))
}
stripes = append(stripes, striped)
}
keyslot.SetKeyMaterialOffset(uint32(headerLength / V1SectorSize))
if err := h.SetKeySlot(i, keyslot); err != nil {
return nil, nil, -1, fmt.Errorf("internal error: setting value for key slot %d: %w", i, err)
}
headerLength += len(mkey) * int(keyslot.Stripes())
headerLength = roundUpToMultiple(headerLength, V1AlignKeyslots)
}
headerLength = roundUpToMultiple(headerLength, V1SectorSize)
h.SetPayloadOffset(uint32(headerLength / V1SectorSize))
head := make([]byte, headerLength)
offset := copy(head, h[:])
offset = roundUpToMultiple(offset, V1AlignKeyslots)
for _, stripe := range stripes {
copy(head[offset:], stripe)
offset = roundUpToMultiple(offset+len(stripe), V1AlignKeyslots)
}
ivTweak := 0
encryptStream := func(plaintext []byte) ([]byte, error) {
ciphertext, err := v1encrypt(h.CipherName(), h.CipherMode(), ivTweak, mkey, plaintext, V1SectorSize, true)
ivTweak += len(plaintext) / V1SectorSize
return ciphertext, err
}
return head, encryptStream, V1SectorSize, nil
}
// EncryptV2 prepares to encrypt data using one or more passwords and the
// specified cipher (or a default, if the specified cipher is "").
//
// Returns a fixed LUKSv2 header which contains keying information, a
// function which will encrypt blocks of data in succession, and the size of
// chunks of data that it expects.
func EncryptV2(password []string, cipher string, payloadSectorSize int) ([]byte, func([]byte) ([]byte, error), int, error) {
if len(password) == 0 {
return nil, nil, -1, errors.New("at least one password is required")
}
if cipher == "" {
cipher = "aes-xts-plain64"
}
cipherSpec := strings.SplitN(cipher, "-", 3)
if len(cipherSpec) != 3 || len(cipherSpec[0]) == 0 || len(cipherSpec[1]) == 0 || len(cipherSpec[2]) == 0 {
return nil, nil, -1, fmt.Errorf("invalid cipher %q", cipher)
}
if payloadSectorSize == 0 {
payloadSectorSize = V2SectorSize
}
switch payloadSectorSize {
default:
return nil, nil, -1, fmt.Errorf("invalid sector size %d", payloadSectorSize)
case 512, 1024, 2048, 4096:
}
headerSalts := make([]byte, v1SaltSize*3)
n, err := rand.Read(headerSalts)
if err != nil {
return nil, nil, -1, err
}
if n != len(headerSalts) {
return nil, nil, -1, errors.New("short read")
}
hSalt1 := headerSalts[:v1SaltSize]
hSalt2 := headerSalts[v1SaltSize : v1SaltSize*2]
mkeySalt := headerSalts[v1SaltSize*2:]
roundHeaderSize := func(size int) (int, error) {
switch {
case size < 0x4000:
return 0x4000, nil
case size < 0x8000:
return 0x8000, nil
case size < 0x10000:
return 0x10000, nil
case size < 0x20000:
return 0x20000, nil
case size < 0x40000:
return 0x40000, nil
case size < 0x80000:
return 0x80000, nil
case size < 0x100000:
return 0x100000, nil
case size < 0x200000:
return 0x200000, nil
case size < 0x400000:
return 0x400000, nil
}
return 0, fmt.Errorf("internal error: unsupported header size %d", size)
}
var h1, h2 V2Header
if err := h1.SetMagic(V2Magic1); err != nil {
return nil, nil, -1, fmt.Errorf("setting magic to v2: %w", err)
}
if err := h2.SetMagic(V2Magic2); err != nil {
return nil, nil, -1, fmt.Errorf("setting magic to v2: %w", err)
}
if err := h1.SetVersion(2); err != nil {
return nil, nil, -1, fmt.Errorf("setting version to 2: %w", err)
}
if err := h2.SetVersion(2); err != nil {
return nil, nil, -1, fmt.Errorf("setting version to 2: %w", err)
}
h1.SetSequenceID(1)
h2.SetSequenceID(1)
h1.SetLabel("")
h2.SetLabel("")
h1.SetChecksumAlgorithm("sha256")
h2.SetChecksumAlgorithm("sha256")
h1.SetSalt(hSalt1)
h2.SetSalt(hSalt2)
uuidString := uuid.NewString()
h1.SetUUID(uuidString)
h2.SetUUID(uuidString)
h1.SetHeaderOffset(0)
h2.SetHeaderOffset(0)
h1.SetChecksum(nil)
h2.SetChecksum(nil)
mkey := make([]byte, 32)
if cipherSpec[1] == "xts" {
mkey = make([]byte, 64)
}
n, err = rand.Read(mkey)
if err != nil {
return nil, nil, -1, fmt.Errorf("reading random data: %w", err)
}
if n != len(mkey) {
return nil, nil, -1, errors.New("short read")
}
tuningSalt := make([]byte, v1SaltSize)
hasher, err := hasherByName(h1.ChecksumAlgorithm())
if err != nil {
return nil, nil, -1, errors.New("internal error")
}
iterations := IterationsPBKDF2(tuningSalt, len(mkey), hasher)
timeCost := 1
threadsCost := 4
memoryCost := MemoryCostArgon2(tuningSalt, len(mkey), timeCost, threadsCost)
priority := V2JSONKeyslotPriorityNormal
var stripes [][]byte
var keyslots []V2JSONKeyslot
mdigest := pbkdf2.Key(mkey, mkeySalt, iterations, len(hasher().Sum([]byte{})), hasher)
digest0 := V2JSONDigest{
Type: "pbkdf2",
Salt: mkeySalt,
Digest: mdigest,
Segments: []string{"0"},
V2JSONDigestPbkdf2: &V2JSONDigestPbkdf2{
Hash: h1.ChecksumAlgorithm(),
Iterations: iterations,
},
}
for i := range password {
keyslotSalt := make([]byte, v1SaltSize)
n, err := rand.Read(keyslotSalt)
if err != nil {
return nil, nil, -1, err
}
if n != len(keyslotSalt) {
return nil, nil, -1, errors.New("short read")
}
key := argon2.Key([]byte(password[i]), keyslotSalt, uint32(timeCost), uint32(memoryCost), uint8(threadsCost), uint32(len(mkey)))
split, err := afSplit(mkey, hasher(), V2Stripes)
if err != nil {
return nil, nil, -1, fmt.Errorf("splitting: %w", err)
}
striped, err := v2encrypt(cipher, 0, key, split, V1SectorSize, false)
if err != nil {
return nil, nil, -1, fmt.Errorf("encrypting: %w", err)
}
stripes = append(stripes, striped)
keyslot := V2JSONKeyslot{
Type: "luks2",
KeySize: len(mkey),
Area: V2JSONArea{
Type: "raw",
Offset: 10000000, // gets updated later
Size: int64(roundUpToMultiple(len(striped), V2AlignKeyslots)),
V2JSONAreaRaw: &V2JSONAreaRaw{
Encryption: cipher,
KeySize: len(key),
},
},
Priority: &priority,
V2JSONKeyslotLUKS2: &V2JSONKeyslotLUKS2{
AF: V2JSONAF{
Type: "luks1",
V2JSONAFLUKS1: &V2JSONAFLUKS1{
Stripes: V2Stripes,
Hash: h1.ChecksumAlgorithm(),
},
},
Kdf: V2JSONKdf{
Type: "argon2i",
Salt: keyslotSalt,
V2JSONKdfArgon2i: &V2JSONKdfArgon2i{
Time: timeCost,
Memory: memoryCost,
CPUs: threadsCost,
},
},
},
}
keyslots = append(keyslots, keyslot)
digest0.Keyslots = append(digest0.Keyslots, strconv.Itoa(i))
}
segment0 := V2JSONSegment{
Type: "crypt",
Offset: "10000000", // gets updated later
Size: "dynamic",
V2JSONSegmentCrypt: &V2JSONSegmentCrypt{
IVTweak: 0,
Encryption: cipher,
SectorSize: payloadSectorSize,
},
}
j := V2JSON{
Config: V2JSONConfig{},
Keyslots: map[string]V2JSONKeyslot{},
Digests: map[string]V2JSONDigest{},
Segments: map[string]V2JSONSegment{},
Tokens: map[string]V2JSONToken{},
}
rebuild:
j.Digests["0"] = digest0
j.Segments["0"] = segment0
encodedJSON, err := json.Marshal(j)
if err != nil {
return nil, nil, -1, err
}
headerPlusPaddedJsonSize, err := roundHeaderSize(int(V2SectorSize) /* binary header */ + len(encodedJSON) + 1)
if err != nil {
return nil, nil, -1, err
}
if j.Config.JsonSize != headerPlusPaddedJsonSize-V2SectorSize {
j.Config.JsonSize = headerPlusPaddedJsonSize - V2SectorSize
goto rebuild
}
if h1.HeaderSize() != uint64(headerPlusPaddedJsonSize) {
h1.SetHeaderSize(uint64(headerPlusPaddedJsonSize))
h2.SetHeaderSize(uint64(headerPlusPaddedJsonSize))
h1.SetHeaderOffset(0)
h2.SetHeaderOffset(uint64(headerPlusPaddedJsonSize))
goto rebuild
}
keyslotsOffset := h2.HeaderOffset() * 2
maxKeys := len(password)
if maxKeys < 64 {
maxKeys = 64
}
for i := 0; i < len(password); i++ {
oldOffset := keyslots[i].Area.Offset
keyslots[i].Area.Offset = int64(keyslotsOffset) + int64(roundUpToMultiple(len(mkey)*V2Stripes, V2AlignKeyslots))*int64(i)
j.Keyslots[strconv.Itoa(i)] = keyslots[i]
if keyslots[i].Area.Offset != oldOffset {
goto rebuild
}
}
keyslotsSize := roundUpToMultiple(len(mkey)*V2Stripes, V2AlignKeyslots) * maxKeys
if j.Config.KeyslotsSize != keyslotsSize {
j.Config.KeyslotsSize = keyslotsSize
goto rebuild
}
segmentOffsetInt := roundUpToMultiple(int(keyslotsOffset)+j.Config.KeyslotsSize, V2SectorSize)
segmentOffset := strconv.Itoa(segmentOffsetInt)
if segment0.Offset != segmentOffset {
segment0.Offset = segmentOffset
goto rebuild
}
d1 := hasher()
h1.SetChecksum(nil)
d1.Write(h1[:])
d1.Write(encodedJSON)
zeropad := make([]byte, headerPlusPaddedJsonSize-len(h1)-len(encodedJSON))
d1.Write(zeropad)
h1.SetChecksum(d1.Sum(nil))
d2 := hasher()
h2.SetChecksum(nil)
d2.Write(h2[:])
d2.Write(encodedJSON)
d1.Write(zeropad)
h2.SetChecksum(d2.Sum(nil))
head := make([]byte, segmentOffsetInt)
copy(head, h1[:])
copy(head[V2SectorSize:], encodedJSON)
copy(head[h2.HeaderOffset():], h2[:])
copy(head[h2.HeaderOffset()+V2SectorSize:], encodedJSON)
for i := 0; i < len(password); i++ {
iAsString := strconv.Itoa(i)
copy(head[j.Keyslots[iAsString].Area.Offset:], stripes[i])
}
ivTweak := 0
encryptStream := func(plaintext []byte) ([]byte, error) {
ciphertext, err := v2encrypt(cipher, ivTweak, mkey, plaintext, payloadSectorSize, true)
ivTweak += len(plaintext) / payloadSectorSize
return ciphertext, err
}
return head, encryptStream, segment0.SectorSize, nil
}

537
vendor/github.com/containers/luksy/encryption.go generated vendored Normal file
View File

@ -0,0 +1,537 @@
package luksy
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
"strings"
"github.com/aead/serpent"
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/ripemd160"
"golang.org/x/crypto/twofish"
"golang.org/x/crypto/xts"
)
func v1encrypt(cipherName, cipherMode string, ivTweak int, key []byte, plaintext []byte, sectorSize int, bulk bool) ([]byte, error) {
var err error
var newBlockCipher func([]byte) (cipher.Block, error)
ciphertext := make([]byte, len(plaintext))
switch cipherName {
case "aes":
newBlockCipher = aes.NewCipher
case "twofish":
newBlockCipher = func(key []byte) (cipher.Block, error) { return twofish.NewCipher(key) }
case "cast5":
newBlockCipher = func(key []byte) (cipher.Block, error) { return cast5.NewCipher(key) }
case "serpent":
newBlockCipher = serpent.NewCipher
default:
return nil, fmt.Errorf("unsupported cipher %s", cipherName)
}
if sectorSize == 0 {
sectorSize = V1SectorSize
}
switch sectorSize {
default:
return nil, fmt.Errorf("invalid sector size %d", sectorSize)
case 512, 1024, 2048, 4096:
}
switch cipherMode {
case "ecb":
cipher, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += cipher.BlockSize() {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
cipher.Encrypt(ciphertext[processed:processed+blockLeft], plaintext[processed:processed+blockLeft])
}
case "cbc-plain":
block, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
ivValue := processed/sectorSize + ivTweak
if bulk { // iv_large_sectors is not being used
ivValue *= sectorSize / V1SectorSize
}
iv0 := make([]byte, block.BlockSize())
binary.LittleEndian.PutUint32(iv0, uint32(ivValue))
cipher := cipher.NewCBCEncrypter(block, iv0)
cipher.CryptBlocks(ciphertext[processed:processed+blockLeft], plaintext[processed:processed+blockLeft])
}
case "cbc-plain64":
block, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
ivValue := processed/sectorSize + ivTweak
if bulk { // iv_large_sectors is not being used
ivValue *= sectorSize / V1SectorSize
}
iv0 := make([]byte, block.BlockSize())
binary.LittleEndian.PutUint64(iv0, uint64(ivValue))
cipher := cipher.NewCBCEncrypter(block, iv0)
cipher.CryptBlocks(ciphertext[processed:processed+blockLeft], plaintext[processed:processed+blockLeft])
}
case "cbc-essiv:sha256":
hasherName := strings.TrimPrefix(cipherMode, "cbc-essiv:")
hasher, err := hasherByName(hasherName)
if err != nil {
return nil, fmt.Errorf("initializing encryption using hash %s: %w", hasherName, err)
}
h := hasher()
h.Write(key)
makeiv, err := newBlockCipher(h.Sum(nil))
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
block, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
ivValue := (processed/sectorSize + ivTweak)
if bulk { // iv_large_sectors is not being used
ivValue *= sectorSize / V1SectorSize
}
plain0 := make([]byte, makeiv.BlockSize())
binary.LittleEndian.PutUint64(plain0, uint64(ivValue))
iv0 := make([]byte, makeiv.BlockSize())
makeiv.Encrypt(iv0, plain0)
cipher := cipher.NewCBCEncrypter(block, iv0)
cipher.CryptBlocks(ciphertext[processed:processed+blockLeft], plaintext[processed:processed+blockLeft])
}
case "xts-plain":
cipher, err := xts.NewCipher(newBlockCipher, key)
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
sector := uint64(processed/sectorSize + ivTweak)
if bulk { // iv_large_sectors is not being used
sector *= uint64(sectorSize / V1SectorSize)
}
sector = sector % 0x100000000
cipher.Encrypt(ciphertext[processed:processed+blockLeft], plaintext[processed:processed+blockLeft], sector)
}
case "xts-plain64":
cipher, err := xts.NewCipher(newBlockCipher, key)
if err != nil {
return nil, fmt.Errorf("initializing encryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
sector := uint64(processed/sectorSize + ivTweak)
if bulk { // iv_large_sectors is not being used
sector *= uint64(sectorSize / V1SectorSize)
}
cipher.Encrypt(ciphertext[processed:processed+blockLeft], plaintext[processed:processed+blockLeft], sector)
}
default:
return nil, fmt.Errorf("unsupported cipher mode %s", cipherMode)
}
if err != nil {
return nil, fmt.Errorf("cipher error: %w", err)
}
return ciphertext, nil
}
func v1decrypt(cipherName, cipherMode string, ivTweak int, key []byte, ciphertext []byte, sectorSize int, bulk bool) ([]byte, error) {
var err error
var newBlockCipher func([]byte) (cipher.Block, error)
plaintext := make([]byte, len(ciphertext))
switch cipherName {
case "aes":
newBlockCipher = aes.NewCipher
case "twofish":
newBlockCipher = func(key []byte) (cipher.Block, error) { return twofish.NewCipher(key) }
case "cast5":
newBlockCipher = func(key []byte) (cipher.Block, error) { return cast5.NewCipher(key) }
case "serpent":
newBlockCipher = serpent.NewCipher
default:
return nil, fmt.Errorf("unsupported cipher %s", cipherName)
}
if sectorSize == 0 {
sectorSize = V1SectorSize
}
switch sectorSize {
default:
return nil, fmt.Errorf("invalid sector size %d", sectorSize)
case 512, 1024, 2048, 4096:
}
switch cipherMode {
case "ecb":
cipher, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
for processed := 0; processed < len(ciphertext); processed += cipher.BlockSize() {
blockLeft := sectorSize
if processed+blockLeft > len(ciphertext) {
blockLeft = len(ciphertext) - processed
}
cipher.Decrypt(plaintext[processed:processed+blockLeft], ciphertext[processed:processed+blockLeft])
}
case "cbc-plain":
block, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
ivValue := processed/sectorSize + ivTweak
if bulk { // iv_large_sectors is not being used
ivValue *= sectorSize / V1SectorSize
}
iv0 := make([]byte, block.BlockSize())
binary.LittleEndian.PutUint32(iv0, uint32(ivValue))
cipher := cipher.NewCBCDecrypter(block, iv0)
cipher.CryptBlocks(plaintext[processed:processed+blockLeft], ciphertext[processed:processed+blockLeft])
}
case "cbc-plain64":
block, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
ivValue := processed/sectorSize + ivTweak
if bulk { // iv_large_sectors is not being used
ivValue *= sectorSize / V1SectorSize
}
iv0 := make([]byte, block.BlockSize())
binary.LittleEndian.PutUint64(iv0, uint64(ivValue))
cipher := cipher.NewCBCDecrypter(block, iv0)
cipher.CryptBlocks(plaintext[processed:processed+blockLeft], ciphertext[processed:processed+blockLeft])
}
case "cbc-essiv:sha256":
hasherName := strings.TrimPrefix(cipherMode, "cbc-essiv:")
hasher, err := hasherByName(hasherName)
if err != nil {
return nil, fmt.Errorf("initializing decryption using hash %s: %w", hasherName, err)
}
h := hasher()
h.Write(key)
makeiv, err := newBlockCipher(h.Sum(nil))
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
block, err := newBlockCipher(key)
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
for processed := 0; processed < len(plaintext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(plaintext) {
blockLeft = len(plaintext) - processed
}
ivValue := (processed/sectorSize + ivTweak)
if bulk { // iv_large_sectors is not being used
ivValue *= sectorSize / V1SectorSize
}
plain0 := make([]byte, makeiv.BlockSize())
binary.LittleEndian.PutUint64(plain0, uint64(ivValue))
iv0 := make([]byte, makeiv.BlockSize())
makeiv.Encrypt(iv0, plain0)
cipher := cipher.NewCBCDecrypter(block, iv0)
cipher.CryptBlocks(plaintext[processed:processed+blockLeft], ciphertext[processed:processed+blockLeft])
}
case "xts-plain":
cipher, err := xts.NewCipher(newBlockCipher, key)
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
for processed := 0; processed < len(ciphertext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(ciphertext) {
blockLeft = len(ciphertext) - processed
}
sector := uint64(processed/sectorSize + ivTweak)
if bulk { // iv_large_sectors is not being used
sector *= uint64(sectorSize / V1SectorSize)
}
sector = sector % 0x100000000
cipher.Decrypt(plaintext[processed:processed+blockLeft], ciphertext[processed:processed+blockLeft], sector)
}
case "xts-plain64":
cipher, err := xts.NewCipher(newBlockCipher, key)
if err != nil {
return nil, fmt.Errorf("initializing decryption: %w", err)
}
for processed := 0; processed < len(ciphertext); processed += sectorSize {
blockLeft := sectorSize
if processed+blockLeft > len(ciphertext) {
blockLeft = len(ciphertext) - processed
}
sector := uint64(processed/sectorSize + ivTweak)
if bulk { // iv_large_sectors is not being used
sector *= uint64(sectorSize / V1SectorSize)
}
cipher.Decrypt(plaintext[processed:processed+blockLeft], ciphertext[processed:processed+blockLeft], sector)
}
default:
return nil, fmt.Errorf("unsupported cipher mode %s", cipherMode)
}
if err != nil {
return nil, fmt.Errorf("cipher error: %w", err)
}
return plaintext, nil
}
func v2encrypt(cipherSuite string, ivTweak int, key []byte, ciphertext []byte, sectorSize int, bulk bool) ([]byte, error) {
var cipherName, cipherMode string
switch {
default:
cipherSpec := strings.SplitN(cipherSuite, "-", 2)
if len(cipherSpec) < 2 {
return nil, fmt.Errorf("unrecognized cipher suite %q", cipherSuite)
}
cipherName = cipherSpec[0]
cipherMode = cipherSpec[1]
}
return v1encrypt(cipherName, cipherMode, ivTweak, key, ciphertext, sectorSize, bulk)
}
func v2decrypt(cipherSuite string, ivTweak int, key []byte, ciphertext []byte, sectorSize int, bulk bool) ([]byte, error) {
var cipherName, cipherMode string
switch {
default:
cipherSpec := strings.SplitN(cipherSuite, "-", 2)
if len(cipherSpec) < 2 {
return nil, fmt.Errorf("unrecognized cipher suite %q", cipherSuite)
}
cipherName = cipherSpec[0]
cipherMode = cipherSpec[1]
}
return v1decrypt(cipherName, cipherMode, ivTweak, key, ciphertext, sectorSize, bulk)
}
func diffuse(key []byte, h hash.Hash) []byte {
sum := make([]byte, len(key))
counter := uint32(0)
for summed := 0; summed < len(key); summed += h.Size() {
h.Reset()
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], counter)
h.Write(buf[:])
needed := len(key) - summed
if needed > h.Size() {
needed = h.Size()
}
h.Write(key[summed : summed+needed])
partial := h.Sum(nil)
copy(sum[summed:summed+needed], partial)
counter++
}
return sum
}
func afMerge(splitKey []byte, h hash.Hash, keysize int, stripes int) ([]byte, error) {
if len(splitKey) != keysize*stripes {
return nil, fmt.Errorf("expected %d af bytes, got %d", keysize*stripes, len(splitKey))
}
d := make([]byte, keysize)
for i := 0; i < stripes-1; i++ {
for j := 0; j < keysize; j++ {
d[j] = d[j] ^ splitKey[i*keysize+j]
}
d = diffuse(d, h)
}
for j := 0; j < keysize; j++ {
d[j] = d[j] ^ splitKey[(stripes-1)*keysize+j]
}
return d, nil
}
func afSplit(key []byte, h hash.Hash, stripes int) ([]byte, error) {
keysize := len(key)
s := make([]byte, keysize*stripes)
d := make([]byte, keysize)
n, err := rand.Read(s[0 : (keysize-1)*stripes])
if err != nil {
return nil, err
}
if n != (keysize-1)*stripes {
return nil, fmt.Errorf("short read when attempting to read random data: %d < %d", n, (keysize-1)*stripes)
}
for i := 0; i < stripes-1; i++ {
for j := 0; j < keysize; j++ {
d[j] = d[j] ^ s[i*keysize+j]
}
d = diffuse(d, h)
}
for j := 0; j < keysize; j++ {
s[(stripes-1)*keysize+j] = d[j] ^ key[j]
}
return s, nil
}
func roundUpToMultiple(i, factor int) int {
if i < 0 {
return 0
}
return i + ((factor - (i % factor)) % factor)
}
func hasherByName(name string) (func() hash.Hash, error) {
switch name {
case "sha1":
return sha1.New, nil
case "sha256":
return sha256.New, nil
case "sha512":
return sha512.New, nil
case "ripemd160":
return ripemd160.New, nil
default:
return nil, fmt.Errorf("unsupported digest algorithm %q", name)
}
}
type wrapper struct {
fn func(plaintext []byte) ([]byte, error)
blockSize int
buf []byte
buffered, consumed int
reader io.Reader
eof bool
writer io.Writer
}
func (w *wrapper) Write(buf []byte) (int, error) {
n := 0
for n < len(buf) {
nBuffered := copy(w.buf[w.buffered:], buf[n:])
w.buffered += nBuffered
n += nBuffered
if w.buffered == len(w.buf) {
processed, err := w.fn(w.buf)
if err != nil {
return n, err
}
nWritten, err := w.writer.Write(processed)
if err != nil {
return n, err
}
w.buffered -= nWritten
if nWritten != len(processed) {
return n, fmt.Errorf("short write: %d != %d", nWritten, len(processed))
}
}
}
return n, nil
}
func (w *wrapper) Read(buf []byte) (int, error) {
n := 0
for n < len(buf) {
nRead := copy(buf[n:], w.buf[w.consumed:])
w.consumed += nRead
n += nRead
if w.consumed == len(w.buf) && !w.eof {
nRead, err := w.reader.Read(w.buf)
w.eof = errors.Is(err, io.EOF)
if err != nil && !w.eof {
return n, err
}
if nRead != len(w.buf) && !w.eof {
return n, fmt.Errorf("short read: %d != %d", nRead, len(w.buf))
}
processed, err := w.fn(w.buf[:nRead])
if err != nil {
return n, err
}
w.buf = processed
w.consumed = 0
}
}
var eof error
if w.consumed == len(w.buf) && w.eof {
eof = io.EOF
}
return n, eof
}
func (w *wrapper) Close() error {
if w.writer != nil {
if w.buffered%w.blockSize != 0 {
w.buffered += copy(w.buf[w.buffered:], make([]byte, roundUpToMultiple(w.buffered%w.blockSize, w.blockSize)))
}
processed, err := w.fn(w.buf[:w.buffered])
if err != nil {
return err
}
nWritten, err := w.writer.Write(processed)
if err != nil {
return err
}
if nWritten != len(processed) {
return fmt.Errorf("short write: %d != %d", nWritten, len(processed))
}
w.buffered = 0
}
return nil
}
// EncryptWriter creates an io.WriteCloser which buffers writes through an
// encryption function. After writing a final block, the returned writer
// should be closed.
func EncryptWriter(fn func(plaintext []byte) ([]byte, error), writer io.Writer, blockSize int) io.WriteCloser {
bufferSize := roundUpToMultiple(1024*1024, blockSize)
return &wrapper{fn: fn, blockSize: blockSize, buf: make([]byte, bufferSize), writer: writer}
}
// DecryptReader creates an io.ReadCloser which buffers reads through a
// decryption function. When data will no longer be read, the returned reader
// should be closed.
func DecryptReader(fn func(ciphertext []byte) ([]byte, error), reader io.Reader, blockSize int) io.ReadCloser {
bufferSize := roundUpToMultiple(1024*1024, blockSize)
return &wrapper{fn: fn, blockSize: blockSize, buf: make([]byte, bufferSize), consumed: bufferSize, reader: reader}
}

75
vendor/github.com/containers/luksy/luks.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package luksy
import (
"bytes"
"encoding/json"
"fmt"
"os"
)
// ReadHeaderOptions can control some of what ReadHeaders() does.
type ReadHeaderOptions struct{}
// ReadHeaders reads LUKS headers from the specified file, returning either a
// LUKSv1 header, or two LUKSv2 headers and a LUKSv2 JSON block, depending on
// which format is detected.
func ReadHeaders(f *os.File, options ReadHeaderOptions) (*V1Header, *V2Header, *V2Header, *V2JSON, error) {
var v1 V1Header
var v2a, v2b V2Header
n, err := f.ReadAt(v2a[:], 0)
if err != nil {
return nil, nil, nil, nil, err
}
if n != len(v2a) {
return nil, nil, nil, nil, fmt.Errorf("only able to read %d bytes - file truncated?", n)
}
if n, err = f.ReadAt(v1[:], 0); err != nil {
return nil, nil, nil, nil, err
}
if n != len(v1) {
return nil, nil, nil, nil, fmt.Errorf("only able to read %d bytes - file truncated?", n)
}
if v2a.Magic() != V2Magic1 {
return nil, nil, nil, nil, fmt.Errorf("internal error: magic mismatch in LUKS header (%q)", v2a.Magic())
}
switch v2a.Version() { // is it a v1 header, or the first v2 header?
case 1:
return &v1, nil, nil, nil, nil
case 2:
size := v2a.HeaderSize()
if size > 0x7fffffffffffffff {
return nil, nil, nil, nil, fmt.Errorf("unsupported header size while looking for second header")
}
if size < 4096 {
return nil, nil, nil, nil, fmt.Errorf("unsupported header size while looking for JSON data")
}
if n, err = f.ReadAt(v2b[:], int64(size)); err != nil || n != len(v2b) {
if err == nil && n != len(v2b) {
err = fmt.Errorf("short read: read only %d bytes, should have read %d", n, len(v2b))
}
return nil, nil, nil, nil, err
}
if v2b.Magic() != V2Magic2 {
return nil, nil, nil, nil, fmt.Errorf("internal error: magic mismatch in second LUKS header (%q)", v2b.Magic())
}
jsonSize := size - 4096
buf := make([]byte, jsonSize)
n, err = f.ReadAt(buf[:], 4096)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("internal error: while reading JSON data: %w", err)
}
if n < 0 || uint64(n) != jsonSize {
return nil, nil, nil, nil, fmt.Errorf("internal error: short read while reading JSON data (wanted %d, got %d)", jsonSize, n)
}
var jsonData V2JSON
buf = bytes.TrimRightFunc(buf, func(r rune) bool { return r == 0 })
if err = json.Unmarshal(buf, &jsonData); err != nil {
return nil, nil, nil, nil, fmt.Errorf("internal error: decoding JSON data: %w", err)
}
if uint64(jsonData.Config.JsonSize) != jsonSize {
return nil, nil, nil, nil, fmt.Errorf("internal error: JSON data size mismatch: (expected %d, used %d)", jsonData.Config.JsonSize, jsonSize)
}
return nil, &v2a, &v2b, &jsonData, nil
}
return nil, nil, nil, nil, fmt.Errorf("error reading LUKS header - magic identifier not found")
}

55
vendor/github.com/containers/luksy/tune.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
package luksy
import (
"hash"
"time"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/pbkdf2"
)
func durationOf(f func()) time.Duration {
start := time.Now()
f()
return time.Since(start)
}
func IterationsPBKDF2(salt []byte, keyLen int, h func() hash.Hash) int {
iterations := 2
var d time.Duration
for d < time.Second {
d = durationOf(func() {
_ = pbkdf2.Key([]byte{}, salt, iterations, keyLen, h)
})
if d < time.Second/10 {
iterations *= 2
} else {
return iterations * int(time.Second) / int(d)
}
}
return iterations
}
func memoryCostArgon2(salt []byte, keyLen, timeCost, threadsCost int, kdf func([]byte, []byte, uint32, uint32, uint8, uint32) []byte) int {
memoryCost := 2
var d time.Duration
for d < time.Second {
d = durationOf(func() {
_ = kdf([]byte{}, salt, uint32(timeCost), uint32(memoryCost), uint8(threadsCost), uint32(keyLen))
})
if d < time.Second/10 {
memoryCost *= 2
} else {
return memoryCost * int(time.Second) / int(d)
}
}
return memoryCost
}
func MemoryCostArgon2(salt []byte, keyLen, timeCost, threadsCost int) int {
return memoryCostArgon2(salt, keyLen, timeCost, threadsCost, argon2.Key)
}
func MemoryCostArgon2i(salt []byte, keyLen, timeCost, threadsCost int) int {
return memoryCostArgon2(salt, keyLen, timeCost, threadsCost, argon2.IDKey)
}

321
vendor/github.com/containers/luksy/v1header.go generated vendored Normal file
View File

@ -0,0 +1,321 @@
package luksy
import (
"encoding/binary"
"fmt"
"syscall"
)
type (
V1Header [592]uint8
V1KeySlot [48]uint8
)
const (
// Mostly verbatim from LUKS1 On-Disk Format Specification version 1.2.3
V1Magic = "LUKS\xba\xbe"
v1MagicStart = 0
v1MagicLength = 6
v1VersionStart = v1MagicStart + v1MagicLength
v1VersionLength = 2
v1CipherNameStart = v1VersionStart + v1VersionLength
v1CipherNameLength = 32
v1CipherModeStart = v1CipherNameStart + v1CipherNameLength
v1CipherModeLength = 32
v1HashSpecStart = v1CipherModeStart + v1CipherModeLength
v1HashSpecLength = 32
v1PayloadOffsetStart = v1HashSpecStart + v1HashSpecLength
v1PayloadOffsetLength = 4
v1KeyBytesStart = v1PayloadOffsetStart + v1PayloadOffsetLength
v1KeyBytesLength = 4
v1MKDigestStart = v1KeyBytesStart + v1KeyBytesLength
v1MKDigestLength = v1DigestSize
v1MKDigestSaltStart = v1MKDigestStart + v1MKDigestLength
v1MKDigestSaltLength = v1SaltSize
v1MKDigestIterStart = v1MKDigestSaltStart + v1MKDigestSaltLength
v1MKDigestIterLength = 4
v1UUIDStart = v1MKDigestIterStart + v1MKDigestIterLength
v1UUIDLength = 40
v1KeySlot1Start = v1UUIDStart + v1UUIDLength
v1KeySlot1Length = 48
v1KeySlot2Start = v1KeySlot1Start + v1KeySlot1Length
v1KeySlot2Length = 48
v1KeySlot3Start = v1KeySlot2Start + v1KeySlot2Length
v1KeySlot3Length = 48
v1KeySlot4Start = v1KeySlot3Start + v1KeySlot3Length
v1KeySlot4Length = 48
v1KeySlot5Start = v1KeySlot4Start + v1KeySlot4Length
v1KeySlot5Length = 48
v1KeySlot6Start = v1KeySlot5Start + v1KeySlot5Length
v1KeySlot6Length = 48
v1KeySlot7Start = v1KeySlot6Start + v1KeySlot6Length
v1KeySlot7Length = 48
v1KeySlot8Start = v1KeySlot7Start + v1KeySlot7Length
v1KeySlot8Length = 48
v1HeaderStructSize = v1KeySlot8Start + v1KeySlot8Length
v1KeySlotActiveStart = 0
v1KeySlotActiveLength = 4
v1KeySlotIterationsStart = v1KeySlotActiveStart + v1KeySlotActiveLength
v1KeySlotIterationsLength = 4
v1KeySlotSaltStart = v1KeySlotIterationsStart + v1KeySlotIterationsLength
v1KeySlotSaltLength = v1SaltSize
v1KeySlotKeyMaterialOffsetStart = v1KeySlotSaltStart + v1KeySlotSaltLength
v1KeySlotKeyMaterialOffsetLength = 4
v1KeySlotStripesStart = v1KeySlotKeyMaterialOffsetStart + v1KeySlotKeyMaterialOffsetLength
v1KeySlotStripesLength = 4
v1KeySlotStructSize = v1KeySlotStripesStart + v1KeySlotStripesLength
v1DigestSize = 20
v1SaltSize = 32
v1NumKeys = 8
v1KeySlotActiveKeyDisabled = 0x0000dead
v1KeySlotActiveKeyEnabled = 0x00ac71f3
V1Stripes = 4000
V1AlignKeyslots = 4096
V1SectorSize = 512
)
func (h V1Header) readu2(offset int) uint16 {
return binary.BigEndian.Uint16(h[offset:])
}
func (h V1Header) readu4(offset int) uint32 {
return binary.BigEndian.Uint32(h[offset:])
}
func (h *V1Header) writeu2(offset int, value uint16) {
binary.BigEndian.PutUint16(h[offset:], value)
}
func (h *V1Header) writeu4(offset int, value uint32) {
binary.BigEndian.PutUint32(h[offset:], value)
}
func (h V1Header) Magic() string {
return trimZeroPad(string(h[v1MagicStart : v1MagicStart+v1MagicLength]))
}
func (h *V1Header) SetMagic(magic string) error {
switch magic {
case V1Magic:
copy(h[v1MagicStart:v1MagicStart+v1MagicLength], []uint8(magic))
return nil
}
return fmt.Errorf("magic %q not acceptable, only %q is an acceptable magic value: %w", magic, V1Magic, syscall.EINVAL)
}
func (h V1Header) Version() uint16 {
return h.readu2(v1VersionStart)
}
func (h *V1Header) SetVersion(version uint16) error {
switch version {
case 1:
h.writeu2(v1VersionStart, version)
return nil
}
return fmt.Errorf("version %d not acceptable, only 1 is an acceptable version: %w", version, syscall.EINVAL)
}
func (h *V1Header) setZeroString(offset int, value string, length int) {
for len(value) < length {
value = value + "\000"
}
copy(h[offset:offset+length], []uint8(value))
}
func (h *V1Header) setInt8(offset int, s []uint8, length int) {
t := make([]byte, length)
copy(t, s)
copy(h[offset:offset+length], s)
}
func (h V1Header) CipherName() string {
return trimZeroPad(string(h[v1CipherNameStart : v1CipherNameStart+v1CipherNameLength]))
}
func (h *V1Header) SetCipherName(name string) {
h.setZeroString(v1CipherNameStart, name, v1CipherNameLength)
}
func (h V1Header) CipherMode() string {
return trimZeroPad(string(h[v1CipherModeStart : v1CipherModeStart+v1CipherModeLength]))
}
func (h *V1Header) SetCipherMode(mode string) {
h.setZeroString(v1CipherModeStart, mode, v1CipherModeLength)
}
func (h V1Header) HashSpec() string {
return trimZeroPad(string(h[v1HashSpecStart : v1HashSpecStart+v1HashSpecLength]))
}
func (h *V1Header) SetHashSpec(spec string) {
h.setZeroString(v1HashSpecStart, spec, v1HashSpecLength)
}
func (h V1Header) PayloadOffset() uint32 {
return h.readu4(v1PayloadOffsetStart)
}
func (h *V1Header) SetPayloadOffset(offset uint32) {
h.writeu4(v1PayloadOffsetStart, offset)
}
func (h V1Header) KeyBytes() uint32 {
return h.readu4(v1KeyBytesStart)
}
func (h *V1Header) SetKeyBytes(bytes uint32) {
h.writeu4(v1KeyBytesStart, bytes)
}
func (h *V1Header) KeySlot(slot int) (V1KeySlot, error) {
var ks V1KeySlot
if slot < 0 || slot >= v1NumKeys {
return ks, fmt.Errorf("invalid key slot number (must be 0..%d)", v1NumKeys-1)
}
switch slot {
case 0:
copy(ks[:], h[v1KeySlot1Start:v1KeySlot1Start+v1KeySlot1Length])
case 1:
copy(ks[:], h[v1KeySlot2Start:v1KeySlot2Start+v1KeySlot2Length])
case 2:
copy(ks[:], h[v1KeySlot3Start:v1KeySlot3Start+v1KeySlot3Length])
case 3:
copy(ks[:], h[v1KeySlot4Start:v1KeySlot4Start+v1KeySlot4Length])
case 4:
copy(ks[:], h[v1KeySlot5Start:v1KeySlot5Start+v1KeySlot5Length])
case 5:
copy(ks[:], h[v1KeySlot6Start:v1KeySlot6Start+v1KeySlot6Length])
case 6:
copy(ks[:], h[v1KeySlot7Start:v1KeySlot7Start+v1KeySlot7Length])
case 7:
copy(ks[:], h[v1KeySlot8Start:v1KeySlot8Start+v1KeySlot8Length])
}
return ks, nil
}
func (h *V1Header) SetKeySlot(slot int, ks V1KeySlot) error {
if slot < 0 || slot >= v1NumKeys {
return fmt.Errorf("invalid key slot number (must be 0..%d)", v1NumKeys-1)
}
switch slot {
case 0:
copy(h[v1KeySlot1Start:v1KeySlot1Start+v1KeySlot1Length], ks[:])
case 1:
copy(h[v1KeySlot2Start:v1KeySlot2Start+v1KeySlot2Length], ks[:])
case 2:
copy(h[v1KeySlot3Start:v1KeySlot3Start+v1KeySlot3Length], ks[:])
case 3:
copy(h[v1KeySlot4Start:v1KeySlot4Start+v1KeySlot4Length], ks[:])
case 4:
copy(h[v1KeySlot5Start:v1KeySlot5Start+v1KeySlot5Length], ks[:])
case 5:
copy(h[v1KeySlot6Start:v1KeySlot6Start+v1KeySlot6Length], ks[:])
case 6:
copy(h[v1KeySlot7Start:v1KeySlot7Start+v1KeySlot7Length], ks[:])
case 7:
copy(h[v1KeySlot8Start:v1KeySlot8Start+v1KeySlot8Length], ks[:])
}
return nil
}
func (h V1Header) MKDigest() []uint8 {
return dupInt8(h[v1MKDigestStart : v1MKDigestStart+v1MKDigestLength])
}
func (h *V1Header) SetMKDigest(digest []uint8) {
h.setInt8(v1MKDigestStart, digest, v1MKDigestLength)
}
func (h V1Header) MKDigestSalt() []uint8 {
return dupInt8(h[v1MKDigestSaltStart : v1MKDigestSaltStart+v1MKDigestSaltLength])
}
func (h *V1Header) SetMKDigestSalt(salt []uint8) {
h.setInt8(v1MKDigestSaltStart, salt, v1MKDigestSaltLength)
}
func (h V1Header) MKDigestIter() uint32 {
return h.readu4(v1MKDigestIterStart)
}
func (h *V1Header) SetMKDigestIter(bytes uint32) {
h.writeu4(v1MKDigestIterStart, bytes)
}
func (h V1Header) UUID() string {
return trimZeroPad(string(h[v1UUIDStart : v1UUIDStart+v1UUIDLength]))
}
func (h *V1Header) SetUUID(uuid string) {
h.setZeroString(v1UUIDStart, uuid, v1UUIDLength)
}
func (s V1KeySlot) readu4(offset int) uint32 {
return binary.BigEndian.Uint32(s[offset:])
}
func (s *V1KeySlot) writeu4(offset int, value uint32) {
binary.BigEndian.PutUint32(s[offset:], value)
}
func (s *V1KeySlot) setInt8(offset int, i []uint8, length int) {
for len(s) < length {
i = append(i, 0)
}
copy(s[offset:offset+length], i)
}
func (s V1KeySlot) Active() (bool, error) {
active := s.readu4(v1KeySlotActiveStart)
switch active {
case v1KeySlotActiveKeyDisabled:
return false, nil
case v1KeySlotActiveKeyEnabled:
return true, nil
}
return false, fmt.Errorf("got invalid active value %#0x: %w", active, syscall.EINVAL)
}
func (s *V1KeySlot) SetActive(active bool) {
if active {
s.writeu4(v1KeySlotActiveStart, v1KeySlotActiveKeyEnabled)
return
}
s.writeu4(v1KeySlotActiveStart, v1KeySlotActiveKeyDisabled)
}
func (s V1KeySlot) Iterations() uint32 {
return s.readu4(v1KeySlotIterationsStart)
}
func (s *V1KeySlot) SetIterations(iterations uint32) {
s.writeu4(v1KeySlotIterationsStart, iterations)
}
func (s V1KeySlot) KeySlotSalt() []uint8 {
return dupInt8(s[v1KeySlotSaltStart : v1KeySlotSaltStart+v1KeySlotSaltLength])
}
func (s *V1KeySlot) SetKeySlotSalt(salt []uint8) {
s.setInt8(v1KeySlotSaltStart, salt, v1KeySlotSaltLength)
}
func (s V1KeySlot) KeyMaterialOffset() uint32 {
return s.readu4(v1KeySlotKeyMaterialOffsetStart)
}
func (s *V1KeySlot) SetKeyMaterialOffset(material uint32) {
s.writeu4(v1KeySlotKeyMaterialOffsetStart, material)
}
func (s V1KeySlot) Stripes() uint32 {
return s.readu4(v1KeySlotStripesStart)
}
func (s *V1KeySlot) SetStripes(stripes uint32) {
s.writeu4(v1KeySlotStripesStart, stripes)
}

203
vendor/github.com/containers/luksy/v2header.go generated vendored Normal file
View File

@ -0,0 +1,203 @@
package luksy
import (
"fmt"
"strings"
"syscall"
)
type V2Header [4096]uint8
const (
// Mostly verbatim from LUKS2 On-Disk Format Specification version 1.1.1
V2Magic1 = V1Magic
V2Magic2 = "SKUL\xba\xbe"
v2MagicStart = 0
v2MagicLength = 6
v2VersionStart = v2MagicStart + v2MagicLength
v2VersionLength = 2
v2HeaderSizeStart = v2VersionStart + v2VersionLength
v2HeaderSizeLength = 8
v2SequenceIDStart = v2HeaderSizeStart + v2HeaderSizeLength
v2SequenceIDLength = 8
v2LabelStart = v2SequenceIDStart + v2SequenceIDLength
v2LabelLength = 48
v2ChecksumAlgorithmStart = v2LabelStart + v2LabelLength
v2ChecksumAlgorithmLength = 32
v2SaltStart = v2ChecksumAlgorithmStart + v2ChecksumAlgorithmLength
v2SaltLength = 64
v2UUIDStart = v2SaltStart + v2SaltLength
v2UUIDLength = 40
v2SubsystemStart = v2UUIDStart + v2UUIDLength
v2SubsystemLength = v2LabelLength
v2HeaderOffsetStart = v2SubsystemStart + v2SubsystemLength
v2HeaderOffsetLength = 8
v2Padding1Start = v2HeaderOffsetStart + v2HeaderOffsetLength
v2Padding1Length = 184
v2ChecksumStart = v2Padding1Start + v2Padding1Length
v2ChecksumLength = 64
v2Padding4096Start = v2ChecksumStart + v2ChecksumLength
v2Padding4096Length = 7 * 512
v2HeaderStructSize = v2Padding4096Start + v2Padding4096Length
V2Stripes = 4000
V2AlignKeyslots = 4096
V2SectorSize = 4096
)
func (h V2Header) Magic() string {
return string(h[v2MagicStart : v2MagicStart+v2MagicLength])
}
func (h *V2Header) SetMagic(magic string) error {
switch magic {
case V2Magic1, V2Magic2:
copy(h[v2MagicStart:v2MagicStart+v2MagicLength], []uint8(magic))
return nil
}
return fmt.Errorf("magic %q not acceptable, only %q and %q are acceptable magic values: %w", magic, V2Magic1, V2Magic2, syscall.EINVAL)
}
func (h V2Header) readu2(offset int) uint16 {
t := uint16(0)
for i := 0; i < 2; i++ {
t = (t << 8) + uint16(h[offset+i])
}
return t
}
func (h V2Header) readu8(offset int) uint64 {
t := uint64(0)
for i := 0; i < 8; i++ {
t = (t << 8) + uint64(h[offset+i])
}
return t
}
func (h *V2Header) writeu2(offset int, value uint16) {
t := value
for i := 0; i < 2; i++ {
h[offset+1-i] = uint8(uint64(t) & 0xff)
t >>= 8
}
}
func (h *V2Header) writeu8(offset int, value uint64) {
t := value
for i := 0; i < 8; i++ {
h[offset+7-i] = uint8(uint64(t) & 0xff)
t >>= 8
}
}
func (h V2Header) Version() uint16 {
return h.readu2(v2VersionStart)
}
func (h *V2Header) SetVersion(version uint16) error {
switch version {
case 2:
h.writeu2(v2VersionStart, version)
return nil
}
return fmt.Errorf("version %d not acceptable, only 2 is an acceptable version: %w", version, syscall.EINVAL)
}
func (h V2Header) HeaderSize() uint64 {
return h.readu8(v2HeaderSizeStart)
}
func (h *V2Header) SetHeaderSize(size uint64) {
h.writeu8(v2HeaderSizeStart, size)
}
func (h V2Header) SequenceID() uint64 {
return h.readu8(v2SequenceIDStart)
}
func (h *V2Header) SetSequenceID(id uint64) {
h.writeu8(v2SequenceIDStart, id)
}
func trimZeroPad(s string) string {
return strings.TrimRightFunc(s, func(r rune) bool { return r == 0 })
}
func (h V2Header) Label() string {
return trimZeroPad(string(h[v2LabelStart : v2LabelStart+v2LabelLength]))
}
func (h *V2Header) setZeroString(offset int, value string, length int) {
for len(value) < length {
value = value + "\000"
}
copy(h[offset:offset+length], []uint8(value))
}
func (h *V2Header) SetLabel(label string) {
h.setZeroString(v2LabelStart, label, v2LabelLength)
}
func (h V2Header) ChecksumAlgorithm() string {
return trimZeroPad(string(h[v2ChecksumAlgorithmStart : v2ChecksumAlgorithmStart+v2ChecksumAlgorithmLength]))
}
func (h *V2Header) SetChecksumAlgorithm(alg string) {
h.setZeroString(v2ChecksumAlgorithmStart, alg, v2ChecksumAlgorithmLength)
}
func dupInt8(s []uint8) []uint8 {
c := make([]uint8, len(s))
copy(c, s)
return c
}
func (h *V2Header) setInt8(offset int, s []uint8, length int) {
t := make([]byte, length)
copy(t, s)
copy(h[offset:offset+length], t)
}
func (h V2Header) Salt() []uint8 {
return dupInt8(h[v2SaltStart : v2SaltStart+v2SaltLength])
}
func (h *V2Header) SetSalt(salt []uint8) {
h.setInt8(v2SaltStart, salt, v2SaltLength)
}
func (h V2Header) UUID() string {
return trimZeroPad(string(h[v2UUIDStart : v2UUIDStart+v2UUIDLength]))
}
func (h *V2Header) SetUUID(uuid string) {
h.setZeroString(v2UUIDStart, uuid, v2UUIDLength)
}
func (h V2Header) Subsystem() string {
return trimZeroPad(string(h[v2SubsystemStart : v2SubsystemStart+v2SubsystemLength]))
}
func (h *V2Header) SetSubsystem(ss string) {
h.setZeroString(v2SubsystemStart, ss, v2SubsystemLength)
}
func (h V2Header) HeaderOffset() uint64 {
return h.readu8(v2HeaderOffsetStart)
}
func (h *V2Header) SetHeaderOffset(o uint64) {
h.writeu8(v2HeaderOffsetStart, o)
}
func (h V2Header) Checksum() []uint8 {
hasher, err := hasherByName(h.ChecksumAlgorithm())
if err == nil {
return dupInt8(h[v2ChecksumStart : v2ChecksumStart+hasher().Size()])
}
return dupInt8(h[v2ChecksumStart : v2ChecksumStart+v2ChecksumLength])
}
func (h *V2Header) SetChecksum(sum []uint8) {
h.setInt8(v2ChecksumStart, sum, v2ChecksumLength)
}

157
vendor/github.com/containers/luksy/v2json.go generated vendored Normal file
View File

@ -0,0 +1,157 @@
package luksy
type V2JSON struct {
Config V2JSONConfig `json:"config"`
Keyslots map[string]V2JSONKeyslot `json:"keyslots"`
Digests map[string]V2JSONDigest `json:"digests"`
Segments map[string]V2JSONSegment `json:"segments"`
Tokens map[string]V2JSONToken `json:"tokens"`
}
type V2JSONKeyslotPriority int
func (p V2JSONKeyslotPriority) String() string {
switch p {
case V2JSONKeyslotPriorityIgnore:
return "ignore"
case V2JSONKeyslotPriorityNormal:
return "normal"
case V2JSONKeyslotPriorityHigh:
return "high"
}
return "unknown"
}
const (
V2JSONKeyslotPriorityIgnore = V2JSONKeyslotPriority(0)
V2JSONKeyslotPriorityNormal = V2JSONKeyslotPriority(1)
V2JSONKeyslotPriorityHigh = V2JSONKeyslotPriority(2)
)
type V2JSONKeyslot struct {
Type string `json:"type"`
KeySize int `json:"key_size"`
Area V2JSONArea `json:"area"`
Priority *V2JSONKeyslotPriority `json:"priority,omitempty"`
*V2JSONKeyslotLUKS2 // type = "luks2"
*V2JSONKeyslotReencrypt // type = "reencrypt"
}
type V2JSONKeyslotLUKS2 struct {
AF V2JSONAF `json:"af"`
Kdf V2JSONKdf `json:"kdf"`
}
type V2JSONKeyslotReencrypt struct {
Mode string `json:"mode"` // only "reencrypt", "encrypt", "decrypt"
Direction string `json:"direction"` // only "forward", "backward"
}
type V2JSONArea struct {
Type string `json:"type"` // only "raw", "none", "journal", "checksum", "datashift", "datashift-journal", "datashift-checksum"
Offset int64 `json:"offset,string"`
Size int64 `json:"size,string"`
*V2JSONAreaRaw // type = "raw"
*V2JSONAreaChecksum // type = "checksum"
*V2JSONAreaDatashift // type = "datashift"
*V2JSONAreaDatashiftChecksum // type = "datashift-checksum"
}
type V2JSONAreaRaw struct {
Encryption string `json:"encryption"`
KeySize int `json:"key_size"`
}
type V2JSONAreaChecksum struct {
Hash string `json:"hash"`
SectorSize int `json:"sector_size"`
}
type V2JSONAreaDatashift struct {
ShiftSize int `json:"shift_size,string"`
}
type V2JSONAreaDatashiftChecksum struct {
V2JSONAreaChecksum
V2JSONAreaDatashift
}
type V2JSONAF struct {
Type string `json:"type"` // "luks1"
*V2JSONAFLUKS1 // type == "luks1"
}
type V2JSONAFLUKS1 struct {
Stripes int `json:"stripes"` // 4000
Hash string `json:"hash"` // "sha256"
}
type V2JSONKdf struct {
Type string `json:"type"`
Salt []byte `json:"salt"`
*V2JSONKdfPbkdf2 // type = "pbkdf2"
*V2JSONKdfArgon2i // type = "argon2i" or type = "argon2id"
}
type V2JSONKdfPbkdf2 struct {
Hash string `json:"hash"`
Iterations int `json:"iterations"`
}
type V2JSONKdfArgon2i struct {
Time int `json:"time"`
Memory int `json:"memory"`
CPUs int `json:"cpus"`
}
type V2JSONSegment struct {
Type string `json:"type"` // only "linear", "crypt"
Offset string `json:"offset"`
Size string `json:"size"` // numeric value or "dynamic"
Flags []string `json:"flags,omitempty"`
*V2JSONSegmentCrypt `json:",omitempty"` // type = "crypt"
}
type V2JSONSegmentCrypt struct {
IVTweak int `json:"iv_tweak,string"`
Encryption string `json:"encryption"`
SectorSize int `json:"sector_size"` // 512 or 1024 or 2048 or 4096
Integrity *V2JSONSegmentIntegrity `json:"integrity,omitempty"`
}
type V2JSONSegmentIntegrity struct {
Type string `json:"type"`
JournalEncryption string `json:"journal_encryption"`
JournalIntegrity string `json:"journal_integrity"`
}
type V2JSONDigest struct {
Type string `json:"type"`
Keyslots []string `json:"keyslots"`
Segments []string `json:"segments"`
Salt []byte `json:"salt"`
Digest []byte `json:"digest"`
*V2JSONDigestPbkdf2 // type == "pbkdf2"
}
type V2JSONDigestPbkdf2 struct {
Hash string `json:"hash"`
Iterations int `json:"iterations"`
}
type V2JSONConfig struct {
JsonSize int `json:"json_size,string"`
KeyslotsSize int `json:"keyslots_size,string,omitempty"`
Flags []string `json:"flags,omitempty"` // one or more of "allow-discards", "same-cpu-crypt", "submit-from-crypt-cpus", "no-journal", "no-read-workqueue", "no-write-workqueue"
Requirements []string `json:"requirements,omitempty"`
}
type V2JSONToken struct {
Type string `json:"type"` // "luks2-keyring"
Keyslots []string `json:"keyslots,omitempty"`
*V2JSONTokenLUKS2Keyring // type == "luks2-keyring"
}
type V2JSONTokenLUKS2Keyring struct {
KeyDescription string `json:"key_description"`
}

283
vendor/golang.org/x/crypto/argon2/argon2.go generated vendored Normal file
View File

@ -0,0 +1,283 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package argon2 implements the key derivation function Argon2.
// Argon2 was selected as the winner of the Password Hashing Competition and can
// be used to derive cryptographic keys from passwords.
//
// For a detailed specification of Argon2 see [1].
//
// If you aren't sure which function you need, use Argon2id (IDKey) and
// the parameter recommendations for your scenario.
//
// # Argon2i
//
// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
// It uses data-independent memory access, which is preferred for password
// hashing and password-based key derivation. Argon2i requires more passes over
// memory than Argon2id to protect from trade-off attacks. The recommended
// parameters (taken from [2]) for non-interactive operations are time=3 and to
// use the maximum available memory.
//
// # Argon2id
//
// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
// Argon2i and Argon2d. It uses data-independent memory access for the first
// half of the first iteration over the memory and data-dependent memory access
// for the rest. Argon2id is side-channel resistant and provides better brute-
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
// parameters for non-interactive operations (taken from [2]) are time=1 and to
// use the maximum available memory.
//
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
package argon2
import (
"encoding/binary"
"sync"
"golang.org/x/crypto/blake2b"
)
// The Argon2 version implemented by this package.
const Version = 0x13
const (
argon2d = iota
argon2i
argon2id
)
// Key derives a key from the password, salt, and cost parameters using Argon2i
// returning a byte slice of length keyLen that can be used as cryptographic
// key. The CPU cost and parallelism degree must be greater than zero.
//
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
//
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
// If using that amount of memory (32 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
// memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
// adjusted to the number of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
}
// IDKey derives a key from the password, salt, and cost parameters using
// Argon2id returning a byte slice of length keyLen that can be used as
// cryptographic key. The CPU cost and parallelism degree must be greater than
// zero.
//
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
//
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
// If using that amount of memory (64 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
// adjusted to the numbers of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
}
func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
if time < 1 {
panic("argon2: number of rounds too small")
}
if threads < 1 {
panic("argon2: parallelism degree too low")
}
h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)
memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))
if memory < 2*syncPoints*uint32(threads) {
memory = 2 * syncPoints * uint32(threads)
}
B := initBlocks(&h0, memory, uint32(threads))
processBlocks(B, time, memory, uint32(threads), mode)
return extractKey(B, memory, uint32(threads), keyLen)
}
const (
blockLength = 128
syncPoints = 4
)
type block [blockLength]uint64
func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {
var (
h0 [blake2b.Size + 8]byte
params [24]byte
tmp [4]byte
)
b2, _ := blake2b.New512(nil)
binary.LittleEndian.PutUint32(params[0:4], threads)
binary.LittleEndian.PutUint32(params[4:8], keyLen)
binary.LittleEndian.PutUint32(params[8:12], memory)
binary.LittleEndian.PutUint32(params[12:16], time)
binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
b2.Write(params[:])
binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
b2.Write(tmp[:])
b2.Write(password)
binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
b2.Write(tmp[:])
b2.Write(salt)
binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
b2.Write(tmp[:])
b2.Write(key)
binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
b2.Write(tmp[:])
b2.Write(data)
b2.Sum(h0[:0])
return h0
}
func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {
var block0 [1024]byte
B := make([]block, memory)
for lane := uint32(0); lane < threads; lane++ {
j := lane * (memory / threads)
binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
blake2bHash(block0[:], h0[:])
for i := range B[j+0] {
B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
}
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
blake2bHash(block0[:], h0[:])
for i := range B[j+1] {
B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
}
}
return B
}
func processBlocks(B []block, time, memory, threads uint32, mode int) {
lanes := memory / threads
segments := lanes / syncPoints
processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
var addresses, in, zero block
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
in[0] = uint64(n)
in[1] = uint64(lane)
in[2] = uint64(slice)
in[3] = uint64(memory)
in[4] = uint64(time)
in[5] = uint64(mode)
}
index := uint32(0)
if n == 0 && slice == 0 {
index = 2 // we have already generated the first two blocks
if mode == argon2i || mode == argon2id {
in[6]++
processBlock(&addresses, &in, &zero)
processBlock(&addresses, &addresses, &zero)
}
}
offset := lane*lanes + slice*segments + index
var random uint64
for index < segments {
prev := offset - 1
if index == 0 && slice == 0 {
prev += lanes // last block in lane
}
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
if index%blockLength == 0 {
in[6]++
processBlock(&addresses, &in, &zero)
processBlock(&addresses, &addresses, &zero)
}
random = addresses[index%blockLength]
} else {
random = B[prev][0]
}
newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
processBlockXOR(&B[offset], &B[prev], &B[newOffset])
index, offset = index+1, offset+1
}
wg.Done()
}
for n := uint32(0); n < time; n++ {
for slice := uint32(0); slice < syncPoints; slice++ {
var wg sync.WaitGroup
for lane := uint32(0); lane < threads; lane++ {
wg.Add(1)
go processSegment(n, slice, lane, &wg)
}
wg.Wait()
}
}
}
func extractKey(B []block, memory, threads, keyLen uint32) []byte {
lanes := memory / threads
for lane := uint32(0); lane < threads-1; lane++ {
for i, v := range B[(lane*lanes)+lanes-1] {
B[memory-1][i] ^= v
}
}
var block [1024]byte
for i, v := range B[memory-1] {
binary.LittleEndian.PutUint64(block[i*8:], v)
}
key := make([]byte, keyLen)
blake2bHash(key, block[:])
return key
}
func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
refLane := uint32(rand>>32) % threads
if n == 0 && slice == 0 {
refLane = lane
}
m, s := 3*segments, ((slice+1)%syncPoints)*segments
if lane == refLane {
m += index
}
if n == 0 {
m, s = slice*segments, 0
if slice == 0 || lane == refLane {
m += index
}
}
if index == 0 || lane == refLane {
m--
}
return phi(rand, uint64(m), uint64(s), refLane, lanes)
}
func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
p := rand & 0xFFFFFFFF
p = (p * p) >> 32
p = (p * m) >> 32
return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
}

53
vendor/golang.org/x/crypto/argon2/blake2b.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package argon2
import (
"encoding/binary"
"hash"
"golang.org/x/crypto/blake2b"
)
// blake2bHash computes an arbitrary long hash value of in
// and writes the hash to out.
func blake2bHash(out []byte, in []byte) {
var b2 hash.Hash
if n := len(out); n < blake2b.Size {
b2, _ = blake2b.New(n, nil)
} else {
b2, _ = blake2b.New512(nil)
}
var buffer [blake2b.Size]byte
binary.LittleEndian.PutUint32(buffer[:4], uint32(len(out)))
b2.Write(buffer[:4])
b2.Write(in)
if len(out) <= blake2b.Size {
b2.Sum(out[:0])
return
}
outLen := len(out)
b2.Sum(buffer[:0])
b2.Reset()
copy(out, buffer[:32])
out = out[32:]
for len(out) > blake2b.Size {
b2.Write(buffer[:])
b2.Sum(buffer[:0])
copy(out, buffer[:32])
out = out[32:]
b2.Reset()
}
if outLen%blake2b.Size > 0 { // outLen > 64
r := ((outLen + 31) / 32) - 2 // ⌈τ /32⌉-2
b2, _ = blake2b.New(outLen-32*r, nil)
}
b2.Write(buffer[:])
b2.Sum(out[:0])
}

61
vendor/golang.org/x/crypto/argon2/blamka_amd64.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package argon2
import "golang.org/x/sys/cpu"
func init() {
useSSE4 = cpu.X86.HasSSE41
}
//go:noescape
func mixBlocksSSE2(out, a, b, c *block)
//go:noescape
func xorBlocksSSE2(out, a, b, c *block)
//go:noescape
func blamkaSSE4(b *block)
func processBlockSSE(out, in1, in2 *block, xor bool) {
var t block
mixBlocksSSE2(&t, in1, in2, &t)
if useSSE4 {
blamkaSSE4(&t)
} else {
for i := 0; i < blockLength; i += 16 {
blamkaGeneric(
&t[i+0], &t[i+1], &t[i+2], &t[i+3],
&t[i+4], &t[i+5], &t[i+6], &t[i+7],
&t[i+8], &t[i+9], &t[i+10], &t[i+11],
&t[i+12], &t[i+13], &t[i+14], &t[i+15],
)
}
for i := 0; i < blockLength/8; i += 2 {
blamkaGeneric(
&t[i], &t[i+1], &t[16+i], &t[16+i+1],
&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
)
}
}
if xor {
xorBlocksSSE2(out, in1, in2, &t)
} else {
mixBlocksSSE2(out, in1, in2, &t)
}
}
func processBlock(out, in1, in2 *block) {
processBlockSSE(out, in1, in2, false)
}
func processBlockXOR(out, in1, in2 *block) {
processBlockSSE(out, in1, in2, true)
}

244
vendor/golang.org/x/crypto/argon2/blamka_amd64.s generated vendored Normal file
View File

@ -0,0 +1,244 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
#include "textflag.h"
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
MOVO v4, t1; \
MOVO v5, v4; \
MOVO t1, v5; \
MOVO v6, t1; \
PUNPCKLQDQ v6, t2; \
PUNPCKHQDQ v7, v6; \
PUNPCKHQDQ t2, v6; \
PUNPCKLQDQ v7, t2; \
MOVO t1, v7; \
MOVO v2, t1; \
PUNPCKHQDQ t2, v7; \
PUNPCKLQDQ v3, t2; \
PUNPCKHQDQ t2, v2; \
PUNPCKLQDQ t1, t2; \
PUNPCKHQDQ t2, v3
#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
MOVO v4, t1; \
MOVO v5, v4; \
MOVO t1, v5; \
MOVO v2, t1; \
PUNPCKLQDQ v2, t2; \
PUNPCKHQDQ v3, v2; \
PUNPCKHQDQ t2, v2; \
PUNPCKLQDQ v3, t2; \
MOVO t1, v3; \
MOVO v6, t1; \
PUNPCKHQDQ t2, v3; \
PUNPCKLQDQ v7, t2; \
PUNPCKHQDQ t2, v6; \
PUNPCKLQDQ t1, t2; \
PUNPCKHQDQ t2, v7
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48) \
MOVO v0, t0; \
PMULULQ v2, t0; \
PADDQ v2, v0; \
PADDQ t0, v0; \
PADDQ t0, v0; \
PXOR v0, v6; \
PSHUFD $0xB1, v6, v6; \
MOVO v4, t0; \
PMULULQ v6, t0; \
PADDQ v6, v4; \
PADDQ t0, v4; \
PADDQ t0, v4; \
PXOR v4, v2; \
PSHUFB c40, v2; \
MOVO v0, t0; \
PMULULQ v2, t0; \
PADDQ v2, v0; \
PADDQ t0, v0; \
PADDQ t0, v0; \
PXOR v0, v6; \
PSHUFB c48, v6; \
MOVO v4, t0; \
PMULULQ v6, t0; \
PADDQ v6, v4; \
PADDQ t0, v4; \
PADDQ t0, v4; \
PXOR v4, v2; \
MOVO v2, t0; \
PADDQ v2, t0; \
PSRLQ $63, v2; \
PXOR t0, v2; \
MOVO v1, t0; \
PMULULQ v3, t0; \
PADDQ v3, v1; \
PADDQ t0, v1; \
PADDQ t0, v1; \
PXOR v1, v7; \
PSHUFD $0xB1, v7, v7; \
MOVO v5, t0; \
PMULULQ v7, t0; \
PADDQ v7, v5; \
PADDQ t0, v5; \
PADDQ t0, v5; \
PXOR v5, v3; \
PSHUFB c40, v3; \
MOVO v1, t0; \
PMULULQ v3, t0; \
PADDQ v3, v1; \
PADDQ t0, v1; \
PADDQ t0, v1; \
PXOR v1, v7; \
PSHUFB c48, v7; \
MOVO v5, t0; \
PMULULQ v7, t0; \
PADDQ v7, v5; \
PADDQ t0, v5; \
PADDQ t0, v5; \
PXOR v5, v3; \
MOVO v3, t0; \
PADDQ v3, t0; \
PSRLQ $63, v3; \
PXOR t0, v3
#define LOAD_MSG_0(block, off) \
MOVOU 8*(off+0)(block), X0; \
MOVOU 8*(off+2)(block), X1; \
MOVOU 8*(off+4)(block), X2; \
MOVOU 8*(off+6)(block), X3; \
MOVOU 8*(off+8)(block), X4; \
MOVOU 8*(off+10)(block), X5; \
MOVOU 8*(off+12)(block), X6; \
MOVOU 8*(off+14)(block), X7
#define STORE_MSG_0(block, off) \
MOVOU X0, 8*(off+0)(block); \
MOVOU X1, 8*(off+2)(block); \
MOVOU X2, 8*(off+4)(block); \
MOVOU X3, 8*(off+6)(block); \
MOVOU X4, 8*(off+8)(block); \
MOVOU X5, 8*(off+10)(block); \
MOVOU X6, 8*(off+12)(block); \
MOVOU X7, 8*(off+14)(block)
#define LOAD_MSG_1(block, off) \
MOVOU 8*off+0*8(block), X0; \
MOVOU 8*off+16*8(block), X1; \
MOVOU 8*off+32*8(block), X2; \
MOVOU 8*off+48*8(block), X3; \
MOVOU 8*off+64*8(block), X4; \
MOVOU 8*off+80*8(block), X5; \
MOVOU 8*off+96*8(block), X6; \
MOVOU 8*off+112*8(block), X7
#define STORE_MSG_1(block, off) \
MOVOU X0, 8*off+0*8(block); \
MOVOU X1, 8*off+16*8(block); \
MOVOU X2, 8*off+32*8(block); \
MOVOU X3, 8*off+48*8(block); \
MOVOU X4, 8*off+64*8(block); \
MOVOU X5, 8*off+80*8(block); \
MOVOU X6, 8*off+96*8(block); \
MOVOU X7, 8*off+112*8(block)
#define BLAMKA_ROUND_0(block, off, t0, t1, c40, c48) \
LOAD_MSG_0(block, off); \
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \
STORE_MSG_0(block, off)
#define BLAMKA_ROUND_1(block, off, t0, t1, c40, c48) \
LOAD_MSG_1(block, off); \
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \
STORE_MSG_1(block, off)
// func blamkaSSE4(b *block)
TEXT ·blamkaSSE4(SB), 4, $0-8
MOVQ b+0(FP), AX
MOVOU ·c40<>(SB), X10
MOVOU ·c48<>(SB), X11
BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11)
RET
// func mixBlocksSSE2(out, a, b, c *block)
TEXT ·mixBlocksSSE2(SB), 4, $0-32
MOVQ out+0(FP), DX
MOVQ a+8(FP), AX
MOVQ b+16(FP), BX
MOVQ a+24(FP), CX
MOVQ $128, BP
loop:
MOVOU 0(AX), X0
MOVOU 0(BX), X1
MOVOU 0(CX), X2
PXOR X1, X0
PXOR X2, X0
MOVOU X0, 0(DX)
ADDQ $16, AX
ADDQ $16, BX
ADDQ $16, CX
ADDQ $16, DX
SUBQ $2, BP
JA loop
RET
// func xorBlocksSSE2(out, a, b, c *block)
TEXT ·xorBlocksSSE2(SB), 4, $0-32
MOVQ out+0(FP), DX
MOVQ a+8(FP), AX
MOVQ b+16(FP), BX
MOVQ a+24(FP), CX
MOVQ $128, BP
loop:
MOVOU 0(AX), X0
MOVOU 0(BX), X1
MOVOU 0(CX), X2
MOVOU 0(DX), X3
PXOR X1, X0
PXOR X2, X0
PXOR X3, X0
MOVOU X0, 0(DX)
ADDQ $16, AX
ADDQ $16, BX
ADDQ $16, CX
ADDQ $16, DX
SUBQ $2, BP
JA loop
RET

163
vendor/golang.org/x/crypto/argon2/blamka_generic.go generated vendored Normal file
View File

@ -0,0 +1,163 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package argon2
var useSSE4 bool
func processBlockGeneric(out, in1, in2 *block, xor bool) {
var t block
for i := range t {
t[i] = in1[i] ^ in2[i]
}
for i := 0; i < blockLength; i += 16 {
blamkaGeneric(
&t[i+0], &t[i+1], &t[i+2], &t[i+3],
&t[i+4], &t[i+5], &t[i+6], &t[i+7],
&t[i+8], &t[i+9], &t[i+10], &t[i+11],
&t[i+12], &t[i+13], &t[i+14], &t[i+15],
)
}
for i := 0; i < blockLength/8; i += 2 {
blamkaGeneric(
&t[i], &t[i+1], &t[16+i], &t[16+i+1],
&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
)
}
if xor {
for i := range t {
out[i] ^= in1[i] ^ in2[i] ^ t[i]
}
} else {
for i := range t {
out[i] = in1[i] ^ in2[i] ^ t[i]
}
}
}
func blamkaGeneric(t00, t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15 *uint64) {
v00, v01, v02, v03 := *t00, *t01, *t02, *t03
v04, v05, v06, v07 := *t04, *t05, *t06, *t07
v08, v09, v10, v11 := *t08, *t09, *t10, *t11
v12, v13, v14, v15 := *t12, *t13, *t14, *t15
v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
v12 ^= v00
v12 = v12>>32 | v12<<32
v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
v04 ^= v08
v04 = v04>>24 | v04<<40
v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
v12 ^= v00
v12 = v12>>16 | v12<<48
v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
v04 ^= v08
v04 = v04>>63 | v04<<1
v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
v13 ^= v01
v13 = v13>>32 | v13<<32
v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
v05 ^= v09
v05 = v05>>24 | v05<<40
v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
v13 ^= v01
v13 = v13>>16 | v13<<48
v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
v05 ^= v09
v05 = v05>>63 | v05<<1
v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
v14 ^= v02
v14 = v14>>32 | v14<<32
v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
v06 ^= v10
v06 = v06>>24 | v06<<40
v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
v14 ^= v02
v14 = v14>>16 | v14<<48
v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
v06 ^= v10
v06 = v06>>63 | v06<<1
v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
v15 ^= v03
v15 = v15>>32 | v15<<32
v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
v07 ^= v11
v07 = v07>>24 | v07<<40
v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
v15 ^= v03
v15 = v15>>16 | v15<<48
v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
v07 ^= v11
v07 = v07>>63 | v07<<1
v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
v15 ^= v00
v15 = v15>>32 | v15<<32
v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
v05 ^= v10
v05 = v05>>24 | v05<<40
v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
v15 ^= v00
v15 = v15>>16 | v15<<48
v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
v05 ^= v10
v05 = v05>>63 | v05<<1
v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
v12 ^= v01
v12 = v12>>32 | v12<<32
v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
v06 ^= v11
v06 = v06>>24 | v06<<40
v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
v12 ^= v01
v12 = v12>>16 | v12<<48
v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
v06 ^= v11
v06 = v06>>63 | v06<<1
v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
v13 ^= v02
v13 = v13>>32 | v13<<32
v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
v07 ^= v08
v07 = v07>>24 | v07<<40
v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
v13 ^= v02
v13 = v13>>16 | v13<<48
v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
v07 ^= v08
v07 = v07>>63 | v07<<1
v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
v14 ^= v03
v14 = v14>>32 | v14<<32
v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
v04 ^= v09
v04 = v04>>24 | v04<<40
v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
v14 ^= v03
v14 = v14>>16 | v14<<48
v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
v04 ^= v09
v04 = v04>>63 | v04<<1
*t00, *t01, *t02, *t03 = v00, v01, v02, v03
*t04, *t05, *t06, *t07 = v04, v05, v06, v07
*t08, *t09, *t10, *t11 = v08, v09, v10, v11
*t12, *t13, *t14, *t15 = v12, v13, v14, v15
}

16
vendor/golang.org/x/crypto/argon2/blamka_ref.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !amd64 || purego || !gc
// +build !amd64 purego !gc
package argon2
func processBlock(out, in1, in2 *block) {
processBlockGeneric(out, in1, in2, false)
}
func processBlockXOR(out, in1, in2 *block) {
processBlockGeneric(out, in1, in2, true)
}

291
vendor/golang.org/x/crypto/blake2b/blake2b.go generated vendored Normal file
View File

@ -0,0 +1,291 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693
// and the extendable output function (XOF) BLAKE2Xb.
//
// BLAKE2b is optimized for 64-bit platforms—including NEON-enabled ARMs—and
// produces digests of any size between 1 and 64 bytes.
// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf
// and for BLAKE2Xb see https://blake2.net/blake2x.pdf
//
// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512).
// If you need a secret-key MAC (message authentication code), use the New512
// function with a non-nil key.
//
// BLAKE2X is a construction to compute hash values larger than 64 bytes. It
// can produce hash values between 0 and 4 GiB.
package blake2b
import (
"encoding/binary"
"errors"
"hash"
)
const (
// The blocksize of BLAKE2b in bytes.
BlockSize = 128
// The hash size of BLAKE2b-512 in bytes.
Size = 64
// The hash size of BLAKE2b-384 in bytes.
Size384 = 48
// The hash size of BLAKE2b-256 in bytes.
Size256 = 32
)
var (
useAVX2 bool
useAVX bool
useSSE4 bool
)
var (
errKeySize = errors.New("blake2b: invalid key size")
errHashSize = errors.New("blake2b: invalid hash size")
)
var iv = [8]uint64{
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
}
// Sum512 returns the BLAKE2b-512 checksum of the data.
func Sum512(data []byte) [Size]byte {
var sum [Size]byte
checkSum(&sum, Size, data)
return sum
}
// Sum384 returns the BLAKE2b-384 checksum of the data.
func Sum384(data []byte) [Size384]byte {
var sum [Size]byte
var sum384 [Size384]byte
checkSum(&sum, Size384, data)
copy(sum384[:], sum[:Size384])
return sum384
}
// Sum256 returns the BLAKE2b-256 checksum of the data.
func Sum256(data []byte) [Size256]byte {
var sum [Size]byte
var sum256 [Size256]byte
checkSum(&sum, Size256, data)
copy(sum256[:], sum[:Size256])
return sum256
}
// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
// A non-nil key turns the hash into a MAC. The key must be between zero and 64 bytes long.
// The hash size can be a value between 1 and 64 but it is highly recommended to use
// values equal or greater than:
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long).
// When the key is nil, the returned hash.Hash implements BinaryMarshaler
// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) }
func newDigest(hashSize int, key []byte) (*digest, error) {
if hashSize < 1 || hashSize > Size {
return nil, errHashSize
}
if len(key) > Size {
return nil, errKeySize
}
d := &digest{
size: hashSize,
keyLen: len(key),
}
copy(d.key[:], key)
d.Reset()
return d, nil
}
func checkSum(sum *[Size]byte, hashSize int, data []byte) {
h := iv
h[0] ^= uint64(hashSize) | (1 << 16) | (1 << 24)
var c [2]uint64
if length := len(data); length > BlockSize {
n := length &^ (BlockSize - 1)
if length == n {
n -= BlockSize
}
hashBlocks(&h, &c, 0, data[:n])
data = data[n:]
}
var block [BlockSize]byte
offset := copy(block[:], data)
remaining := uint64(BlockSize - offset)
if c[0] < remaining {
c[1]--
}
c[0] -= remaining
hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
for i, v := range h[:(hashSize+7)/8] {
binary.LittleEndian.PutUint64(sum[8*i:], v)
}
}
type digest struct {
h [8]uint64
c [2]uint64
size int
block [BlockSize]byte
offset int
key [BlockSize]byte
keyLen int
}
const (
magic = "b2b"
marshaledSize = len(magic) + 8*8 + 2*8 + 1 + BlockSize + 1
)
func (d *digest) MarshalBinary() ([]byte, error) {
if d.keyLen != 0 {
return nil, errors.New("crypto/blake2b: cannot marshal MACs")
}
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
for i := 0; i < 8; i++ {
b = appendUint64(b, d.h[i])
}
b = appendUint64(b, d.c[0])
b = appendUint64(b, d.c[1])
// Maximum value for size is 64
b = append(b, byte(d.size))
b = append(b, d.block[:]...)
b = append(b, byte(d.offset))
return b, nil
}
func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("crypto/blake2b: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("crypto/blake2b: invalid hash state size")
}
b = b[len(magic):]
for i := 0; i < 8; i++ {
b, d.h[i] = consumeUint64(b)
}
b, d.c[0] = consumeUint64(b)
b, d.c[1] = consumeUint64(b)
d.size = int(b[0])
b = b[1:]
copy(d.block[:], b[:BlockSize])
b = b[BlockSize:]
d.offset = int(b[0])
return nil
}
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Size() int { return d.size }
func (d *digest) Reset() {
d.h = iv
d.h[0] ^= uint64(d.size) | (uint64(d.keyLen) << 8) | (1 << 16) | (1 << 24)
d.offset, d.c[0], d.c[1] = 0, 0, 0
if d.keyLen > 0 {
d.block = d.key
d.offset = BlockSize
}
}
func (d *digest) Write(p []byte) (n int, err error) {
n = len(p)
if d.offset > 0 {
remaining := BlockSize - d.offset
if n <= remaining {
d.offset += copy(d.block[d.offset:], p)
return
}
copy(d.block[d.offset:], p[:remaining])
hashBlocks(&d.h, &d.c, 0, d.block[:])
d.offset = 0
p = p[remaining:]
}
if length := len(p); length > BlockSize {
nn := length &^ (BlockSize - 1)
if length == nn {
nn -= BlockSize
}
hashBlocks(&d.h, &d.c, 0, p[:nn])
p = p[nn:]
}
if len(p) > 0 {
d.offset += copy(d.block[:], p)
}
return
}
func (d *digest) Sum(sum []byte) []byte {
var hash [Size]byte
d.finalize(&hash)
return append(sum, hash[:d.size]...)
}
func (d *digest) finalize(hash *[Size]byte) {
var block [BlockSize]byte
copy(block[:], d.block[:d.offset])
remaining := uint64(BlockSize - d.offset)
c := d.c
if c[0] < remaining {
c[1]--
}
c[0] -= remaining
h := d.h
hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
for i, v := range h {
binary.LittleEndian.PutUint64(hash[8*i:], v)
}
}
func appendUint64(b []byte, x uint64) []byte {
var a [8]byte
binary.BigEndian.PutUint64(a[:], x)
return append(b, a[:]...)
}
func appendUint32(b []byte, x uint32) []byte {
var a [4]byte
binary.BigEndian.PutUint32(a[:], x)
return append(b, a[:]...)
}
func consumeUint64(b []byte) ([]byte, uint64) {
x := binary.BigEndian.Uint64(b)
return b[8:], x
}
func consumeUint32(b []byte) ([]byte, uint32) {
x := binary.BigEndian.Uint32(b)
return b[4:], x
}

View File

@ -0,0 +1,38 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.7 && amd64 && gc && !purego
// +build go1.7,amd64,gc,!purego
package blake2b
import "golang.org/x/sys/cpu"
func init() {
useAVX2 = cpu.X86.HasAVX2
useAVX = cpu.X86.HasAVX
useSSE4 = cpu.X86.HasSSE41
}
//go:noescape
func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
//go:noescape
func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
//go:noescape
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
switch {
case useAVX2:
hashBlocksAVX2(h, c, flag, blocks)
case useAVX:
hashBlocksAVX(h, c, flag, blocks)
case useSSE4:
hashBlocksSSE4(h, c, flag, blocks)
default:
hashBlocksGeneric(h, c, flag, blocks)
}
}

745
vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s generated vendored Normal file
View File

@ -0,0 +1,745 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.7 && amd64 && gc && !purego
// +build go1.7,amd64,gc,!purego
#include "textflag.h"
DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b
DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1
GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32
DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1
DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b
DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179
GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32
DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403
DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32
DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302
DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32
DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16
DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16
DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1
DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16
DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16
DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16
DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16
#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39
#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93
#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e
#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93
#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39
#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \
VPADDQ m0, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFD $-79, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPSHUFB c40, Y1, Y1; \
VPADDQ m1, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFB c48, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPADDQ Y1, Y1, t; \
VPSRLQ $63, Y1, Y1; \
VPXOR t, Y1, Y1; \
VPERMQ_0x39_Y1_Y1; \
VPERMQ_0x4E_Y2_Y2; \
VPERMQ_0x93_Y3_Y3; \
VPADDQ m2, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFD $-79, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPSHUFB c40, Y1, Y1; \
VPADDQ m3, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFB c48, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPADDQ Y1, Y1, t; \
VPSRLQ $63, Y1, Y1; \
VPXOR t, Y1, Y1; \
VPERMQ_0x39_Y3_Y3; \
VPERMQ_0x4E_Y2_Y2; \
VPERMQ_0x93_Y1_Y1
#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E
#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26
#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E
#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36
#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E
#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n
#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n
#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n
#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n
#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n
#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01
#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01
#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01
#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01
#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01
#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01
#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8
#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01
// load msg: Y12 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \
VMOVQ_SI_X12(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X12(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y12, Y12
// load msg: Y13 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \
VMOVQ_SI_X13(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X13(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y13, Y13
// load msg: Y14 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \
VMOVQ_SI_X14(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X14(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y14, Y14
// load msg: Y15 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \
VMOVQ_SI_X15(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X15(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \
VMOVQ_SI_X12_0; \
VMOVQ_SI_X11(4*8); \
VPINSRQ_1_SI_X12(2*8); \
VPINSRQ_1_SI_X11(6*8); \
VINSERTI128 $1, X11, Y12, Y12; \
LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \
LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \
LOAD_MSG_AVX2_Y15(9, 11, 13, 15)
#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \
LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \
LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \
VMOVQ_SI_X11(11*8); \
VPSHUFD $0x4E, 0*8(SI), X14; \
VPINSRQ_1_SI_X11(5*8); \
VINSERTI128 $1, X11, Y14, Y14; \
LOAD_MSG_AVX2_Y15(12, 2, 7, 3)
#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \
VMOVQ_SI_X11(5*8); \
VMOVDQU 11*8(SI), X12; \
VPINSRQ_1_SI_X11(15*8); \
VINSERTI128 $1, X11, Y12, Y12; \
VMOVQ_SI_X13(8*8); \
VMOVQ_SI_X11(2*8); \
VPINSRQ_1_SI_X13_0; \
VPINSRQ_1_SI_X11(13*8); \
VINSERTI128 $1, X11, Y13, Y13; \
LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \
LOAD_MSG_AVX2_Y15(14, 6, 1, 4)
#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \
LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \
LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \
LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \
VMOVQ_SI_X15(6*8); \
VMOVQ_SI_X11_0; \
VPINSRQ_1_SI_X15(10*8); \
VPINSRQ_1_SI_X11(8*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \
LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \
VMOVQ_SI_X13_0; \
VMOVQ_SI_X11(4*8); \
VPINSRQ_1_SI_X13(7*8); \
VPINSRQ_1_SI_X11(15*8); \
VINSERTI128 $1, X11, Y13, Y13; \
LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \
LOAD_MSG_AVX2_Y15(1, 12, 8, 13)
#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \
VMOVQ_SI_X12(2*8); \
VMOVQ_SI_X11_0; \
VPINSRQ_1_SI_X12(6*8); \
VPINSRQ_1_SI_X11(8*8); \
VINSERTI128 $1, X11, Y12, Y12; \
LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \
LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \
LOAD_MSG_AVX2_Y15(13, 5, 14, 9)
#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \
LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \
LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \
VMOVQ_SI_X14_0; \
VPSHUFD $0x4E, 8*8(SI), X11; \
VPINSRQ_1_SI_X14(6*8); \
VINSERTI128 $1, X11, Y14, Y14; \
LOAD_MSG_AVX2_Y15(7, 3, 2, 11)
#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \
LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \
LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \
LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \
VMOVQ_SI_X15_0; \
VMOVQ_SI_X11(6*8); \
VPINSRQ_1_SI_X15(4*8); \
VPINSRQ_1_SI_X11(10*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \
VMOVQ_SI_X12(6*8); \
VMOVQ_SI_X11(11*8); \
VPINSRQ_1_SI_X12(14*8); \
VPINSRQ_1_SI_X11_0; \
VINSERTI128 $1, X11, Y12, Y12; \
LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \
VMOVQ_SI_X11(1*8); \
VMOVDQU 12*8(SI), X14; \
VPINSRQ_1_SI_X11(10*8); \
VINSERTI128 $1, X11, Y14, Y14; \
VMOVQ_SI_X15(2*8); \
VMOVDQU 4*8(SI), X11; \
VPINSRQ_1_SI_X15(7*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \
LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \
VMOVQ_SI_X13(2*8); \
VPSHUFD $0x4E, 5*8(SI), X11; \
VPINSRQ_1_SI_X13(4*8); \
VINSERTI128 $1, X11, Y13, Y13; \
LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \
VMOVQ_SI_X15(11*8); \
VMOVQ_SI_X11(12*8); \
VPINSRQ_1_SI_X15(14*8); \
VPINSRQ_1_SI_X11_0; \
VINSERTI128 $1, X11, Y15, Y15
// func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
TEXT ·hashBlocksAVX2(SB), 4, $320-48 // frame size = 288 + 32 byte alignment
MOVQ h+0(FP), AX
MOVQ c+8(FP), BX
MOVQ flag+16(FP), CX
MOVQ blocks_base+24(FP), SI
MOVQ blocks_len+32(FP), DI
MOVQ SP, DX
ADDQ $31, DX
ANDQ $~31, DX
MOVQ CX, 16(DX)
XORQ CX, CX
MOVQ CX, 24(DX)
VMOVDQU ·AVX2_c40<>(SB), Y4
VMOVDQU ·AVX2_c48<>(SB), Y5
VMOVDQU 0(AX), Y8
VMOVDQU 32(AX), Y9
VMOVDQU ·AVX2_iv0<>(SB), Y6
VMOVDQU ·AVX2_iv1<>(SB), Y7
MOVQ 0(BX), R8
MOVQ 8(BX), R9
MOVQ R9, 8(DX)
loop:
ADDQ $128, R8
MOVQ R8, 0(DX)
CMPQ R8, $128
JGE noinc
INCQ R9
MOVQ R9, 8(DX)
noinc:
VMOVDQA Y8, Y0
VMOVDQA Y9, Y1
VMOVDQA Y6, Y2
VPXOR 0(DX), Y7, Y3
LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15()
VMOVDQA Y12, 32(DX)
VMOVDQA Y13, 64(DX)
VMOVDQA Y14, 96(DX)
VMOVDQA Y15, 128(DX)
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3()
VMOVDQA Y12, 160(DX)
VMOVDQA Y13, 192(DX)
VMOVDQA Y14, 224(DX)
VMOVDQA Y15, 256(DX)
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
ROUND_AVX2(32(DX), 64(DX), 96(DX), 128(DX), Y10, Y4, Y5)
ROUND_AVX2(160(DX), 192(DX), 224(DX), 256(DX), Y10, Y4, Y5)
VPXOR Y0, Y8, Y8
VPXOR Y1, Y9, Y9
VPXOR Y2, Y8, Y8
VPXOR Y3, Y9, Y9
LEAQ 128(SI), SI
SUBQ $128, DI
JNE loop
MOVQ R8, 0(BX)
MOVQ R9, 8(BX)
VMOVDQU Y8, 0(AX)
VMOVDQU Y9, 32(AX)
VZEROUPPER
RET
#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA
#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB
#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF
#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD
#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE
#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7
#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF
#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7
#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF
#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7
#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7
#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF
#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF
#define SHUFFLE_AVX() \
VMOVDQA X6, X13; \
VMOVDQA X2, X14; \
VMOVDQA X4, X6; \
VPUNPCKLQDQ_X13_X13_X15; \
VMOVDQA X5, X4; \
VMOVDQA X6, X5; \
VPUNPCKHQDQ_X15_X7_X6; \
VPUNPCKLQDQ_X7_X7_X15; \
VPUNPCKHQDQ_X15_X13_X7; \
VPUNPCKLQDQ_X3_X3_X15; \
VPUNPCKHQDQ_X15_X2_X2; \
VPUNPCKLQDQ_X14_X14_X15; \
VPUNPCKHQDQ_X15_X3_X3; \
#define SHUFFLE_AVX_INV() \
VMOVDQA X2, X13; \
VMOVDQA X4, X14; \
VPUNPCKLQDQ_X2_X2_X15; \
VMOVDQA X5, X4; \
VPUNPCKHQDQ_X15_X3_X2; \
VMOVDQA X14, X5; \
VPUNPCKLQDQ_X3_X3_X15; \
VMOVDQA X6, X14; \
VPUNPCKHQDQ_X15_X13_X3; \
VPUNPCKLQDQ_X7_X7_X15; \
VPUNPCKHQDQ_X15_X6_X6; \
VPUNPCKLQDQ_X14_X14_X15; \
VPUNPCKHQDQ_X15_X7_X7; \
#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
VPADDQ m0, v0, v0; \
VPADDQ v2, v0, v0; \
VPADDQ m1, v1, v1; \
VPADDQ v3, v1, v1; \
VPXOR v0, v6, v6; \
VPXOR v1, v7, v7; \
VPSHUFD $-79, v6, v6; \
VPSHUFD $-79, v7, v7; \
VPADDQ v6, v4, v4; \
VPADDQ v7, v5, v5; \
VPXOR v4, v2, v2; \
VPXOR v5, v3, v3; \
VPSHUFB c40, v2, v2; \
VPSHUFB c40, v3, v3; \
VPADDQ m2, v0, v0; \
VPADDQ v2, v0, v0; \
VPADDQ m3, v1, v1; \
VPADDQ v3, v1, v1; \
VPXOR v0, v6, v6; \
VPXOR v1, v7, v7; \
VPSHUFB c48, v6, v6; \
VPSHUFB c48, v7, v7; \
VPADDQ v6, v4, v4; \
VPADDQ v7, v5, v5; \
VPXOR v4, v2, v2; \
VPXOR v5, v3, v3; \
VPADDQ v2, v2, t0; \
VPSRLQ $63, v2, v2; \
VPXOR t0, v2, v2; \
VPADDQ v3, v3, t0; \
VPSRLQ $63, v3, v3; \
VPXOR t0, v3, v3
// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7)
// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0
#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \
VMOVQ_SI_X12(i0*8); \
VMOVQ_SI_X13(i2*8); \
VMOVQ_SI_X14(i4*8); \
VMOVQ_SI_X15(i6*8); \
VPINSRQ_1_SI_X12(i1*8); \
VPINSRQ_1_SI_X13(i3*8); \
VPINSRQ_1_SI_X14(i5*8); \
VPINSRQ_1_SI_X15(i7*8)
// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7)
#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \
VMOVQ_SI_X12_0; \
VMOVQ_SI_X13(4*8); \
VMOVQ_SI_X14(1*8); \
VMOVQ_SI_X15(5*8); \
VPINSRQ_1_SI_X12(2*8); \
VPINSRQ_1_SI_X13(6*8); \
VPINSRQ_1_SI_X14(3*8); \
VPINSRQ_1_SI_X15(7*8)
// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3)
#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \
VPSHUFD $0x4E, 0*8(SI), X12; \
VMOVQ_SI_X13(11*8); \
VMOVQ_SI_X14(12*8); \
VMOVQ_SI_X15(7*8); \
VPINSRQ_1_SI_X13(5*8); \
VPINSRQ_1_SI_X14(2*8); \
VPINSRQ_1_SI_X15(3*8)
// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13)
#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \
VMOVDQU 11*8(SI), X12; \
VMOVQ_SI_X13(5*8); \
VMOVQ_SI_X14(8*8); \
VMOVQ_SI_X15(2*8); \
VPINSRQ_1_SI_X13(15*8); \
VPINSRQ_1_SI_X14_0; \
VPINSRQ_1_SI_X15(13*8)
// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8)
#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \
VMOVQ_SI_X12(2*8); \
VMOVQ_SI_X13(4*8); \
VMOVQ_SI_X14(6*8); \
VMOVQ_SI_X15_0; \
VPINSRQ_1_SI_X12(5*8); \
VPINSRQ_1_SI_X13(15*8); \
VPINSRQ_1_SI_X14(10*8); \
VPINSRQ_1_SI_X15(8*8)
// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15)
#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \
VMOVQ_SI_X12(9*8); \
VMOVQ_SI_X13(2*8); \
VMOVQ_SI_X14_0; \
VMOVQ_SI_X15(4*8); \
VPINSRQ_1_SI_X12(5*8); \
VPINSRQ_1_SI_X13(10*8); \
VPINSRQ_1_SI_X14(7*8); \
VPINSRQ_1_SI_X15(15*8)
// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3)
#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \
VMOVQ_SI_X12(2*8); \
VMOVQ_SI_X13_0; \
VMOVQ_SI_X14(12*8); \
VMOVQ_SI_X15(11*8); \
VPINSRQ_1_SI_X12(6*8); \
VPINSRQ_1_SI_X13(8*8); \
VPINSRQ_1_SI_X14(10*8); \
VPINSRQ_1_SI_X15(3*8)
// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11)
#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \
MOVQ 0*8(SI), X12; \
VPSHUFD $0x4E, 8*8(SI), X13; \
MOVQ 7*8(SI), X14; \
MOVQ 2*8(SI), X15; \
VPINSRQ_1_SI_X12(6*8); \
VPINSRQ_1_SI_X14(3*8); \
VPINSRQ_1_SI_X15(11*8)
// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8)
#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \
MOVQ 6*8(SI), X12; \
MOVQ 11*8(SI), X13; \
MOVQ 15*8(SI), X14; \
MOVQ 3*8(SI), X15; \
VPINSRQ_1_SI_X12(14*8); \
VPINSRQ_1_SI_X13_0; \
VPINSRQ_1_SI_X14(9*8); \
VPINSRQ_1_SI_X15(8*8)
// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10)
#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \
MOVQ 5*8(SI), X12; \
MOVQ 8*8(SI), X13; \
MOVQ 0*8(SI), X14; \
MOVQ 6*8(SI), X15; \
VPINSRQ_1_SI_X12(15*8); \
VPINSRQ_1_SI_X13(2*8); \
VPINSRQ_1_SI_X14(4*8); \
VPINSRQ_1_SI_X15(10*8)
// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5)
#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \
VMOVDQU 12*8(SI), X12; \
MOVQ 1*8(SI), X13; \
MOVQ 2*8(SI), X14; \
VPINSRQ_1_SI_X13(10*8); \
VPINSRQ_1_SI_X14(7*8); \
VMOVDQU 4*8(SI), X15
// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0)
#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \
MOVQ 15*8(SI), X12; \
MOVQ 3*8(SI), X13; \
MOVQ 11*8(SI), X14; \
MOVQ 12*8(SI), X15; \
VPINSRQ_1_SI_X12(9*8); \
VPINSRQ_1_SI_X13(13*8); \
VPINSRQ_1_SI_X14(14*8); \
VPINSRQ_1_SI_X15_0
// func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
MOVQ h+0(FP), AX
MOVQ c+8(FP), BX
MOVQ flag+16(FP), CX
MOVQ blocks_base+24(FP), SI
MOVQ blocks_len+32(FP), DI
MOVQ SP, R10
ADDQ $15, R10
ANDQ $~15, R10
VMOVDQU ·AVX_c40<>(SB), X0
VMOVDQU ·AVX_c48<>(SB), X1
VMOVDQA X0, X8
VMOVDQA X1, X9
VMOVDQU ·AVX_iv3<>(SB), X0
VMOVDQA X0, 0(R10)
XORQ CX, 0(R10) // 0(R10) = ·AVX_iv3 ^ (CX || 0)
VMOVDQU 0(AX), X10
VMOVDQU 16(AX), X11
VMOVDQU 32(AX), X2
VMOVDQU 48(AX), X3
MOVQ 0(BX), R8
MOVQ 8(BX), R9
loop:
ADDQ $128, R8
CMPQ R8, $128
JGE noinc
INCQ R9
noinc:
VMOVQ_R8_X15
VPINSRQ_1_R9_X15
VMOVDQA X10, X0
VMOVDQA X11, X1
VMOVDQU ·AVX_iv0<>(SB), X4
VMOVDQU ·AVX_iv1<>(SB), X5
VMOVDQU ·AVX_iv2<>(SB), X6
VPXOR X15, X6, X6
VMOVDQA 0(R10), X7
LOAD_MSG_AVX_0_2_4_6_1_3_5_7()
VMOVDQA X12, 16(R10)
VMOVDQA X13, 32(R10)
VMOVDQA X14, 48(R10)
VMOVDQA X15, 64(R10)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15)
VMOVDQA X12, 80(R10)
VMOVDQA X13, 96(R10)
VMOVDQA X14, 112(R10)
VMOVDQA X15, 128(R10)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6)
VMOVDQA X12, 144(R10)
VMOVDQA X13, 160(R10)
VMOVDQA X14, 176(R10)
VMOVDQA X15, 192(R10)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX_1_0_11_5_12_2_7_3()
VMOVDQA X12, 208(R10)
VMOVDQA X13, 224(R10)
VMOVDQA X14, 240(R10)
VMOVDQA X15, 256(R10)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX_11_12_5_15_8_0_2_13()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX_2_5_4_15_6_10_0_8()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX_9_5_2_10_0_7_4_15()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX_2_6_0_8_12_10_11_3()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX_0_6_9_8_7_3_2_11()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX_5_15_8_2_0_4_6_10()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX_6_14_11_0_15_9_3_8()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX_12_13_1_10_2_7_4_5()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX()
LOAD_MSG_AVX_15_9_3_13_11_14_12_0()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(R10), 32(R10), 48(R10), 64(R10), X15, X8, X9)
SHUFFLE_AVX()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(R10), 96(R10), 112(R10), 128(R10), X15, X8, X9)
SHUFFLE_AVX_INV()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(R10), 160(R10), 176(R10), 192(R10), X15, X8, X9)
SHUFFLE_AVX()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(R10), 224(R10), 240(R10), 256(R10), X15, X8, X9)
SHUFFLE_AVX_INV()
VMOVDQU 32(AX), X14
VMOVDQU 48(AX), X15
VPXOR X0, X10, X10
VPXOR X1, X11, X11
VPXOR X2, X14, X14
VPXOR X3, X15, X15
VPXOR X4, X10, X10
VPXOR X5, X11, X11
VPXOR X6, X14, X2
VPXOR X7, X15, X3
VMOVDQU X2, 32(AX)
VMOVDQU X3, 48(AX)
LEAQ 128(SI), SI
SUBQ $128, DI
JNE loop
VMOVDQU X10, 0(AX)
VMOVDQU X11, 16(AX)
MOVQ R8, 0(BX)
MOVQ R9, 8(BX)
VZEROUPPER
RET

25
vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.7 && amd64 && gc && !purego
// +build !go1.7,amd64,gc,!purego
package blake2b
import "golang.org/x/sys/cpu"
func init() {
useSSE4 = cpu.X86.HasSSE41
}
//go:noescape
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
if useSSE4 {
hashBlocksSSE4(h, c, flag, blocks)
} else {
hashBlocksGeneric(h, c, flag, blocks)
}
}

279
vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s generated vendored Normal file
View File

@ -0,0 +1,279 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
#include "textflag.h"
DATA ·iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
DATA ·iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
GLOBL ·iv0<>(SB), (NOPTR+RODATA), $16
DATA ·iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
DATA ·iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
GLOBL ·iv1<>(SB), (NOPTR+RODATA), $16
DATA ·iv2<>+0x00(SB)/8, $0x510e527fade682d1
DATA ·iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
GLOBL ·iv2<>(SB), (NOPTR+RODATA), $16
DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
MOVO v4, t1; \
MOVO v5, v4; \
MOVO t1, v5; \
MOVO v6, t1; \
PUNPCKLQDQ v6, t2; \
PUNPCKHQDQ v7, v6; \
PUNPCKHQDQ t2, v6; \
PUNPCKLQDQ v7, t2; \
MOVO t1, v7; \
MOVO v2, t1; \
PUNPCKHQDQ t2, v7; \
PUNPCKLQDQ v3, t2; \
PUNPCKHQDQ t2, v2; \
PUNPCKLQDQ t1, t2; \
PUNPCKHQDQ t2, v3
#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
MOVO v4, t1; \
MOVO v5, v4; \
MOVO t1, v5; \
MOVO v2, t1; \
PUNPCKLQDQ v2, t2; \
PUNPCKHQDQ v3, v2; \
PUNPCKHQDQ t2, v2; \
PUNPCKLQDQ v3, t2; \
MOVO t1, v3; \
MOVO v6, t1; \
PUNPCKHQDQ t2, v3; \
PUNPCKLQDQ v7, t2; \
PUNPCKHQDQ t2, v6; \
PUNPCKLQDQ t1, t2; \
PUNPCKHQDQ t2, v7
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
PADDQ m0, v0; \
PADDQ m1, v1; \
PADDQ v2, v0; \
PADDQ v3, v1; \
PXOR v0, v6; \
PXOR v1, v7; \
PSHUFD $0xB1, v6, v6; \
PSHUFD $0xB1, v7, v7; \
PADDQ v6, v4; \
PADDQ v7, v5; \
PXOR v4, v2; \
PXOR v5, v3; \
PSHUFB c40, v2; \
PSHUFB c40, v3; \
PADDQ m2, v0; \
PADDQ m3, v1; \
PADDQ v2, v0; \
PADDQ v3, v1; \
PXOR v0, v6; \
PXOR v1, v7; \
PSHUFB c48, v6; \
PSHUFB c48, v7; \
PADDQ v6, v4; \
PADDQ v7, v5; \
PXOR v4, v2; \
PXOR v5, v3; \
MOVOU v2, t0; \
PADDQ v2, t0; \
PSRLQ $63, v2; \
PXOR t0, v2; \
MOVOU v3, t0; \
PADDQ v3, t0; \
PSRLQ $63, v3; \
PXOR t0, v3
#define LOAD_MSG(m0, m1, m2, m3, src, i0, i1, i2, i3, i4, i5, i6, i7) \
MOVQ i0*8(src), m0; \
PINSRQ $1, i1*8(src), m0; \
MOVQ i2*8(src), m1; \
PINSRQ $1, i3*8(src), m1; \
MOVQ i4*8(src), m2; \
PINSRQ $1, i5*8(src), m2; \
MOVQ i6*8(src), m3; \
PINSRQ $1, i7*8(src), m3
// func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
TEXT ·hashBlocksSSE4(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
MOVQ h+0(FP), AX
MOVQ c+8(FP), BX
MOVQ flag+16(FP), CX
MOVQ blocks_base+24(FP), SI
MOVQ blocks_len+32(FP), DI
MOVQ SP, R10
ADDQ $15, R10
ANDQ $~15, R10
MOVOU ·iv3<>(SB), X0
MOVO X0, 0(R10)
XORQ CX, 0(R10) // 0(R10) = ·iv3 ^ (CX || 0)
MOVOU ·c40<>(SB), X13
MOVOU ·c48<>(SB), X14
MOVOU 0(AX), X12
MOVOU 16(AX), X15
MOVQ 0(BX), R8
MOVQ 8(BX), R9
loop:
ADDQ $128, R8
CMPQ R8, $128
JGE noinc
INCQ R9
noinc:
MOVQ R8, X8
PINSRQ $1, R9, X8
MOVO X12, X0
MOVO X15, X1
MOVOU 32(AX), X2
MOVOU 48(AX), X3
MOVOU ·iv0<>(SB), X4
MOVOU ·iv1<>(SB), X5
MOVOU ·iv2<>(SB), X6
PXOR X8, X6
MOVO 0(R10), X7
LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7)
MOVO X8, 16(R10)
MOVO X9, 32(R10)
MOVO X10, 48(R10)
MOVO X11, 64(R10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15)
MOVO X8, 80(R10)
MOVO X9, 96(R10)
MOVO X10, 112(R10)
MOVO X11, 128(R10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6)
MOVO X8, 144(R10)
MOVO X9, 160(R10)
MOVO X10, 176(R10)
MOVO X11, 192(R10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3)
MOVO X8, 208(R10)
MOVO X9, 224(R10)
MOVO X10, 240(R10)
MOVO X11, 256(R10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 16(R10), 32(R10), 48(R10), 64(R10), X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 80(R10), 96(R10), 112(R10), 128(R10), X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 144(R10), 160(R10), 176(R10), 192(R10), X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 208(R10), 224(R10), 240(R10), 256(R10), X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
MOVOU 32(AX), X10
MOVOU 48(AX), X11
PXOR X0, X12
PXOR X1, X15
PXOR X2, X10
PXOR X3, X11
PXOR X4, X12
PXOR X5, X15
PXOR X6, X10
PXOR X7, X11
MOVOU X10, 32(AX)
MOVOU X11, 48(AX)
LEAQ 128(SI), SI
SUBQ $128, DI
JNE loop
MOVOU X12, 0(AX)
MOVOU X15, 16(AX)
MOVQ R8, 0(BX)
MOVQ R9, 8(BX)
RET

182
vendor/golang.org/x/crypto/blake2b/blake2b_generic.go generated vendored Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package blake2b
import (
"encoding/binary"
"math/bits"
)
// the precomputed values for BLAKE2b
// there are 12 16-byte arrays - one for each round
// the entries are calculated from the sigma constants.
var precomputed = [12][16]byte{
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
{11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
{7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
{9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
{2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
{12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
{13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
{6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15}, // equal to the first
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3}, // equal to the second
}
func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
var m [16]uint64
c0, c1 := c[0], c[1]
for i := 0; i < len(blocks); {
c0 += BlockSize
if c0 < BlockSize {
c1++
}
v0, v1, v2, v3, v4, v5, v6, v7 := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]
v8, v9, v10, v11, v12, v13, v14, v15 := iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]
v12 ^= c0
v13 ^= c1
v14 ^= flag
for j := range m {
m[j] = binary.LittleEndian.Uint64(blocks[i:])
i += 8
}
for j := range precomputed {
s := &(precomputed[j])
v0 += m[s[0]]
v0 += v4
v12 ^= v0
v12 = bits.RotateLeft64(v12, -32)
v8 += v12
v4 ^= v8
v4 = bits.RotateLeft64(v4, -24)
v1 += m[s[1]]
v1 += v5
v13 ^= v1
v13 = bits.RotateLeft64(v13, -32)
v9 += v13
v5 ^= v9
v5 = bits.RotateLeft64(v5, -24)
v2 += m[s[2]]
v2 += v6
v14 ^= v2
v14 = bits.RotateLeft64(v14, -32)
v10 += v14
v6 ^= v10
v6 = bits.RotateLeft64(v6, -24)
v3 += m[s[3]]
v3 += v7
v15 ^= v3
v15 = bits.RotateLeft64(v15, -32)
v11 += v15
v7 ^= v11
v7 = bits.RotateLeft64(v7, -24)
v0 += m[s[4]]
v0 += v4
v12 ^= v0
v12 = bits.RotateLeft64(v12, -16)
v8 += v12
v4 ^= v8
v4 = bits.RotateLeft64(v4, -63)
v1 += m[s[5]]
v1 += v5
v13 ^= v1
v13 = bits.RotateLeft64(v13, -16)
v9 += v13
v5 ^= v9
v5 = bits.RotateLeft64(v5, -63)
v2 += m[s[6]]
v2 += v6
v14 ^= v2
v14 = bits.RotateLeft64(v14, -16)
v10 += v14
v6 ^= v10
v6 = bits.RotateLeft64(v6, -63)
v3 += m[s[7]]
v3 += v7
v15 ^= v3
v15 = bits.RotateLeft64(v15, -16)
v11 += v15
v7 ^= v11
v7 = bits.RotateLeft64(v7, -63)
v0 += m[s[8]]
v0 += v5
v15 ^= v0
v15 = bits.RotateLeft64(v15, -32)
v10 += v15
v5 ^= v10
v5 = bits.RotateLeft64(v5, -24)
v1 += m[s[9]]
v1 += v6
v12 ^= v1
v12 = bits.RotateLeft64(v12, -32)
v11 += v12
v6 ^= v11
v6 = bits.RotateLeft64(v6, -24)
v2 += m[s[10]]
v2 += v7
v13 ^= v2
v13 = bits.RotateLeft64(v13, -32)
v8 += v13
v7 ^= v8
v7 = bits.RotateLeft64(v7, -24)
v3 += m[s[11]]
v3 += v4
v14 ^= v3
v14 = bits.RotateLeft64(v14, -32)
v9 += v14
v4 ^= v9
v4 = bits.RotateLeft64(v4, -24)
v0 += m[s[12]]
v0 += v5
v15 ^= v0
v15 = bits.RotateLeft64(v15, -16)
v10 += v15
v5 ^= v10
v5 = bits.RotateLeft64(v5, -63)
v1 += m[s[13]]
v1 += v6
v12 ^= v1
v12 = bits.RotateLeft64(v12, -16)
v11 += v12
v6 ^= v11
v6 = bits.RotateLeft64(v6, -63)
v2 += m[s[14]]
v2 += v7
v13 ^= v2
v13 = bits.RotateLeft64(v13, -16)
v8 += v13
v7 ^= v8
v7 = bits.RotateLeft64(v7, -63)
v3 += m[s[15]]
v3 += v4
v14 ^= v3
v14 = bits.RotateLeft64(v14, -16)
v9 += v14
v4 ^= v9
v4 = bits.RotateLeft64(v4, -63)
}
h[0] ^= v0 ^ v8
h[1] ^= v1 ^ v9
h[2] ^= v2 ^ v10
h[3] ^= v3 ^ v11
h[4] ^= v4 ^ v12
h[5] ^= v5 ^ v13
h[6] ^= v6 ^ v14
h[7] ^= v7 ^ v15
}
c[0], c[1] = c0, c1
}

12
vendor/golang.org/x/crypto/blake2b/blake2b_ref.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !amd64 || purego || !gc
// +build !amd64 purego !gc
package blake2b
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
hashBlocksGeneric(h, c, flag, blocks)
}

177
vendor/golang.org/x/crypto/blake2b/blake2x.go generated vendored Normal file
View File

@ -0,0 +1,177 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package blake2b
import (
"encoding/binary"
"errors"
"io"
)
// XOF defines the interface to hash functions that
// support arbitrary-length output.
type XOF interface {
// Write absorbs more data into the hash's state. It panics if called
// after Read.
io.Writer
// Read reads more output from the hash. It returns io.EOF if the limit
// has been reached.
io.Reader
// Clone returns a copy of the XOF in its current state.
Clone() XOF
// Reset resets the XOF to its initial state.
Reset()
}
// OutputLengthUnknown can be used as the size argument to NewXOF to indicate
// the length of the output is not known in advance.
const OutputLengthUnknown = 0
// magicUnknownOutputLength is a magic value for the output size that indicates
// an unknown number of output bytes.
const magicUnknownOutputLength = (1 << 32) - 1
// maxOutputLength is the absolute maximum number of bytes to produce when the
// number of output bytes is unknown.
const maxOutputLength = (1 << 32) * 64
// NewXOF creates a new variable-output-length hash. The hash either produce a
// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
// (size == OutputLengthUnknown). In the latter case, an absolute limit of
// 256GiB applies.
//
// A non-nil key turns the hash into a MAC. The key must between
// zero and 32 bytes long.
func NewXOF(size uint32, key []byte) (XOF, error) {
if len(key) > Size {
return nil, errKeySize
}
if size == magicUnknownOutputLength {
// 2^32-1 indicates an unknown number of bytes and thus isn't a
// valid length.
return nil, errors.New("blake2b: XOF length too large")
}
if size == OutputLengthUnknown {
size = magicUnknownOutputLength
}
x := &xof{
d: digest{
size: Size,
keyLen: len(key),
},
length: size,
}
copy(x.d.key[:], key)
x.Reset()
return x, nil
}
type xof struct {
d digest
length uint32
remaining uint64
cfg, root, block [Size]byte
offset int
nodeOffset uint32
readMode bool
}
func (x *xof) Write(p []byte) (n int, err error) {
if x.readMode {
panic("blake2b: write to XOF after read")
}
return x.d.Write(p)
}
func (x *xof) Clone() XOF {
clone := *x
return &clone
}
func (x *xof) Reset() {
x.cfg[0] = byte(Size)
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length
x.cfg[17] = byte(Size) // inner hash size
x.d.Reset()
x.d.h[1] ^= uint64(x.length) << 32
x.remaining = uint64(x.length)
if x.remaining == magicUnknownOutputLength {
x.remaining = maxOutputLength
}
x.offset, x.nodeOffset = 0, 0
x.readMode = false
}
func (x *xof) Read(p []byte) (n int, err error) {
if !x.readMode {
x.d.finalize(&x.root)
x.readMode = true
}
if x.remaining == 0 {
return 0, io.EOF
}
n = len(p)
if uint64(n) > x.remaining {
n = int(x.remaining)
p = p[:n]
}
if x.offset > 0 {
blockRemaining := Size - x.offset
if n < blockRemaining {
x.offset += copy(p, x.block[x.offset:])
x.remaining -= uint64(n)
return
}
copy(p, x.block[x.offset:])
p = p[blockRemaining:]
x.offset = 0
x.remaining -= uint64(blockRemaining)
}
for len(p) >= Size {
binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
x.nodeOffset++
x.d.initConfig(&x.cfg)
x.d.Write(x.root[:])
x.d.finalize(&x.block)
copy(p, x.block[:])
p = p[Size:]
x.remaining -= uint64(Size)
}
if todo := len(p); todo > 0 {
if x.remaining < uint64(Size) {
x.cfg[0] = byte(x.remaining)
}
binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
x.nodeOffset++
x.d.initConfig(&x.cfg)
x.d.Write(x.root[:])
x.d.finalize(&x.block)
x.offset = copy(p, x.block[:todo])
x.remaining -= uint64(todo)
}
return
}
func (d *digest) initConfig(cfg *[Size]byte) {
d.offset, d.c[0], d.c[1] = 0, 0, 0
for i := range d.h {
d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:])
}
}

33
vendor/golang.org/x/crypto/blake2b/register.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.9
// +build go1.9
package blake2b
import (
"crypto"
"hash"
)
func init() {
newHash256 := func() hash.Hash {
h, _ := New256(nil)
return h
}
newHash384 := func() hash.Hash {
h, _ := New384(nil)
return h
}
newHash512 := func() hash.Hash {
h, _ := New512(nil)
return h
}
crypto.RegisterHash(crypto.BLAKE2b_256, newHash256)
crypto.RegisterHash(crypto.BLAKE2b_384, newHash384)
crypto.RegisterHash(crypto.BLAKE2b_512, newHash512)
}

124
vendor/golang.org/x/crypto/ripemd160/ripemd160.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ripemd160 implements the RIPEMD-160 hash algorithm.
//
// Deprecated: RIPEMD-160 is a legacy hash and should not be used for new
// applications. Also, this package does not and will not provide an optimized
// implementation. Instead, use a modern hash like SHA-256 (from crypto/sha256).
package ripemd160 // import "golang.org/x/crypto/ripemd160"
// RIPEMD-160 is designed by Hans Dobbertin, Antoon Bosselaers, and Bart
// Preneel with specifications available at:
// http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/AB-9601.pdf.
import (
"crypto"
"hash"
)
func init() {
crypto.RegisterHash(crypto.RIPEMD160, New)
}
// The size of the checksum in bytes.
const Size = 20
// The block size of the hash algorithm in bytes.
const BlockSize = 64
const (
_s0 = 0x67452301
_s1 = 0xefcdab89
_s2 = 0x98badcfe
_s3 = 0x10325476
_s4 = 0xc3d2e1f0
)
// digest represents the partial evaluation of a checksum.
type digest struct {
s [5]uint32 // running context
x [BlockSize]byte // temporary buffer
nx int // index into x
tc uint64 // total count of bytes processed
}
func (d *digest) Reset() {
d.s[0], d.s[1], d.s[2], d.s[3], d.s[4] = _s0, _s1, _s2, _s3, _s4
d.nx = 0
d.tc = 0
}
// New returns a new hash.Hash computing the checksum.
func New() hash.Hash {
result := new(digest)
result.Reset()
return result
}
func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.tc += uint64(nn)
if d.nx > 0 {
n := len(p)
if n > BlockSize-d.nx {
n = BlockSize - d.nx
}
for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i]
}
d.nx += n
if d.nx == BlockSize {
_Block(d, d.x[0:])
d.nx = 0
}
p = p[n:]
}
n := _Block(d, p)
p = p[n:]
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
tc := d.tc
var tmp [64]byte
tmp[0] = 0x80
if tc%64 < 56 {
d.Write(tmp[0 : 56-tc%64])
} else {
d.Write(tmp[0 : 64+56-tc%64])
}
// Length in bits.
tc <<= 3
for i := uint(0); i < 8; i++ {
tmp[i] = byte(tc >> (8 * i))
}
d.Write(tmp[0:8])
if d.nx != 0 {
panic("d.nx != 0")
}
var digest [Size]byte
for i, s := range d.s {
digest[i*4] = byte(s)
digest[i*4+1] = byte(s >> 8)
digest[i*4+2] = byte(s >> 16)
digest[i*4+3] = byte(s >> 24)
}
return append(in, digest[:]...)
}

165
vendor/golang.org/x/crypto/ripemd160/ripemd160block.go generated vendored Normal file
View File

@ -0,0 +1,165 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// RIPEMD-160 block step.
// In its own file so that a faster assembly or C version
// can be substituted easily.
package ripemd160
import (
"math/bits"
)
// work buffer indices and roll amounts for one line
var _n = [80]uint{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13,
}
var _r = [80]uint{
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6,
}
// same for the other parallel one
var n_ = [80]uint{
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11,
}
var r_ = [80]uint{
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
}
func _Block(md *digest, p []byte) int {
n := 0
var x [16]uint32
var alpha, beta uint32
for len(p) >= BlockSize {
a, b, c, d, e := md.s[0], md.s[1], md.s[2], md.s[3], md.s[4]
aa, bb, cc, dd, ee := a, b, c, d, e
j := 0
for i := 0; i < 16; i++ {
x[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
j += 4
}
// round 1
i := 0
for i < 16 {
alpha = a + (b ^ c ^ d) + x[_n[i]]
s := int(_r[i])
alpha = bits.RotateLeft32(alpha, s) + e
beta = bits.RotateLeft32(c, 10)
a, b, c, d, e = e, alpha, b, beta, d
// parallel line
alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6
s = int(r_[i])
alpha = bits.RotateLeft32(alpha, s) + ee
beta = bits.RotateLeft32(cc, 10)
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
i++
}
// round 2
for i < 32 {
alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999
s := int(_r[i])
alpha = bits.RotateLeft32(alpha, s) + e
beta = bits.RotateLeft32(c, 10)
a, b, c, d, e = e, alpha, b, beta, d
// parallel line
alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124
s = int(r_[i])
alpha = bits.RotateLeft32(alpha, s) + ee
beta = bits.RotateLeft32(cc, 10)
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
i++
}
// round 3
for i < 48 {
alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1
s := int(_r[i])
alpha = bits.RotateLeft32(alpha, s) + e
beta = bits.RotateLeft32(c, 10)
a, b, c, d, e = e, alpha, b, beta, d
// parallel line
alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3
s = int(r_[i])
alpha = bits.RotateLeft32(alpha, s) + ee
beta = bits.RotateLeft32(cc, 10)
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
i++
}
// round 4
for i < 64 {
alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc
s := int(_r[i])
alpha = bits.RotateLeft32(alpha, s) + e
beta = bits.RotateLeft32(c, 10)
a, b, c, d, e = e, alpha, b, beta, d
// parallel line
alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9
s = int(r_[i])
alpha = bits.RotateLeft32(alpha, s) + ee
beta = bits.RotateLeft32(cc, 10)
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
i++
}
// round 5
for i < 80 {
alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e
s := int(_r[i])
alpha = bits.RotateLeft32(alpha, s) + e
beta = bits.RotateLeft32(c, 10)
a, b, c, d, e = e, alpha, b, beta, d
// parallel line
alpha = aa + (bb ^ cc ^ dd) + x[n_[i]]
s = int(r_[i])
alpha = bits.RotateLeft32(alpha, s) + ee
beta = bits.RotateLeft32(cc, 10)
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
i++
}
// combine results
dd += c + md.s[1]
md.s[1] = md.s[2] + d + ee
md.s[2] = md.s[3] + e + aa
md.s[3] = md.s[4] + a + bb
md.s[4] = md.s[0] + b + cc
md.s[0] = dd
p = p[BlockSize:]
n += BlockSize
}
return n
}

341
vendor/golang.org/x/crypto/twofish/twofish.go generated vendored Normal file
View File

@ -0,0 +1,341 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package twofish implements Bruce Schneier's Twofish encryption algorithm.
//
// Deprecated: Twofish is a legacy cipher and should not be used for new
// applications. Also, this package does not and will not provide an optimized
// implementation. Instead, use AES (from crypto/aes, if necessary in an AEAD
// mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
// golang.org/x/crypto/chacha20poly1305).
package twofish // import "golang.org/x/crypto/twofish"
// Twofish is defined in https://www.schneier.com/paper-twofish-paper.pdf [TWOFISH]
// This code is a port of the LibTom C implementation.
// See http://libtom.org/?page=features&newsitems=5&whatfile=crypt.
// LibTomCrypt is free for all purposes under the public domain.
// It was heavily inspired by the go blowfish package.
import (
"math/bits"
"strconv"
)
// BlockSize is the constant block size of Twofish.
const BlockSize = 16
const mdsPolynomial = 0x169 // x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2
const rsPolynomial = 0x14d // x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3
// A Cipher is an instance of Twofish encryption using a particular key.
type Cipher struct {
s [4][256]uint32
k [40]uint32
}
type KeySizeError int
func (k KeySizeError) Error() string {
return "crypto/twofish: invalid key size " + strconv.Itoa(int(k))
}
// NewCipher creates and returns a Cipher.
// The key argument should be the Twofish key, 16, 24 or 32 bytes.
func NewCipher(key []byte) (*Cipher, error) {
keylen := len(key)
if keylen != 16 && keylen != 24 && keylen != 32 {
return nil, KeySizeError(keylen)
}
// k is the number of 64 bit words in key
k := keylen / 8
// Create the S[..] words
var S [4 * 4]byte
for i := 0; i < k; i++ {
// Computes [y0 y1 y2 y3] = rs . [x0 x1 x2 x3 x4 x5 x6 x7]
for j, rsRow := range rs {
for k, rsVal := range rsRow {
S[4*i+j] ^= gfMult(key[8*i+k], rsVal, rsPolynomial)
}
}
}
// Calculate subkeys
c := new(Cipher)
var tmp [4]byte
for i := byte(0); i < 20; i++ {
// A = h(p * 2x, Me)
for j := range tmp {
tmp[j] = 2 * i
}
A := h(tmp[:], key, 0)
// B = rolc(h(p * (2x + 1), Mo), 8)
for j := range tmp {
tmp[j] = 2*i + 1
}
B := h(tmp[:], key, 1)
B = bits.RotateLeft32(B, 8)
c.k[2*i] = A + B
// K[2i+1] = (A + 2B) <<< 9
c.k[2*i+1] = bits.RotateLeft32(2*B+A, 9)
}
// Calculate sboxes
switch k {
case 2:
for i := range c.s[0] {
c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][byte(i)]^S[0]]^S[4]], 0)
c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][byte(i)]^S[1]]^S[5]], 1)
c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][byte(i)]^S[2]]^S[6]], 2)
c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][byte(i)]^S[3]]^S[7]], 3)
}
case 3:
for i := range c.s[0] {
c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]], 0)
c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[1]]^S[5]]^S[9]], 1)
c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]], 2)
c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[3]]^S[7]]^S[11]], 3)
}
default:
for i := range c.s[0] {
c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]]^S[12]], 0)
c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[1]]^S[5]]^S[9]]^S[13]], 1)
c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]]^S[14]], 2)
c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][sbox[1][byte(i)]^S[3]]^S[7]]^S[11]]^S[15]], 3)
}
}
return c, nil
}
// BlockSize returns the Twofish block size, 16 bytes.
func (c *Cipher) BlockSize() int { return BlockSize }
// store32l stores src in dst in little-endian form.
func store32l(dst []byte, src uint32) {
dst[0] = byte(src)
dst[1] = byte(src >> 8)
dst[2] = byte(src >> 16)
dst[3] = byte(src >> 24)
return
}
// load32l reads a little-endian uint32 from src.
func load32l(src []byte) uint32 {
return uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
}
// The RS matrix. See [TWOFISH] 4.3
var rs = [4][8]byte{
{0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E},
{0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5},
{0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19},
{0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03},
}
// sbox tables
var sbox = [2][256]byte{
{
0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38,
0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48,
0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82,
0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61,
0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1,
0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7,
0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71,
0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7,
0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90,
0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef,
0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64,
0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a,
0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d,
0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4,
0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0,
},
{
0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b,
0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f,
0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5,
0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51,
0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c,
0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8,
0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2,
0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17,
0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e,
0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9,
0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48,
0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64,
0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69,
0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc,
0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9,
0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91,
},
}
// gfMult returns a·b in GF(2^8)/p
func gfMult(a, b byte, p uint32) byte {
B := [2]uint32{0, uint32(b)}
P := [2]uint32{0, p}
var result uint32
// branchless GF multiplier
for i := 0; i < 7; i++ {
result ^= B[a&1]
a >>= 1
B[1] = P[B[1]>>7] ^ (B[1] << 1)
}
result ^= B[a&1]
return byte(result)
}
// mdsColumnMult calculates y{col} where [y0 y1 y2 y3] = MDS · [x0]
func mdsColumnMult(in byte, col int) uint32 {
mul01 := in
mul5B := gfMult(in, 0x5B, mdsPolynomial)
mulEF := gfMult(in, 0xEF, mdsPolynomial)
switch col {
case 0:
return uint32(mul01) | uint32(mul5B)<<8 | uint32(mulEF)<<16 | uint32(mulEF)<<24
case 1:
return uint32(mulEF) | uint32(mulEF)<<8 | uint32(mul5B)<<16 | uint32(mul01)<<24
case 2:
return uint32(mul5B) | uint32(mulEF)<<8 | uint32(mul01)<<16 | uint32(mulEF)<<24
case 3:
return uint32(mul5B) | uint32(mul01)<<8 | uint32(mulEF)<<16 | uint32(mul5B)<<24
}
panic("unreachable")
}
// h implements the S-box generation function. See [TWOFISH] 4.3.5
func h(in, key []byte, offset int) uint32 {
var y [4]byte
for x := range y {
y[x] = in[x]
}
switch len(key) / 8 {
case 4:
y[0] = sbox[1][y[0]] ^ key[4*(6+offset)+0]
y[1] = sbox[0][y[1]] ^ key[4*(6+offset)+1]
y[2] = sbox[0][y[2]] ^ key[4*(6+offset)+2]
y[3] = sbox[1][y[3]] ^ key[4*(6+offset)+3]
fallthrough
case 3:
y[0] = sbox[1][y[0]] ^ key[4*(4+offset)+0]
y[1] = sbox[1][y[1]] ^ key[4*(4+offset)+1]
y[2] = sbox[0][y[2]] ^ key[4*(4+offset)+2]
y[3] = sbox[0][y[3]] ^ key[4*(4+offset)+3]
fallthrough
case 2:
y[0] = sbox[1][sbox[0][sbox[0][y[0]]^key[4*(2+offset)+0]]^key[4*(0+offset)+0]]
y[1] = sbox[0][sbox[0][sbox[1][y[1]]^key[4*(2+offset)+1]]^key[4*(0+offset)+1]]
y[2] = sbox[1][sbox[1][sbox[0][y[2]]^key[4*(2+offset)+2]]^key[4*(0+offset)+2]]
y[3] = sbox[0][sbox[1][sbox[1][y[3]]^key[4*(2+offset)+3]]^key[4*(0+offset)+3]]
}
// [y0 y1 y2 y3] = MDS . [x0 x1 x2 x3]
var mdsMult uint32
for i := range y {
mdsMult ^= mdsColumnMult(y[i], i)
}
return mdsMult
}
// Encrypt encrypts a 16-byte block from src to dst, which may overlap.
// Note that for amounts of data larger than a block,
// it is not safe to just call Encrypt on successive blocks;
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
func (c *Cipher) Encrypt(dst, src []byte) {
S1 := c.s[0]
S2 := c.s[1]
S3 := c.s[2]
S4 := c.s[3]
// Load input
ia := load32l(src[0:4])
ib := load32l(src[4:8])
ic := load32l(src[8:12])
id := load32l(src[12:16])
// Pre-whitening
ia ^= c.k[0]
ib ^= c.k[1]
ic ^= c.k[2]
id ^= c.k[3]
for i := 0; i < 8; i++ {
k := c.k[8+i*4 : 12+i*4]
t2 := S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)]
t1 := S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2
ic = bits.RotateLeft32(ic^(t1+k[0]), -1)
id = bits.RotateLeft32(id, 1) ^ (t2 + t1 + k[1])
t2 = S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)]
t1 = S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2
ia = bits.RotateLeft32(ia^(t1+k[2]), -1)
ib = bits.RotateLeft32(ib, 1) ^ (t2 + t1 + k[3])
}
// Output with "undo last swap"
ta := ic ^ c.k[4]
tb := id ^ c.k[5]
tc := ia ^ c.k[6]
td := ib ^ c.k[7]
store32l(dst[0:4], ta)
store32l(dst[4:8], tb)
store32l(dst[8:12], tc)
store32l(dst[12:16], td)
}
// Decrypt decrypts a 16-byte block from src to dst, which may overlap.
func (c *Cipher) Decrypt(dst, src []byte) {
S1 := c.s[0]
S2 := c.s[1]
S3 := c.s[2]
S4 := c.s[3]
// Load input
ta := load32l(src[0:4])
tb := load32l(src[4:8])
tc := load32l(src[8:12])
td := load32l(src[12:16])
// Undo undo final swap
ia := tc ^ c.k[6]
ib := td ^ c.k[7]
ic := ta ^ c.k[4]
id := tb ^ c.k[5]
for i := 8; i > 0; i-- {
k := c.k[4+i*4 : 8+i*4]
t2 := S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)]
t1 := S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2
ia = bits.RotateLeft32(ia, 1) ^ (t1 + k[2])
ib = bits.RotateLeft32(ib^(t2+t1+k[3]), -1)
t2 = S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)]
t1 = S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2
ic = bits.RotateLeft32(ic, 1) ^ (t1 + k[0])
id = bits.RotateLeft32(id^(t2+t1+k[1]), -1)
}
// Undo pre-whitening
ia ^= c.k[0]
ib ^= c.k[1]
ic ^= c.k[2]
id ^= c.k[3]
store32l(dst[0:4], ia)
store32l(dst[4:8], ib)
store32l(dst[8:12], ic)
store32l(dst[12:16], id)
}

164
vendor/golang.org/x/crypto/xts/xts.go generated vendored Normal file
View File

@ -0,0 +1,164 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package xts implements the XTS cipher mode as specified in IEEE P1619/D16.
//
// XTS mode is typically used for disk encryption, which presents a number of
// novel problems that make more common modes inapplicable. The disk is
// conceptually an array of sectors and we must be able to encrypt and decrypt
// a sector in isolation. However, an attacker must not be able to transpose
// two sectors of plaintext by transposing their ciphertext.
//
// XTS wraps a block cipher with Rogaway's XEX mode in order to build a
// tweakable block cipher. This allows each sector to have a unique tweak and
// effectively create a unique key for each sector.
//
// XTS does not provide any authentication. An attacker can manipulate the
// ciphertext and randomise a block (16 bytes) of the plaintext. This package
// does not implement ciphertext-stealing so sectors must be a multiple of 16
// bytes.
//
// Note that XTS is usually not appropriate for any use besides disk encryption.
// Most users should use an AEAD mode like GCM (from crypto/cipher.NewGCM) instead.
package xts // import "golang.org/x/crypto/xts"
import (
"crypto/cipher"
"encoding/binary"
"errors"
"sync"
"golang.org/x/crypto/internal/alias"
)
// Cipher contains an expanded key structure. It is safe for concurrent use if
// the underlying block cipher is safe for concurrent use.
type Cipher struct {
k1, k2 cipher.Block
}
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
var tweakPool = sync.Pool{
New: func() interface{} {
return new([blockSize]byte)
},
}
// NewCipher creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes). The key must be
// twice the length of the underlying cipher's key.
func NewCipher(cipherFunc func([]byte) (cipher.Block, error), key []byte) (c *Cipher, err error) {
c = new(Cipher)
if c.k1, err = cipherFunc(key[:len(key)/2]); err != nil {
return
}
c.k2, err = cipherFunc(key[len(key)/2:])
if c.k1.BlockSize() != blockSize {
err = errors.New("xts: cipher does not have a block size of 16")
}
return
}
// Encrypt encrypts a sector of plaintext and puts the result into ciphertext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *Cipher) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
if len(ciphertext) < len(plaintext) {
panic("xts: ciphertext is smaller than plaintext")
}
if len(plaintext)%blockSize != 0 {
panic("xts: plaintext is not a multiple of the block size")
}
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("xts: invalid buffer overlap")
}
tweak := tweakPool.Get().(*[blockSize]byte)
for i := range tweak {
tweak[i] = 0
}
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
c.k2.Encrypt(tweak[:], tweak[:])
for len(plaintext) > 0 {
for j := range tweak {
ciphertext[j] = plaintext[j] ^ tweak[j]
}
c.k1.Encrypt(ciphertext, ciphertext)
for j := range tweak {
ciphertext[j] ^= tweak[j]
}
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(tweak)
}
tweakPool.Put(tweak)
}
// Decrypt decrypts a sector of ciphertext and puts the result into plaintext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *Cipher) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
if len(plaintext) < len(ciphertext) {
panic("xts: plaintext is smaller than ciphertext")
}
if len(ciphertext)%blockSize != 0 {
panic("xts: ciphertext is not a multiple of the block size")
}
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("xts: invalid buffer overlap")
}
tweak := tweakPool.Get().(*[blockSize]byte)
for i := range tweak {
tweak[i] = 0
}
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
c.k2.Encrypt(tweak[:], tweak[:])
for len(ciphertext) > 0 {
for j := range tweak {
plaintext[j] = ciphertext[j] ^ tweak[j]
}
c.k1.Decrypt(plaintext, plaintext)
for j := range tweak {
plaintext[j] ^= tweak[j]
}
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(tweak)
}
tweakPool.Put(tweak)
}
// mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2(tweak *[blockSize]byte) {
var carryIn byte
for j := range tweak {
carryOut := tweak[j] >> 7
tweak[j] = (tweak[j] << 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
// If we have a carry bit then we need to subtract a multiple
// of the irreducible polynomial (x¹²⁸ + x⁷ + x² + x + 1).
// By dropping the carry bit, we're subtracting the x^128 term
// so all that remains is to subtract x⁷ + x² + x + 1.
// Subtraction (and addition) in this representation is just
// XOR.
tweak[0] ^= 1<<7 | 1<<2 | 1<<1 | 1
}
}

11
vendor/modules.txt vendored
View File

@ -51,6 +51,9 @@ github.com/VividCortex/ewma
# github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d # github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
## explicit ## explicit
github.com/acarl005/stripansi github.com/acarl005/stripansi
# github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6
## explicit
github.com/aead/serpent
# github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 # github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
## explicit; go 1.13 ## explicit; go 1.13
github.com/asaskevich/govalidator github.com/asaskevich/govalidator
@ -201,6 +204,9 @@ github.com/containers/image/v5/version
# github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 # github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01
## explicit ## explicit
github.com/containers/libtrust github.com/containers/libtrust
# github.com/containers/luksy v0.0.0-20230808154129-d2d74a56682f
## explicit; go 1.20
github.com/containers/luksy
# github.com/containers/ocicrypt v1.1.8 # github.com/containers/ocicrypt v1.1.8
## explicit; go 1.20 ## explicit; go 1.20
github.com/containers/ocicrypt github.com/containers/ocicrypt
@ -689,7 +695,9 @@ go.opencensus.io/trace/internal
go.opencensus.io/trace/tracestate go.opencensus.io/trace/tracestate
# golang.org/x/crypto v0.13.0 # golang.org/x/crypto v0.13.0
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/crypto/argon2
golang.org/x/crypto/bcrypt golang.org/x/crypto/bcrypt
golang.org/x/crypto/blake2b
golang.org/x/crypto/blowfish golang.org/x/crypto/blowfish
golang.org/x/crypto/cast5 golang.org/x/crypto/cast5
golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20
@ -707,12 +715,15 @@ golang.org/x/crypto/openpgp/errors
golang.org/x/crypto/openpgp/packet golang.org/x/crypto/openpgp/packet
golang.org/x/crypto/openpgp/s2k golang.org/x/crypto/openpgp/s2k
golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pbkdf2
golang.org/x/crypto/ripemd160
golang.org/x/crypto/salsa20/salsa golang.org/x/crypto/salsa20/salsa
golang.org/x/crypto/scrypt golang.org/x/crypto/scrypt
golang.org/x/crypto/sha3 golang.org/x/crypto/sha3
golang.org/x/crypto/ssh golang.org/x/crypto/ssh
golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/agent
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
golang.org/x/crypto/twofish
golang.org/x/crypto/xts
# golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b # golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b
## explicit; go 1.20 ## explicit; go 1.20
golang.org/x/exp/constraints golang.org/x/exp/constraints