mirror of
https://github.com/containers/buildah.git
synced 2025-07-31 15:24:26 +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:
17
Makefile
17
Makefile
@ -39,7 +39,7 @@ LIBSECCOMP_COMMIT := release-2.3
|
||||
|
||||
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 ?=
|
||||
|
||||
@ -69,9 +69,22 @@ static:
|
||||
mkdir -p ./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
|
||||
|
||||
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
|
||||
buildah: bin/buildah
|
||||
|
||||
|
@ -386,6 +386,11 @@ type ImportFromImageOptions struct {
|
||||
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.
|
||||
func NewBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) {
|
||||
if options.CommonBuildOpts == nil {
|
||||
|
@ -14,16 +14,22 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var logLevel string
|
||||
debug := false
|
||||
if InitReexec() {
|
||||
return
|
||||
}
|
||||
flag.BoolVar(&debug, "debug", false, "turn on debug logging")
|
||||
flag.StringVar(&logLevel, "log-level", "error", "log level")
|
||||
flag.Parse()
|
||||
logrus.SetLevel(logrus.ErrorLevel)
|
||||
if debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
level, err := logrus.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ type commitInputOptions struct {
|
||||
blobCache string
|
||||
certDir string
|
||||
creds string
|
||||
cwOptions string
|
||||
disableCompression bool
|
||||
format string
|
||||
iidfile string
|
||||
@ -87,6 +88,7 @@ func commitListFlagSet(cmd *cobra.Command, opts *commitInputOptions) {
|
||||
_ = cmd.RegisterFlagCompletionFunc("cert-dir", completion.AutocompleteDefault)
|
||||
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
|
||||
_ = 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.StringVarP(&opts.format, "format", "f", defaultFormat(), "`format` of the image manifest and metadata")
|
||||
_ = cmd.RegisterFlagCompletionFunc("format", completion.AutocompleteNone)
|
||||
@ -239,6 +241,14 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error
|
||||
options.HistoryTimestamp = ×tamp
|
||||
}
|
||||
|
||||
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 {
|
||||
return errors.New("can not use more then one timestamp option at at time")
|
||||
}
|
||||
|
@ -224,8 +224,8 @@ func Tail(a []string) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// UsageTemplate returns the usage template for podman commands
|
||||
// This blocks the displaying of the global options. The main podman
|
||||
// UsageTemplate returns the usage template for buildah commands
|
||||
// This blocks the displaying of the global options. The main buildah
|
||||
// command should not use this.
|
||||
func UsageTemplate() string {
|
||||
return `Usage:{{if .Runnable}}
|
||||
|
@ -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.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.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.MemoryProfile, "memory-profile", "", "`file` to write memory profile")
|
||||
|
||||
|
77
cmd/buildah/mkcw.go
Normal file
77
cmd/buildah/mkcw.go
Normal 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))
|
||||
}
|
||||
}
|
@ -105,6 +105,10 @@ type CommitOptions struct {
|
||||
// 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.
|
||||
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.
|
||||
// Deprecated: use UnsetEnv() before committing instead.
|
||||
UnsetEnvs []string
|
||||
|
217
convertcw.go
Normal file
217
convertcw.go
Normal 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
163
convertcw_test.go
Normal 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(), ®istrationRequest); 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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -163,6 +163,10 @@ type BuildOptions struct {
|
||||
// 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.
|
||||
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
|
||||
// way to add them.
|
||||
AdditionalTags []string
|
||||
|
@ -47,8 +47,16 @@ const (
|
||||
OCI = "oci"
|
||||
// DOCKER used to define the "docker" image format
|
||||
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 (
|
||||
// DefaultCapabilities is the list of capabilities which we grant by
|
||||
// default to containers which are running under UID 0.
|
||||
@ -105,6 +113,23 @@ type BuildOutputOption struct {
|
||||
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 creates a temporary directory, arranges for its contents to be
|
||||
// the contents of that URL, and returns the temporary directory's path, along
|
||||
|
2
go.mod
2
go.mod
@ -8,6 +8,7 @@ require (
|
||||
github.com/containernetworking/plugins v1.3.0
|
||||
github.com/containers/common v0.55.1-0.20230830075933-12405381ff45
|
||||
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/storage v1.49.0
|
||||
github.com/cyphar/filepath-securejoin v0.2.4
|
||||
@ -48,6 +49,7 @@ require (
|
||||
github.com/Microsoft/hcsshim v0.10.0 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // 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/chzyer/readline v1.5.1 // indirect
|
||||
github.com/container-orchestrated-devices/container-device-interface v0.6.0 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -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/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/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-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
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/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/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/go.mod h1:jM362hyBtbwLMWzXQZTlkjKGAQf/BN/LFMtH0FIRt34=
|
||||
github.com/containers/storage v1.49.0 h1:7Vqj8OKlwlcWZ4U61+eS2bNwyIG/qXQo/UZVW5kXn74=
|
||||
|
85
image.go
85
image.go
@ -16,6 +16,7 @@ import (
|
||||
"github.com/containers/buildah/copier"
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/docker"
|
||||
"github.com/containers/buildah/internal/mkcw"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/image"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
@ -69,6 +70,7 @@ type containerImageRef struct {
|
||||
annotations map[string]string
|
||||
preferredManifestType string
|
||||
squash bool
|
||||
confidentialWorkload ConfidentialWorkloadOptions
|
||||
omitHistory bool
|
||||
emptyLayer bool
|
||||
idMappingOptions *define.IDMappingOptions
|
||||
@ -158,6 +160,52 @@ func computeLayerMIMEType(what string, layerCompression archive.Compression) (om
|
||||
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.
|
||||
// Takes ExtractRootfsOptions as argument which allows caller to configure
|
||||
// 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{}
|
||||
// Only clear the history if we're squashing, otherwise leave it be so that we can append
|
||||
// entries to it.
|
||||
if i.squash || i.omitHistory {
|
||||
if i.confidentialWorkload.Convert || i.squash || i.omitHistory {
|
||||
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.
|
||||
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.
|
||||
dimage.RootFS = &docker.V2S2RootFS{}
|
||||
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
|
||||
// that we can append entries to it. Clear the parent, too, we no
|
||||
// longer include its layers and history.
|
||||
if i.squash || i.omitHistory {
|
||||
if i.confidentialWorkload.Convert || i.squash || i.omitHistory {
|
||||
dimage.Parent = ""
|
||||
dimage.History = []docker.V2S2History{}
|
||||
}
|
||||
@ -296,7 +362,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
|
||||
for layer != nil {
|
||||
layers = append(append([]string{}, layerID), layers...)
|
||||
layerID = layer.Parent
|
||||
if layerID == "" || i.squash {
|
||||
if layerID == "" || i.confidentialWorkload.Convert || i.squash {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
@ -333,7 +399,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
|
||||
blobLayers := make(map[digest.Digest]blobLayerInfo)
|
||||
for _, layerID := range layers {
|
||||
what := fmt.Sprintf("layer %q", layerID)
|
||||
if i.squash {
|
||||
if i.confidentialWorkload.Convert || i.squash {
|
||||
what = fmt.Sprintf("container %q", i.containerID)
|
||||
}
|
||||
// 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
|
||||
// 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
|
||||
layerBlobSize := layer.UncompressedSize
|
||||
diffID := layer.UncompressedDigest
|
||||
@ -389,7 +455,13 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
|
||||
}
|
||||
var rc io.ReadCloser
|
||||
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.
|
||||
rc, errChan, err = i.extractRootfs(ExtractRootfsOptions{})
|
||||
if err != nil {
|
||||
@ -842,6 +914,7 @@ func (b *Builder) makeContainerImageRef(options CommitOptions) (*containerImageR
|
||||
annotations: b.Annotations(),
|
||||
preferredManifestType: manifestType,
|
||||
squash: options.Squash,
|
||||
confidentialWorkload: options.ConfidentialWorkloadOptions,
|
||||
omitHistory: options.OmitHistory,
|
||||
emptyLayer: options.EmptyLayer && !options.Squash,
|
||||
idMappingOptions: &b.IDMappingOptions,
|
||||
|
@ -148,6 +148,7 @@ type Executor struct {
|
||||
osVersion string
|
||||
osFeatures []string
|
||||
envs []string
|
||||
confidentialWorkload define.ConfidentialWorkloadOptions
|
||||
}
|
||||
|
||||
type imageTypeAndHistoryAndDiffIDs struct {
|
||||
@ -303,6 +304,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
|
||||
osVersion: options.OSVersion,
|
||||
osFeatures: append([]string{}, options.OSFeatures...),
|
||||
envs: append([]string{}, options.Envs...),
|
||||
confidentialWorkload: options.ConfidentialWorkload,
|
||||
}
|
||||
if exec.err == nil {
|
||||
exec.err = os.Stderr
|
||||
|
@ -1032,7 +1032,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
// squash the contents of the base image. Whichever is
|
||||
// the case, we need to commit() to create a new image.
|
||||
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)
|
||||
}
|
||||
// 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
|
||||
// via the command line, so we need to commit.
|
||||
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
|
||||
}
|
||||
// Generate build output if needed.
|
||||
@ -1193,7 +1193,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
// stage.
|
||||
if lastStage || imageIsUsedLater {
|
||||
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 {
|
||||
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
|
||||
// layers even if its a squashed build so that they
|
||||
// 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 {
|
||||
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
|
||||
// if we're supposed to create one and this
|
||||
// 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 {
|
||||
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
|
||||
// the name if there is one, generating a unique ID-based one otherwise.
|
||||
// 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
|
||||
var imageRef types.ImageReference
|
||||
if output != "" {
|
||||
@ -2069,6 +2069,9 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
|
||||
HistoryTimestamp: s.executor.timestamp,
|
||||
Manifest: s.executor.manifest,
|
||||
}
|
||||
if finalInstruction {
|
||||
options.ConfidentialWorkloadOptions = s.executor.confidentialWorkload
|
||||
}
|
||||
imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
|
464
internal/mkcw/archive.go
Normal file
464
internal/mkcw/archive.go
Normal 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
|
||||
}
|
181
internal/mkcw/archive_test.go
Normal file
181
internal/mkcw/archive_test.go
Normal 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(), ®istrationRequest); 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
250
internal/mkcw/attest.go
Normal 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
BIN
internal/mkcw/embed/entrypoint
Executable file
Binary file not shown.
BIN
internal/mkcw/embed/entrypoint.gz
Executable file
BIN
internal/mkcw/embed/entrypoint.gz
Executable file
Binary file not shown.
16
internal/mkcw/embed/entrypoint.s
Normal file
16
internal/mkcw/embed/entrypoint.s
Normal 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
|
6
internal/mkcw/entrypoint.go
Normal file
6
internal/mkcw/entrypoint.go
Normal file
@ -0,0 +1,6 @@
|
||||
package mkcw
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed "embed/entrypoint.gz"
|
||||
var entrypointCompressedBytes []byte
|
51
internal/mkcw/luks.go
Normal file
51
internal/mkcw/luks.go
Normal 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
|
||||
}
|
66
internal/mkcw/luks_test.go
Normal file
66
internal/mkcw/luks_test.go
Normal 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
38
internal/mkcw/makefs.go
Normal 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)
|
||||
}
|
47
internal/mkcw/types/attest.go
Normal file
47
internal/mkcw/types/attest.go
Normal 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"`
|
||||
}
|
34
internal/mkcw/types/workload.go
Normal file
34
internal/mkcw/types/workload.go
Normal 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
223
internal/mkcw/workload.go
Normal 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
|
||||
}
|
62
internal/mkcw/workload_test.go
Normal file
62
internal/mkcw/workload_test.go
Normal 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)
|
||||
}
|
@ -296,6 +296,13 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
|
||||
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 cacheFrom []reference.Named
|
||||
cacheTo = nil
|
||||
@ -364,6 +371,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
|
||||
CacheTTL: cacheTTL,
|
||||
CNIConfigDir: iopts.CNIConfigDir,
|
||||
CNIPluginPath: iopts.CNIPlugInPath,
|
||||
ConfidentialWorkload: confidentialWorkloadOptions,
|
||||
CPPFlags: iopts.CPPFlags,
|
||||
CommonBuildOpts: commonOpts,
|
||||
Compression: compression,
|
||||
|
@ -107,6 +107,7 @@ type BudResults struct {
|
||||
Envs []string
|
||||
OSFeatures []string
|
||||
OSVersion string
|
||||
CWOptions string
|
||||
}
|
||||
|
||||
// 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.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.StringVarP(&flags.CWOptions, "cw", "", "", "confidential workload `options`")
|
||||
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.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["cpp-flag"] = commonComp.AutocompleteNone
|
||||
flagCompletion["creds"] = commonComp.AutocompleteNone
|
||||
flagCompletion["cw"] = commonComp.AutocompleteNone
|
||||
flagCompletion["env"] = commonComp.AutocompleteNone
|
||||
flagCompletion["file"] = commonComp.AutocompleteDefault
|
||||
flagCompletion["format"] = commonComp.AutocompleteNone
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containers/buildah/define"
|
||||
mkcwtypes "github.com/containers/buildah/internal/mkcw/types"
|
||||
internalParse "github.com/containers/buildah/internal/parse"
|
||||
internalUtil "github.com/containers/buildah/internal/util"
|
||||
"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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func IDMappingOptions(c *cobra.Command, isolation define.Isolation) (usernsOptions define.NamespaceOptions, idmapOptions *define.IDMappingOptions, err error) {
|
||||
return IDMappingOptionsFromFlagSet(c.Flags(), c.PersistentFlags(), c.Flag)
|
||||
|
82
tests/mkcw.bats
Normal file
82
tests/mkcw.bats
Normal 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
25
vendor/github.com/aead/serpent/.gitignore
generated
vendored
Normal 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
21
vendor/github.com/aead/serpent/LICENSE
generated
vendored
Normal 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
9
vendor/github.com/aead/serpent/README.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
[](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
316
vendor/github.com/aead/serpent/sbox_ref.go
generated
vendored
Normal 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
119
vendor/github.com/aead/serpent/serpent.go
generated
vendored
Normal 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
276
vendor/github.com/aead/serpent/serpent_ref.go
generated
vendored
Normal 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
16
vendor/github.com/containers/luksy/.cirrus.yml
generated
vendored
Normal 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
2
vendor/github.com/containers/luksy/.dockerignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
lukstool
|
||||
lukstool.test
|
21
vendor/github.com/containers/luksy/.gitignore
generated
vendored
Normal file
21
vendor/github.com/containers/luksy/.gitignore
generated
vendored
Normal 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
201
vendor/github.com/containers/luksy/LICENSE
generated
vendored
Normal 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
14
vendor/github.com/containers/luksy/Makefile
generated
vendored
Normal 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
10
vendor/github.com/containers/luksy/README.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
luksy: offline encryption/decryption using LUKS formats [](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
244
vendor/github.com/containers/luksy/decrypt.go
generated
vendored
Normal 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
421
vendor/github.com/containers/luksy/encrypt.go
generated
vendored
Normal 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
537
vendor/github.com/containers/luksy/encryption.go
generated
vendored
Normal 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
75
vendor/github.com/containers/luksy/luks.go
generated
vendored
Normal 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
55
vendor/github.com/containers/luksy/tune.go
generated
vendored
Normal 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
321
vendor/github.com/containers/luksy/v1header.go
generated
vendored
Normal 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
203
vendor/github.com/containers/luksy/v2header.go
generated
vendored
Normal 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
157
vendor/github.com/containers/luksy/v2json.go
generated
vendored
Normal 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
283
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
Normal 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
53
vendor/golang.org/x/crypto/argon2/blake2b.go
generated
vendored
Normal 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
61
vendor/golang.org/x/crypto/argon2/blamka_amd64.go
generated
vendored
Normal 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
244
vendor/golang.org/x/crypto/argon2/blamka_amd64.s
generated
vendored
Normal 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
163
vendor/golang.org/x/crypto/argon2/blamka_generic.go
generated
vendored
Normal 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
16
vendor/golang.org/x/crypto/argon2/blamka_ref.go
generated
vendored
Normal 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
291
vendor/golang.org/x/crypto/blake2b/blake2b.go
generated
vendored
Normal 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
|
||||
}
|
38
vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go
generated
vendored
Normal file
38
vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go
generated
vendored
Normal 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
745
vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s
generated
vendored
Normal 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
25
vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go
generated
vendored
Normal 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
279
vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s
generated
vendored
Normal 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
182
vendor/golang.org/x/crypto/blake2b/blake2b_generic.go
generated
vendored
Normal 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
12
vendor/golang.org/x/crypto/blake2b/blake2b_ref.go
generated
vendored
Normal 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
177
vendor/golang.org/x/crypto/blake2b/blake2x.go
generated
vendored
Normal 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
33
vendor/golang.org/x/crypto/blake2b/register.go
generated
vendored
Normal 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
124
vendor/golang.org/x/crypto/ripemd160/ripemd160.go
generated
vendored
Normal 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
165
vendor/golang.org/x/crypto/ripemd160/ripemd160block.go
generated
vendored
Normal 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
341
vendor/golang.org/x/crypto/twofish/twofish.go
generated
vendored
Normal 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
164
vendor/golang.org/x/crypto/xts/xts.go
generated
vendored
Normal 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
11
vendor/modules.txt
vendored
@ -51,6 +51,9 @@ github.com/VividCortex/ewma
|
||||
# github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
## explicit
|
||||
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
|
||||
## explicit; go 1.13
|
||||
github.com/asaskevich/govalidator
|
||||
@ -201,6 +204,9 @@ github.com/containers/image/v5/version
|
||||
# github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01
|
||||
## explicit
|
||||
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
|
||||
## explicit; go 1.20
|
||||
github.com/containers/ocicrypt
|
||||
@ -689,7 +695,9 @@ go.opencensus.io/trace/internal
|
||||
go.opencensus.io/trace/tracestate
|
||||
# golang.org/x/crypto v0.13.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/crypto/argon2
|
||||
golang.org/x/crypto/bcrypt
|
||||
golang.org/x/crypto/blake2b
|
||||
golang.org/x/crypto/blowfish
|
||||
golang.org/x/crypto/cast5
|
||||
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/s2k
|
||||
golang.org/x/crypto/pbkdf2
|
||||
golang.org/x/crypto/ripemd160
|
||||
golang.org/x/crypto/salsa20/salsa
|
||||
golang.org/x/crypto/scrypt
|
||||
golang.org/x/crypto/sha3
|
||||
golang.org/x/crypto/ssh
|
||||
golang.org/x/crypto/ssh/agent
|
||||
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
|
||||
## explicit; go 1.20
|
||||
golang.org/x/exp/constraints
|
||||
|
Reference in New Issue
Block a user