vendor: update containerd/cri 1a00c06886

full diff: c0294ebfe0...1a00c06886

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2020-03-06 21:07:29 +01:00
parent 5607b23b0f
commit 12c7d69769
98 changed files with 24933 additions and 92 deletions

View File

@@ -78,7 +78,7 @@ specifications as appropriate.
backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml) for an example on trusty.
* **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL)
2. Install **`socat`** (required by portforward).
2. Install and setup a go 1.12.9 development environment. (Note: You can check the travis logs for a recent pull request to confirm the version(s) of golang currently being used to build and test master.)
2. Install and setup a go 1.13.8 development environment. (Note: You can check the travis logs for a recent pull request to confirm the version(s) of golang currently being used to build and test master.)
3. Make a local clone of this repository.
4. Install binary dependencies by running the following command from your cloned `cri/` project directory:
```bash

View File

@@ -153,6 +153,18 @@ type RegistryConfig struct {
TLS *TLSConfig `toml:"tls" json:"tls"`
}
// ImageDecryption contains configuration to handling decryption of encrypted container images.
type ImageDecryption struct {
// KeyModel specifies the trust model of where keys should reside.
//
// Details of field usage can be found in:
// https://github.com/containerd/cri/tree/master/docs/config.md
//
// Details of key models can be found in:
// https://github.com/containerd/cri/tree/master/docs/decryption.md
KeyModel string `toml:"key_model" json:"keyModel"`
}
// PluginConfig contains toml config related to CRI plugin,
// it is a subset of Config.
type PluginConfig struct {
@@ -162,6 +174,8 @@ type PluginConfig struct {
CniConfig `toml:"cni" json:"cni"`
// Registry contains config related to the registry
Registry Registry `toml:"registry" json:"registry"`
// ImageDecryption contains config related to handling decryption of encrypted container images
ImageDecryption `toml:"image_decryption" json:"imageDecryption"`
// DisableTCPService disables serving CRI on the TCP server.
DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"`
// StreamServerAddress is the ip address streaming server is listening on.
@@ -236,6 +250,9 @@ const (
RuntimeUntrusted = "untrusted"
// RuntimeDefault is the implicit runtime defined for ContainerdConfig.DefaultRuntime
RuntimeDefault = "default"
// KeyModelNode is the key model where key for encrypted images reside
// on the worker nodes
KeyModelNode = "node"
)
// ValidatePluginConfig validates the given plugin configuration.

View File

@@ -52,7 +52,7 @@ func DefaultConfig() PluginConfig {
TLSKeyFile: "",
TLSCertFile: "",
},
SandboxImage: "k8s.gcr.io/pause:3.1",
SandboxImage: "k8s.gcr.io/pause:3.2",
StatsCollectPeriod: 10,
SystemdCgroup: false,
MaxContainerLogLineSize: 16 * 1024,

View File

@@ -423,10 +423,11 @@ func WithResources(resources *runtime.LinuxContainerResources) oci.SpecOpts {
s.Linux.Resources.Memory = &runtimespec.LinuxMemory{}
}
var (
p = uint64(resources.GetCpuPeriod())
q = resources.GetCpuQuota()
shares = uint64(resources.GetCpuShares())
limit = resources.GetMemoryLimitInBytes()
p = uint64(resources.GetCpuPeriod())
q = resources.GetCpuQuota()
shares = uint64(resources.GetCpuShares())
limit = resources.GetMemoryLimitInBytes()
hugepages = resources.GetHugepageLimits()
)
if p != 0 {
@@ -447,6 +448,12 @@ func WithResources(resources *runtime.LinuxContainerResources) oci.SpecOpts {
if limit != 0 {
s.Linux.Resources.Memory.Limit = &limit
}
for _, limit := range hugepages {
s.Linux.Resources.HugepageLimits = append(s.Linux.Resources.HugepageLimits, runtimespec.LinuxHugepageLimit{
Pagesize: limit.PageSize,
Limit: limit.Limit,
})
}
return nil
}
}

View File

@@ -32,6 +32,8 @@ import (
containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/remotes/docker"
"github.com/containerd/imgcrypt"
"github.com/containerd/imgcrypt/images/encryption"
distribution "github.com/docker/distribution/reference"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
@@ -106,7 +108,8 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
return nil, nil
}
)
image, err := c.client.Pull(ctx, ref,
pullOpts := []containerd.RemoteOpt{
containerd.WithSchema1Conversion,
containerd.WithResolver(resolver),
containerd.WithPullSnapshotter(c.config.ContainerdConfig.Snapshotter),
@@ -114,7 +117,11 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
containerd.WithPullLabel(imageLabelKey, imageLabelValue),
containerd.WithMaxConcurrentDownloads(c.config.MaxConcurrentDownloads),
containerd.WithImageHandler(imageHandler),
)
}
pullOpts = append(pullOpts, c.encryptedImagesPullOpts()...)
image, err := c.client.Pull(ctx, ref, pullOpts...)
if err != nil {
return nil, errors.Wrapf(err, "failed to pull and unpack image %q", ref)
}
@@ -403,3 +410,15 @@ func newTransport() *http.Transport {
ExpectContinueTimeout: 5 * time.Second,
}
}
// encryptedImagesPullOpts returns the necessary list of pull options required
// for decryption of encrypted images based on the cri decryption configuration.
func (c *criService) encryptedImagesPullOpts() []containerd.RemoteOpt {
if c.config.ImageDecryption.KeyModel == criconfig.KeyModelNode {
ltdd := imgcrypt.Payload{}
decUnpackOpt := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(&ltdd))
opt := containerd.WithUnpackOpts([]containerd.UnpackOpt{decUnpackOpt})
return []containerd.RemoteOpt{opt}
}
return nil
}

View File

@@ -16,12 +16,18 @@ limitations under the License.
package store
import "errors"
import "github.com/containerd/containerd/errdefs"
var (
// ErrAlreadyExist is the error returned when data added in the store
// already exists.
ErrAlreadyExist = errors.New("already exists")
//
// This error has been DEPRECATED and will be removed in 1.5. Please switch
// usage directly to `errdefs.ErrAlreadyExists`.
ErrAlreadyExist = errdefs.ErrAlreadyExists
// ErrNotExist is the error returned when data is not in the store.
ErrNotExist = errors.New("does not exist")
//
// This error has been DEPRECATED and will be removed in 1.5. Please switch
// usage directly to `errdefs.ErrNotFound`.
ErrNotExist = errdefs.ErrNotFound
)

View File

@@ -1,89 +1,97 @@
# cri dependencies
github.com/tchap/go-patricia 666120de432aea38ab06bd5c818f04f4129882c9 # v2.2.6
github.com/opencontainers/selinux 5215b1806f52b1fcc2070a8826c542c9d33cd3cf
github.com/docker/docker d1d5f6476656c6aad457e2a91d3436e66b6f2251
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/docker d1d5f6476656c6aad457e2a91d3436e66b6f2251
github.com/opencontainers/selinux 31f70552238c5e017d78c3f1ba65e85f593f48e0 # v1.3.3
github.com/tchap/go-patricia 666120de432aea38ab06bd5c818f04f4129882c9 # v2.2.6
# containerd dependencies
go.opencensus.io v0.22.0
go.etcd.io/bbolt a0458a2b35708eef59eb5f620ceb3cd1c01a824d # v1.3.3
google.golang.org/grpc 39e8a7b072a67ca2a75f57fa2e0d50995f5b22f6 # v1.23.1
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
golang.org/x/sys c990c680b611ac1aeb7d8f2af94a825f98d69720 https://github.com/golang/sys
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/net f3200d17e092c607f615320ecaad13d87ad9a2b3
github.com/urfave/cli bfe2e925cfb6d44b40ad3a779165ea7e8aff9212 # v1.22.0
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
github.com/pkg/errors ba968bfe8b2f7e042a574c888954fccecfa385b4 # v0.8.1
github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db
github.com/opencontainers/runc dc9208a3303feef5b3839f4323d9beb36df0a9dd # v1.0.0-rc10
github.com/opencontainers/image-spec d60099175f88c47cd379c4738d158884749ed235 # v1.0.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c # v1.0.1
github.com/konsorten/go-windows-terminal-sequences 5c8c8bd35d3832f5d134ae1e1e375b69a4d25242 # v1.0.1
github.com/imdario/mergo 7c29201646fa3de8506f701213473dd407f19646 # v0.3.7
github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
github.com/google/uuid 0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1
github.com/golang/protobuf aa810b61a9c79d51363740d207bb46cf8e620ed5 # v1.2.0
github.com/gogo/protobuf ba06b47c162d49f2af050fb4c75bcbc86a159d5c # v1.2.1
github.com/gogo/googleapis d31c731455cb061f42baff3bda55bad0118b126b # v1.2.0
github.com/godbus/dbus/v5 37bf87eef99d69c4f1d3528bd66e3a87dc201472 # v5.0.3
github.com/docker/go-units 519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0
github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/coreos/go-systemd/v22 2d78030078ef61b3cae27f42ad6d0e46db51b339 # v22.0.0
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/ttrpc 92c8520ef9f86600c650dd540266a007bf03670f
github.com/containerd/go-runc a5c2862aed5e6358b305b0e16bfce58e0549b1cd
github.com/containerd/fifo bda0ff6ed73c67bfb5e62bc9c697f146b7fd7f13
github.com/containerd/continuity f2a389ac0a02ce21c09edd7344677a601970f41c
github.com/containerd/containerd e1221e69a824ce9aaca34c5bb603feb2f921b883
github.com/containerd/console 8375c3424e4d7b114e8a90a4a40c8e1b40d1d4e6
github.com/containerd/cgroups 7347743e5d1e8500d9f27c8e748e689ed991d92b
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/Microsoft/hcsshim b3f49c06ffaeef24d09c6c08ec8ec8425a0303e2 # v0.8.7
github.com/Microsoft/go-winio 6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
github.com/BurntSushi/toml 3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005 # v0.3.1
github.com/cpuguy83/go-md2man 7762f7e404f8416dfa1d9bb6a8c192aa9acb4d19 # v1.0.10
github.com/russross/blackfriday 05f3235734ad95d0016f6a23902f06461fcf567a # v1.5.2
github.com/beorn7/perks 37c8de3658fcb183f997c4e13e8337516ab753e6 # v1.0.1
github.com/BurntSushi/toml 3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005 # v0.3.1
github.com/cespare/xxhash/v2 d7df74196a9e781ede915320c11c378c1b2f3a1f # v2.1.1
github.com/containerd/cgroups 7347743e5d1e8500d9f27c8e748e689ed991d92b
github.com/containerd/console 8375c3424e4d7b114e8a90a4a40c8e1b40d1d4e6
github.com/containerd/containerd 01310155947cb6eec37dcae29742a165e56acb4a
github.com/containerd/continuity 0ec596719c75bfd42908850990acea594b7593ac
github.com/containerd/fifo bda0ff6ed73c67bfb5e62bc9c697f146b7fd7f13
github.com/containerd/go-runc a5c2862aed5e6358b305b0e16bfce58e0549b1cd
github.com/containerd/ttrpc 92c8520ef9f86600c650dd540266a007bf03670f
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/coreos/go-systemd/v22 2d78030078ef61b3cae27f42ad6d0e46db51b339 # v22.0.0
github.com/cpuguy83/go-md2man 7762f7e404f8416dfa1d9bb6a8c192aa9acb4d19 # v1.0.10
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-metrics b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1
github.com/docker/go-units 519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0
github.com/godbus/dbus/v5 37bf87eef99d69c4f1d3528bd66e3a87dc201472 # v5.0.3
github.com/gogo/googleapis 01e0f9cca9b92166042241267ee2a5cdf5cff46c # v1.3.2
github.com/gogo/protobuf 5628607bb4c51c3157aacc3a50f0ab707582b805 # v1.3.1
github.com/golang/protobuf d23c5127dc24889085f8ccea5c9d560a57a879d8 # v1.3.3
github.com/google/uuid 0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1
github.com/grpc-ecosystem/go-grpc-prometheus c225b8c3b01faf2899099b768856a9e916e5087b # v1.2.0
github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3
github.com/imdario/mergo 7c29201646fa3de8506f701213473dd407f19646 # v0.3.7
github.com/konsorten/go-windows-terminal-sequences 5c8c8bd35d3832f5d134ae1e1e375b69a4d25242 # v1.0.1
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c # v1.0.1
github.com/Microsoft/go-winio 6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
github.com/Microsoft/hcsshim 0b571ac85d7c5842b26d2571de4868634a4c39d7 # v0.8.7-24-g0b571ac8
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/opencontainers/image-spec d60099175f88c47cd379c4738d158884749ed235 # v1.0.1
github.com/opencontainers/runc dc9208a3303feef5b3839f4323d9beb36df0a9dd # v1.0.0-rc10
github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db
github.com/pkg/errors ba968bfe8b2f7e042a574c888954fccecfa385b4 # v0.8.1
github.com/prometheus/client_golang c42bebe5a5cddfc6b28cd639103369d8a75dfa89 # v1.3.0
github.com/prometheus/client_model d1d2010b5beead3fa1c5f271a5cf626e40b3ad6e # v0.1.0
github.com/prometheus/common 287d3e634a1e550c9e463dd7e5a75a422c614505 # v0.7.0
github.com/prometheus/procfs 6d489fc7f1d9cd890a250f3ea3431b1744b9623f # v0.0.8
github.com/russross/blackfriday 05f3235734ad95d0016f6a23902f06461fcf567a # v1.5.2
github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
github.com/urfave/cli bfe2e925cfb6d44b40ad3a779165ea7e8aff9212 # v1.22.0
go.etcd.io/bbolt a0458a2b35708eef59eb5f620ceb3cd1c01a824d # v1.3.3
go.opencensus.io 9c377598961b706d1542bd2d84d538b5094d596e # v0.22.0
golang.org/x/net f3200d17e092c607f615320ecaad13d87ad9a2b3
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/sys 52ab431487773bc9dd1b0766228b1cf3944126bf
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
google.golang.org/genproto e50cd9704f63023d62cd06a1994b98227fc4d21a
google.golang.org/grpc f495f5b15ae7ccda3b38c53a1bfcde4c1a58a2bc # v1.27.1
# cgroups dependencies
github.com/cilium/ebpf 60c3aa43f488292fe2ee50fb8b833b383ca8ebbb
github.com/cilium/ebpf 60c3aa43f488292fe2ee50fb8b833b383ca8ebbb
# kubernetes dependencies
sigs.k8s.io/yaml fd68e9863619f6ec2fdd8625fe1f02e7c877e480 # v1.1.0
k8s.io/utils e782cd3c129fc98ee807f3c889c0f26eb7c9daf5
k8s.io/kubernetes v1.17.1
k8s.io/klog v1.0.0
k8s.io/cri-api kubernetes-1.17.1
k8s.io/client-go kubernetes-1.17.1
k8s.io/api kubernetes-1.17.1
k8s.io/apiserver kubernetes-1.17.1
k8s.io/apimachinery kubernetes-1.17.1
gopkg.in/yaml.v2 53403b58ad1b561927d19068c655246f2db79d48 # v2.2.8
gopkg.in/inf.v0 v0.9.1
golang.org/x/time 9d24e82272b4f38b78bc8cff74fa936d31ccd8ef
golang.org/x/oauth2 0f29369cfe4552d0e4bcddc57cc75f4d7e672a33
golang.org/x/crypto 60c769a6c58655dab1b9adac0d58967dd517cfba
github.com/stretchr/testify v1.4.0
github.com/seccomp/libseccomp-golang 689e3c1541a84461afc49c1c87352a6cedf72e9c # v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/modern-go/reflect2 4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd # 1.0.1
github.com/modern-go/concurrent bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 # 1.0.3
github.com/json-iterator/go v1.1.8
github.com/google/gofuzz f140a6486e521aad38f5917de355cbf147cc0496 # v1.0.0
github.com/emicklei/go-restful b993709ae1a4f6dd19cfa475232614441b11c9d5 # v2.9.5
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73 # v1.1.1
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73 # v1.1.1
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful b993709ae1a4f6dd19cfa475232614441b11c9d5 # v2.9.5
github.com/google/gofuzz db92cf7ae75e4a7a28abc005addab2b394362888 # v1.1.0
github.com/json-iterator/go 03217c3e97663914aec3faafde50d081f197a0a2 # v1.1.8
github.com/modern-go/concurrent bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 # 1.0.3
github.com/modern-go/reflect2 4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd # 1.0.1
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 # v1.0.0
github.com/seccomp/libseccomp-golang 689e3c1541a84461afc49c1c87352a6cedf72e9c # v0.9.1
github.com/stretchr/testify 221dbe5ed46703ee255b1da0dec05086f5035f62 # v1.4.0
golang.org/x/crypto bac4c82f69751a6dd76e702d54b3ceb88adab236
golang.org/x/oauth2 0f29369cfe4552d0e4bcddc57cc75f4d7e672a33
golang.org/x/time 9d24e82272b4f38b78bc8cff74fa936d31ccd8ef
gopkg.in/inf.v0 d2d2541c53f18d2a059457998ce2876cc8e67cbf # v0.9.1
gopkg.in/yaml.v2 53403b58ad1b561927d19068c655246f2db79d48 # v2.2.8
k8s.io/api e6bc7324d7efd1c8ab0e68dd8162a2b500b0ce3b # v0.18.0-beta.1
k8s.io/apimachinery 2373d029717c4d169463414a6127cd1d0d12680e # v0.18.0-beta.1
k8s.io/apiserver 9af6eefd238b73bb688c429c32d113d04eeea3a8 # v0.18.0-beta.1
k8s.io/client-go 33a99c0dca04e5e335442262f93df4c3faab201b # v0.18.0-beta.1
k8s.io/cri-api 3d1680d8d202aa12c5dc5689170c3c03a488d35b # v0.18.0-beta.1
k8s.io/klog 2ca9ad30301bf30a8a6e0fa2110db6b8df699a91 # v1.0.0
k8s.io/kubernetes f2d7577e31829664899f1b8e3d3a73de8c5f4029 # v1.18.0-beta.1
k8s.io/utils 5f6fbceb4c31d35291b2de756aeaae2ddeee3e92
sigs.k8s.io/structured-merge-diff/v3 877aee05330847a873a1a8998b40e12a1e0fde25 # v3.0.0
sigs.k8s.io/yaml 9fc95527decd95bb9d28cc2eab08179b2d0f6971 # v1.2.0
# cni dependencies
github.com/containernetworking/plugins 9f96827c7cabb03f21d86326000c00f61e181f6a # v0.7.6
github.com/containernetworking/cni 4cfb7b568922a3c79a23e438dc52fe537fc9687e # v0.7.1
github.com/containerd/go-cni 0d360c50b10b350b6bb23863fd4dfb1c232b01c9
github.com/containerd/go-cni 0d360c50b10b350b6bb23863fd4dfb1c232b01c9
github.com/containernetworking/cni 4cfb7b568922a3c79a23e438dc52fe537fc9687e # v0.7.1
github.com/containernetworking/plugins 9f96827c7cabb03f21d86326000c00f61e181f6a # v0.7.6
# image decrypt depedencies
github.com/containerd/imgcrypt 9e761ccd6069fb707ec9493435f31475b5524b38 # v1.0.1
github.com/containers/ocicrypt 0343cc6053fd65069df55bce6838096e09b4033a # v1.0.1 from containerd/imgcrypt
github.com/fullsailor/pkcs7 8306686428a5fe132eac8cb7c4848af725098bd4 # from containers/ocicrypt
gopkg.in/square/go-jose.v2 730df5f748271903322feb182be83b43ebbbe27d # v2.3.1 from containers/ocicrypt

191
vendor/github.com/containerd/imgcrypt/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://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
Copyright The containerd Authors
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
https://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.

102
vendor/github.com/containerd/imgcrypt/README.md generated vendored Normal file
View File

@@ -0,0 +1,102 @@
# imgcrypt image encryption library and command line lool
Project `imgcrypt` is a non-core subproject of containerd.
The `imgcrypt` library provides API exensions for containerd to support encryted container images and implements
the `ctd-decoder` command line tool for use by containerd to decrypt encrypted container images. An extended version
of containerd's `ctr` tool (`ctr-enc') with support for encrypting and decrypting container images is also provided.
`imgcrypt` relies on the [`ocicrypt`](https://github.com/containers/ocicrypt) library for crypto functions on image layers.
# Usage
`imgcrypt` requires containerd 1.3 or later.
Build and install `imgcrypt`:
```
# make
# sudo make install
```
Start containerd with a configuration file that looks as follows. To avoid interference with a containerd from a Docker
installation we use /tmp for directories. Also, we build containerd 1.3 from the source but do not install it.
```
# cat config.toml
disable_plugins = ["cri"]
root = "/tmp/var/lib/containerd"
state = "/tmp/run/containerd"
[grpc]
address = "/tmp/run/containerd/mycontainerd.sock"
uid = 0
gid = 0
[stream_processors]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar+gzip"
path = "/usr/local/bin/ctd-decoder"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar"
path = "/usr/local/bin/ctd-decoder"
# sudo ~/src/github.com/containerd/containerd/bin/containerd -c config.toml
```
Create an RSA key pair using the openssl command line tool and encrypted an image:
```
# openssl genrsa --out mykey.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................+++++
............................+++++
e is 65537 (0x010001)
# openssl rsa -in mykey.pem -pubout -out mypubkey.pem
writing RSA key
# sudo chmod 0666 /tmp/run/containerd/containerd.sock
# CTR="/usr/local/bin/ctr-enc -a /tmp/run/containerd/containerd.sock"
# $CTR images pull --all-platforms docker.io/library/bash:latest
[...]
# $CTR images layerinfo --platform linux/amd64 docker.io/library/bash:latest
# DIGEST PLATFORM SIZE ENCRYPTION RECIPIENTS
0 sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609 linux/amd64 2789669
1 sha256:7dd01fd971d4ec7058c5636a505327b24e5fc8bd7f62816a9d518472bd9b15c0 linux/amd64 3174665
2 sha256:691cfbca522787898c8b37f063dd20e5524e7d103e1a3b298bd2e2b8da54faf5 linux/amd64 340
# $CTR images encrypt --recipient jwe:mypubkey.pem --platform linux/amd64 docker.io/library/bash:latest bash.enc:latest
Encrypting docker.io/library/bash:latest to bash.enc:latest
$ $CTR images layerinfo --platform linux/amd64 bash.enc:latest
# DIGEST PLATFORM SIZE ENCRYPTION RECIPIENTS
0 sha256:360be141b01f69b25427a9085b36ba8ad7d7a335449013fa6b32c1ecb894ab5b linux/amd64 2789669 jwe [jwe]
1 sha256:ac601e66cdd275ee0e10afead03a2722e153a60982122d2d369880ea54fe82f8 linux/amd64 3174665 jwe [jwe]
2 sha256:41e47064fd00424e328915ad2f7f716bd86ea2d0d8315edaf33ecaa6a2464530 linux/amd64 340 jwe [jwe]
```
Start a local image registry so we can push the encrypted image to it. A recent versions of the registry is required
to accept encrypted container images.
```
# docker pull registry:latest
# docker run -d -p 5000:5000 --restart=always --name registry registry
```
Push the encrypted image to the local registry, pull it using `ctr-enc`, and then run the image.
```
# $CTR images tag bash.enc:latest localhost:5000/bash.enc:latest
# $CTR images push localhost:5000/bash.enc:latest
# $CTR images rm localhost:5000/bash.enc:latest bash.enc:latest
# $CTR images pull localhost:5000/bash.enc:latest
# sudo $CTR run --rm localhost:5000/bash.enc:latest test echo 'Hello World!'
ctr: you are not authorized to use this image: missing private key needed for decryption
# sudo $CTR run --rm --key mykey.pem localhost:5000/bash.enc:latest test echo 'Hello World!'
Hello World!
```
## Project details
**imgcrypt** is a non-core containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

39
vendor/github.com/containerd/imgcrypt/go.mod generated vendored Normal file
View File

@@ -0,0 +1,39 @@
module github.com/containerd/imgcrypt
go 1.13
require (
github.com/Microsoft/go-winio v0.4.14
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4
github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601 // indirect
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50
github.com/containerd/containerd v1.3.0
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02 // indirect
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 // indirect
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda // indirect
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8 // indirect
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
github.com/containers/ocicrypt v1.0.1
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b // indirect
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible // indirect
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 // indirect
github.com/gogo/googleapis v1.2.0 // indirect
github.com/gogo/protobuf v1.2.1
github.com/imdario/mergo v0.3.8 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9 // indirect
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559
github.com/pkg/errors v0.8.1
github.com/prometheus/procfs v0.0.8 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
github.com/urfave/cli v1.22.1
go.etcd.io/bbolt v1.3.3 // indirect
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e // indirect
google.golang.org/grpc v1.24.0
)

View File

@@ -0,0 +1,83 @@
/*
Copyright The containerd Authors.
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.
*/
package encryption
import (
"context"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/diff"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/imgcrypt"
"github.com/containerd/typeurl"
encconfig "github.com/containers/ocicrypt/config"
"github.com/gogo/protobuf/types"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// WithDecryptedUnpack allows to pass parameters the 'layertool' needs to the applier
func WithDecryptedUnpack(data *imgcrypt.Payload) diff.ApplyOpt {
return func(_ context.Context, desc ocispec.Descriptor, c *diff.ApplyConfig) error {
if c.ProcessorPayloads == nil {
c.ProcessorPayloads = make(map[string]*types.Any)
}
data.Descriptor = desc
any, err := typeurl.MarshalAny(data)
if err != nil {
return errors.Wrapf(err, "failed to marshal payload")
}
for _, id := range imgcrypt.PayloadToolIDs {
c.ProcessorPayloads[id] = any
}
return nil
}
}
// WithUnpackConfigApplyOpts allows to pass an ApplyOpt
func WithUnpackConfigApplyOpts(opt diff.ApplyOpt) containerd.UnpackOpt {
return func(_ context.Context, uc *containerd.UnpackConfig) error {
uc.ApplyOpts = append(uc.ApplyOpts, opt)
return nil
}
}
// WithUnpackOpts is used to add unpack options to the unpacker.
func WithUnpackOpts(opts []containerd.UnpackOpt) containerd.RemoteOpt {
return func(_ *containerd.Client, c *containerd.RemoteContext) error {
c.UnpackOpts = append(c.UnpackOpts, opts...)
return nil
}
}
// WithAuthorizationCheck checks the authorization of keys used for encrypted containers
// be checked upon creation of a container
func WithAuthorizationCheck(dc *encconfig.DecryptConfig) containerd.NewContainerOpts {
return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
image, err := client.ImageService().Get(ctx, c.Image)
if errdefs.IsNotFound(err) {
// allow creation of container without a existing image
return nil
} else if err != nil {
return err
}
return CheckAuthorization(ctx, client.ContentStore(), image.Target, dc)
}
}

View File

@@ -0,0 +1,468 @@
/*
Copyright The containerd Authors.
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.
*/
package encryption
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"math/rand"
"github.com/containerd/containerd/images"
"github.com/containers/ocicrypt"
encconfig "github.com/containers/ocicrypt/config"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms"
encocispec "github.com/containers/ocicrypt/spec"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go"
"github.com/pkg/errors"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
type cryptoOp int
const (
cryptoOpEncrypt cryptoOp = iota
cryptoOpDecrypt = iota
cryptoOpUnwrapOnly = iota
)
// LayerFilter allows to select Layers by certain criteria
type LayerFilter func(desc ocispec.Descriptor) bool
// IsEncryptedDiff returns true if mediaType is a known encrypted media type.
func IsEncryptedDiff(ctx context.Context, mediaType string) bool {
switch mediaType {
case encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc:
return true
}
return false
}
// HasEncryptedLayer returns true if any LayerInfo indicates that the layer is encrypted
func HasEncryptedLayer(ctx context.Context, layerInfos []ocispec.Descriptor) bool {
for i := 0; i < len(layerInfos); i++ {
if IsEncryptedDiff(ctx, layerInfos[i].MediaType) {
return true
}
}
return false
}
// encryptLayer encrypts the layer using the CryptoConfig and creates a new OCI Descriptor.
// A call to this function may also only manipulate the wrapped keys list.
// The caller is expected to store the returned encrypted data and OCI Descriptor
func encryptLayer(cc *encconfig.CryptoConfig, dataReader content.ReaderAt, desc ocispec.Descriptor) (ocispec.Descriptor, io.Reader, ocicrypt.EncryptLayerFinalizer, error) {
var (
size int64
d digest.Digest
err error
)
encLayerReader, encLayerFinalizer, err := ocicrypt.EncryptLayer(cc.EncryptConfig, ocicrypt.ReaderFromReaderAt(dataReader), desc)
if err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
// were data touched ?
if encLayerReader != nil {
size = 0
d = ""
} else {
size = desc.Size
d = desc.Digest
}
newDesc := ocispec.Descriptor{
Digest: d,
Size: size,
Platform: desc.Platform,
}
switch desc.MediaType {
case images.MediaTypeDockerSchema2LayerGzip:
newDesc.MediaType = encocispec.MediaTypeLayerGzipEnc
case images.MediaTypeDockerSchema2Layer:
newDesc.MediaType = encocispec.MediaTypeLayerEnc
case encocispec.MediaTypeLayerGzipEnc:
newDesc.MediaType = encocispec.MediaTypeLayerGzipEnc
case encocispec.MediaTypeLayerEnc:
newDesc.MediaType = encocispec.MediaTypeLayerEnc
// TODO: Mediatypes to be added in ocispec
case ocispec.MediaTypeImageLayerGzip:
newDesc.MediaType = encocispec.MediaTypeLayerGzipEnc
case ocispec.MediaTypeImageLayer:
newDesc.MediaType = encocispec.MediaTypeLayerEnc
default:
return ocispec.Descriptor{}, nil, nil, errors.Errorf("Encryption: unsupporter layer MediaType: %s\n", desc.MediaType)
}
return newDesc, encLayerReader, encLayerFinalizer, nil
}
// DecryptLayer decrypts the layer using the DecryptConfig and creates a new OCI Descriptor.
// The caller is expected to store the returned plain data and OCI Descriptor
func DecryptLayer(dc *encconfig.DecryptConfig, dataReader io.Reader, desc ocispec.Descriptor, unwrapOnly bool) (ocispec.Descriptor, io.Reader, digest.Digest, error) {
resultReader, layerDigest, err := ocicrypt.DecryptLayer(dc, dataReader, desc, unwrapOnly)
if err != nil || unwrapOnly {
return ocispec.Descriptor{}, nil, "", err
}
newDesc := ocispec.Descriptor{
Size: 0,
Platform: desc.Platform,
}
switch desc.MediaType {
case encocispec.MediaTypeLayerGzipEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2LayerGzip
case encocispec.MediaTypeLayerEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2Layer
default:
return ocispec.Descriptor{}, nil, "", errors.Errorf("Decryption: unsupporter layer MediaType: %s\n", desc.MediaType)
}
return newDesc, resultReader, layerDigest, nil
}
// decryptLayer decrypts the layer using the CryptoConfig and creates a new OCI Descriptor.
// The caller is expected to store the returned plain data and OCI Descriptor
func decryptLayer(cc *encconfig.CryptoConfig, dataReader content.ReaderAt, desc ocispec.Descriptor, unwrapOnly bool) (ocispec.Descriptor, io.Reader, error) {
resultReader, d, err := ocicrypt.DecryptLayer(cc.DecryptConfig, ocicrypt.ReaderFromReaderAt(dataReader), desc, unwrapOnly)
if err != nil || unwrapOnly {
return ocispec.Descriptor{}, nil, err
}
newDesc := ocispec.Descriptor{
Digest: d,
Size: 0,
Platform: desc.Platform,
}
switch desc.MediaType {
case encocispec.MediaTypeLayerGzipEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2LayerGzip
case encocispec.MediaTypeLayerEnc:
newDesc.MediaType = images.MediaTypeDockerSchema2Layer
default:
return ocispec.Descriptor{}, nil, errors.Errorf("Decryption: unsupporter layer MediaType: %s\n", desc.MediaType)
}
return newDesc, resultReader, nil
}
// cryptLayer handles the changes due to encryption or decryption of a layer
func cryptLayer(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, cryptoOp cryptoOp) (ocispec.Descriptor, error) {
var (
resultReader io.Reader
newDesc ocispec.Descriptor
encLayerFinalizer ocicrypt.EncryptLayerFinalizer
)
dataReader, err := cs.ReaderAt(ctx, desc)
if err != nil {
return ocispec.Descriptor{}, err
}
defer dataReader.Close()
if cryptoOp == cryptoOpEncrypt {
newDesc, resultReader, encLayerFinalizer, err = encryptLayer(cc, dataReader, desc)
} else {
newDesc, resultReader, err = decryptLayer(cc, dataReader, desc, cryptoOp == cryptoOpUnwrapOnly)
}
if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, err
}
newDesc.Annotations = ocicrypt.FilterOutAnnotations(desc.Annotations)
// some operations, such as changing recipients, may not touch the layer at all
if resultReader != nil {
var ref string
// If we have the digest, write blob with checks
haveDigest := newDesc.Digest.String() != ""
if haveDigest {
ref = fmt.Sprintf("layer-%s", newDesc.Digest.String())
} else {
ref = fmt.Sprintf("blob-%d-%d", rand.Int(), rand.Int())
}
if haveDigest {
if err := content.WriteBlob(ctx, cs, ref, resultReader, newDesc); err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config")
}
} else {
newDesc.Digest, newDesc.Size, err = ingestReader(ctx, cs, ref, resultReader)
if err != nil {
return ocispec.Descriptor{}, err
}
}
}
// After performing encryption, call finalizer to get annotations
if encLayerFinalizer != nil {
annotations, err := encLayerFinalizer()
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "Error getting annotations from encLayer finalizer")
}
for k, v := range annotations {
newDesc.Annotations[k] = v
}
}
return newDesc, err
}
func ingestReader(ctx context.Context, cs content.Ingester, ref string, r io.Reader) (digest.Digest, int64, error) {
cw, err := content.OpenWriter(ctx, cs, content.WithRef(ref))
if err != nil {
return "", 0, errors.Wrap(err, "failed to open writer")
}
defer cw.Close()
if _, err := content.CopyReader(cw, r); err != nil {
return "", 0, errors.Wrap(err, "copy failed")
}
st, err := cw.Status()
if err != nil {
return "", 0, errors.Wrap(err, "failed to get state")
}
if err := cw.Commit(ctx, st.Offset, ""); err != nil {
if !errdefs.IsAlreadyExists(err) {
return "", 0, errors.Wrapf(err, "failed commit on ref %q", ref)
}
}
return cw.Digest(), st.Offset, nil
}
// Encrypt or decrypt all the Children of a given descriptor
func cryptChildren(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp, thisPlatform *ocispec.Platform) (ocispec.Descriptor, bool, error) {
children, err := images.Children(ctx, cs, desc)
if err != nil {
if errdefs.IsNotFound(err) {
return desc, false, nil
}
return ocispec.Descriptor{}, false, err
}
var newLayers []ocispec.Descriptor
var config ocispec.Descriptor
modified := false
for _, child := range children {
// we only encrypt child layers and have to update their parents if encryption happened
switch child.MediaType {
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
config = child
case images.MediaTypeDockerSchema2LayerGzip, images.MediaTypeDockerSchema2Layer,
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer:
if cryptoOp == cryptoOpEncrypt && lf(child) {
nl, err := cryptLayer(ctx, cs, child, cc, cryptoOp)
if err != nil {
return ocispec.Descriptor{}, false, err
}
modified = true
newLayers = append(newLayers, nl)
} else {
newLayers = append(newLayers, child)
}
case encocispec.MediaTypeLayerGzipEnc, encocispec.MediaTypeLayerEnc:
// this one can be decrypted but also its recipients list changed
if lf(child) {
nl, err := cryptLayer(ctx, cs, child, cc, cryptoOp)
if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err
}
modified = true
newLayers = append(newLayers, nl)
} else {
newLayers = append(newLayers, child)
}
case images.MediaTypeDockerSchema2LayerForeign, images.MediaTypeDockerSchema2LayerForeignGzip:
// never encrypt/decrypt
newLayers = append(newLayers, child)
default:
return ocispec.Descriptor{}, false, errors.Errorf("bad/unhandled MediaType %s in encryptChildren\n", child.MediaType)
}
}
if modified && len(newLayers) > 0 {
newManifest := ocispec.Manifest{
Versioned: specs.Versioned{
SchemaVersion: 2,
},
Config: config,
Layers: newLayers,
}
mb, err := json.MarshalIndent(newManifest, "", " ")
if err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to marshal image")
}
newDesc := ocispec.Descriptor{
MediaType: ocispec.MediaTypeImageManifest,
Size: int64(len(mb)),
Digest: digest.Canonical.FromBytes(mb),
Platform: desc.Platform,
}
labels := map[string]string{}
labels["containerd.io/gc.ref.content.0"] = newManifest.Config.Digest.String()
for i, ch := range newManifest.Layers {
labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i+1)] = ch.Digest.String()
}
ref := fmt.Sprintf("manifest-%s", newDesc.Digest.String())
if err := content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write config")
}
return newDesc, true, nil
}
return desc, modified, nil
}
// cryptManifest encrypts or decrypts the children of a top level manifest
func cryptManifest(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
p, err := content.ReadBlob(ctx, cs, desc)
if err != nil {
return ocispec.Descriptor{}, false, err
}
var manifest ocispec.Manifest
if err := json.Unmarshal(p, &manifest); err != nil {
return ocispec.Descriptor{}, false, err
}
platform := platforms.DefaultSpec()
newDesc, modified, err := cryptChildren(ctx, cs, desc, cc, lf, cryptoOp, &platform)
if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err
}
return newDesc, modified, nil
}
// cryptManifestList encrypts or decrypts the children of a top level manifest list
func cryptManifestList(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
// read the index; if any layer is encrypted and any manifests change we will need to rewrite it
b, err := content.ReadBlob(ctx, cs, desc)
if err != nil {
return ocispec.Descriptor{}, false, err
}
var index ocispec.Index
if err := json.Unmarshal(b, &index); err != nil {
return ocispec.Descriptor{}, false, err
}
var newManifests []ocispec.Descriptor
modified := false
for _, manifest := range index.Manifests {
newManifest, m, err := cryptChildren(ctx, cs, manifest, cc, lf, cryptoOp, manifest.Platform)
if err != nil || cryptoOp == cryptoOpUnwrapOnly {
return ocispec.Descriptor{}, false, err
}
if m {
modified = true
}
newManifests = append(newManifests, newManifest)
}
if modified {
// we need to update the index
newIndex := ocispec.Index{
Versioned: index.Versioned,
Manifests: newManifests,
}
mb, err := json.MarshalIndent(newIndex, "", " ")
if err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to marshal index")
}
newDesc := ocispec.Descriptor{
MediaType: ocispec.MediaTypeImageIndex,
Size: int64(len(mb)),
Digest: digest.Canonical.FromBytes(mb),
}
labels := map[string]string{}
for i, m := range newIndex.Manifests {
labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = m.Digest.String()
}
ref := fmt.Sprintf("index-%s", newDesc.Digest.String())
if err = content.WriteBlob(ctx, cs, ref, bytes.NewReader(mb), newDesc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, false, errors.Wrap(err, "failed to write index")
}
return newDesc, true, nil
}
return desc, false, nil
}
// cryptImage is the dispatcher to encrypt/decrypt an image; it accepts either an OCI descriptor
// representing a manifest list or a single manifest
func cryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter, cryptoOp cryptoOp) (ocispec.Descriptor, bool, error) {
if cc == nil {
return ocispec.Descriptor{}, false, errors.Wrapf(errdefs.ErrInvalidArgument, "CryptoConfig must not be nil")
}
switch desc.MediaType {
case ocispec.MediaTypeImageIndex, images.MediaTypeDockerSchema2ManifestList:
return cryptManifestList(ctx, cs, desc, cc, lf, cryptoOp)
case ocispec.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest:
return cryptManifest(ctx, cs, desc, cc, lf, cryptoOp)
default:
return ocispec.Descriptor{}, false, errors.Errorf("CryptImage: Unhandled media type: %s", desc.MediaType)
}
}
// EncryptImage encrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest
func EncryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter) (ocispec.Descriptor, bool, error) {
return cryptImage(ctx, cs, desc, cc, lf, cryptoOpEncrypt)
}
// DecryptImage decrypts an image; it accepts either an OCI descriptor representing a manifest list or a single manifest
func DecryptImage(ctx context.Context, cs content.Store, desc ocispec.Descriptor, cc *encconfig.CryptoConfig, lf LayerFilter) (ocispec.Descriptor, bool, error) {
return cryptImage(ctx, cs, desc, cc, lf, cryptoOpDecrypt)
}
// CheckAuthorization checks whether a user has the right keys to be allowed to access an image (every layer)
// It takes decrypting of the layers only as far as decrypting the asymmetrically encrypted data
// The decryption is only done for the current platform
func CheckAuthorization(ctx context.Context, cs content.Store, desc ocispec.Descriptor, dc *encconfig.DecryptConfig) error {
cc := encconfig.InitDecryption(dc.Parameters)
lf := func(desc ocispec.Descriptor) bool {
return true
}
_, _, err := cryptImage(ctx, cs, desc, &cc, lf, cryptoOpUnwrapOnly)
if err != nil {
return errors.Wrapf(err, "you are not authorized to use this image")
}
return nil
}

43
vendor/github.com/containerd/imgcrypt/payload.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
/*
Copyright The containerd Authors.
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.
*/
package imgcrypt
import (
"github.com/containerd/typeurl"
encconfig "github.com/containers/ocicrypt/config"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
const (
PayloadURI = "io.containerd.ocicrypt.v1.Payload"
)
var PayloadToolIDs = []string{
"io.containerd.ocicrypt.decoder.v1.tar",
"io.containerd.ocicrypt.decoder.v1.tar.gzip",
}
func init() {
typeurl.Register(&Payload{}, PayloadURI)
}
// Payload holds data that the external layer decryption tool
// needs for decrypting a layer
type Payload struct {
DecryptConfig encconfig.DecryptConfig
Descriptor ocispec.Descriptor
}

45
vendor/github.com/containerd/imgcrypt/vendor.conf generated vendored Normal file
View File

@@ -0,0 +1,45 @@
github.com/containerd/containerd v1.3.0
github.com/containerd/cgroups c4b9ac5c7601384c965b9646fc515884e091ebb9
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/continuity f2a389ac0a02ce21c09edd7344677a601970f41c
github.com/containerd/console 0650fd9eeb50bab4fc99dceb9f2e14cf58f36e7f
github.com/containerd/go-runc e029b79d8cda8374981c64eba71f28ec38e5526f
github.com/gogo/protobuf v1.2.1
github.com/gogo/googleapis v1.2.0
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-units v0.4.0
github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f
github.com/sirupsen/logrus v1.4.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 3e425f80a8c931f88e6d94a8c831b9d5aa481657
github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db
golang.org/x/sys 9eafafc0a87e0fd0aeeba439a4573537970c44c7 https://github.com/golang/sys
golang.org/x/net f3200d17e092c607f615320ecaad13d87ad9a2b3
golang.org/x/crypto 5c40567a22f818bd14a1ea7245dad9f8ef0691aa
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
github.com/pkg/errors v0.8.1
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
google.golang.org/grpc 6eaf6f47437a6b4e2153a190160ef39a92c7eceb # v1.23.0
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
github.com/golang/protobuf v1.2.0
github.com/containerd/ttrpc 92c8520ef9f86600c650dd540266a007bf03670f
github.com/urfave/cli v1.22.0
github.com/cpuguy83/go-md2man v1.0.10
github.com/russross/blackfriday v1.5.2
# image encryption dependencies
github.com/containers/ocicrypt b87a4a69c741007e2e8c713732c3e3da76f99dab
# windows
github.com/Microsoft/go-winio v0.4.14
github.com/Microsoft/hcsshim 8abdbb8205e4192c68b5f84c31197156f31be517
github.com/konsorten/go-windows-terminal-sequences v1.0.1
# encryption dependencies
gopkg.in/square/go-jose.v2 v2.3.1 https://github.com/square/go-jose.git
github.com/fullsailor/pkcs7 8306686428a5fe132eac8cb7c4848af725098bd4

189
vendor/github.com/containers/ocicrypt/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,189 @@
Apache License
Version 2.0, January 2004
https://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
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
https://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.

32
vendor/github.com/containers/ocicrypt/README.md generated vendored Normal file
View File

@@ -0,0 +1,32 @@
# OCIcrypt Library
The `ocicrypt` library is the OCI image spec implementation of container image encryption. More details of the spec can be seen in the [OCI repository](https://github.com/opencontainers/image-spec/pull/775). The purpose of this library is to encode spec structures and consts in code, as well as provide a consistent implementation of image encryption across container runtimes and build tools.
## Usage
There are various levels of usage for this library. The main consumers of these would be runtime/buil tools, and a more specific use would be in the ability to extend cryptographic function.
### Runtime/Build tool usage
The general exposed interface a runtime/build tool would use, would be to perform encryption or decryption of layers:
```
package "github.com/containers/ocicrypt"
func EncryptLayer(ec *config.EncryptConfig, encOrPlainLayerReader io.Reader, desc ocispec.Descriptor) (io.Reader, EncryptLayerFinalizer, error)
func DecryptLayer(dc *config.DecryptConfig, encLayerReader io.Reader, desc ocispec.Descriptor, unwrapOnly bool) (io.Reader, digest.Digest, error)
```
The settings/parameters to these functions can be specified via creation of an encryption config with the `github.com/containers/ocicrypt/config` package. We note that because setting of annotations and other fields of the layer descriptor is done through various means in different runtimes/build tools, it is the resposibility of the caller to still ensure that the layer descriptor follows the OCI specification (i.e. encoding, setting annotations, etc.).
### Crypto Agility and Extensibility
The implementation for both symmetric and assymetric encryption used in this library are behind 2 main interfaces, which users can extend if need be. These are in the following packages:
- github.com/containers/ocicrypt/blockcipher - LayerBlockCipher interface for block ciphers
- github.com/containers/ocicrypt/keywrap - KeyWrapper interface for key wrapping
We note that adding interfaces here is risky outside the OCI spec is not recommended, unless for very specialized and confined usecases. Please open an issue or PR if there is a general usecase that could be added to the OCI spec.
## Security Issues
We consider security issues related to this library critical. Please report and security related issues by emailing maintainers in the [MAINTAINERS](MAINTAINERS) file.

View File

@@ -0,0 +1,160 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package blockcipher
import (
"io"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
// LayerCipherType is the ciphertype as specified in the layer metadata
type LayerCipherType string
// TODO: Should be obtained from OCI spec once included
const (
AES256CTR LayerCipherType = "AES_256_CTR_HMAC_SHA256"
)
// PrivateLayerBlockCipherOptions includes the information required to encrypt/decrypt
// an image which are sensitive and should not be in plaintext
type PrivateLayerBlockCipherOptions struct {
// SymmetricKey represents the symmetric key used for encryption/decryption
// This field should be populated by Encrypt/Decrypt calls
SymmetricKey []byte `json:"symkey"`
// Digest is the digest of the original data for verification.
// This is NOT populated by Encrypt/Decrypt calls
Digest digest.Digest `json:"digest"`
// CipherOptions contains the cipher metadata used for encryption/decryption
// This field should be populated by Encrypt/Decrypt calls
CipherOptions map[string][]byte `json:"cipheroptions"`
}
// PublicLayerBlockCipherOptions includes the information required to encrypt/decrypt
// an image which are public and can be deduplicated in plaintext across multiple
// recipients
type PublicLayerBlockCipherOptions struct {
// CipherType denotes the cipher type according to the list of OCI suppported
// cipher types.
CipherType LayerCipherType `json:"cipher"`
// Hmac contains the hmac string to help verify encryption
Hmac []byte `json:"hmac"`
// CipherOptions contains the cipher metadata used for encryption/decryption
// This field should be populated by Encrypt/Decrypt calls
CipherOptions map[string][]byte `json:"cipheroptions"`
}
// LayerBlockCipherOptions contains the public and private LayerBlockCipherOptions
// required to encrypt/decrypt an image
type LayerBlockCipherOptions struct {
Public PublicLayerBlockCipherOptions
Private PrivateLayerBlockCipherOptions
}
// LayerBlockCipher returns a provider for encrypt/decrypt functionality
// for handling the layer data for a specific algorithm
type LayerBlockCipher interface {
// GenerateKey creates a symmetric key
GenerateKey() ([]byte, error)
// Encrypt takes in layer data and returns the ciphertext and relevant LayerBlockCipherOptions
Encrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, Finalizer, error)
// Decrypt takes in layer ciphertext data and returns the plaintext and relevant LayerBlockCipherOptions
Decrypt(layerDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error)
}
// LayerBlockCipherHandler is the handler for encrypt/decrypt for layers
type LayerBlockCipherHandler struct {
cipherMap map[LayerCipherType]LayerBlockCipher
}
// Finalizer is called after data blobs are written, and returns the LayerBlockCipherOptions for the encrypted blob
type Finalizer func() (LayerBlockCipherOptions, error)
// GetOpt returns the value of the cipher option and if the option exists
func (lbco LayerBlockCipherOptions) GetOpt(key string) (value []byte, ok bool) {
if v, ok := lbco.Public.CipherOptions[key]; ok {
return v, ok
} else if v, ok := lbco.Private.CipherOptions[key]; ok {
return v, ok
} else {
return nil, false
}
}
func wrapFinalizerWithType(fin Finalizer, typ LayerCipherType) Finalizer {
return func() (LayerBlockCipherOptions, error) {
lbco, err := fin()
if err != nil {
return LayerBlockCipherOptions{}, err
}
lbco.Public.CipherType = typ
return lbco, err
}
}
// Encrypt is the handler for the layer decryption routine
func (h *LayerBlockCipherHandler) Encrypt(plainDataReader io.Reader, typ LayerCipherType) (io.Reader, Finalizer, error) {
if c, ok := h.cipherMap[typ]; ok {
sk, err := c.GenerateKey()
if err != nil {
return nil, nil, err
}
opt := LayerBlockCipherOptions{
Private: PrivateLayerBlockCipherOptions{
SymmetricKey: sk,
},
}
encDataReader, fin, err := c.Encrypt(plainDataReader, opt)
if err == nil {
fin = wrapFinalizerWithType(fin, typ)
}
return encDataReader, fin, err
}
return nil, nil, errors.Errorf("unsupported cipher type: %s", typ)
}
// Decrypt is the handler for the layer decryption routine
func (h *LayerBlockCipherHandler) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error) {
typ := opt.Public.CipherType
if typ == "" {
return nil, LayerBlockCipherOptions{}, errors.New("no cipher type provided")
}
if c, ok := h.cipherMap[LayerCipherType(typ)]; ok {
return c.Decrypt(encDataReader, opt)
}
return nil, LayerBlockCipherOptions{}, errors.Errorf("unsupported cipher type: %s", typ)
}
// NewLayerBlockCipherHandler returns a new default handler
func NewLayerBlockCipherHandler() (*LayerBlockCipherHandler, error) {
h := LayerBlockCipherHandler{
cipherMap: map[LayerCipherType]LayerBlockCipher{},
}
var err error
h.cipherMap[AES256CTR], err = NewAESCTRLayerBlockCipher(256)
if err != nil {
return nil, errors.Wrap(err, "unable to set up Cipher AES-256-CTR")
}
return &h, nil
}

View File

@@ -0,0 +1,193 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package blockcipher
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"fmt"
"hash"
"io"
"github.com/containers/ocicrypt/utils"
"github.com/pkg/errors"
)
// AESCTRLayerBlockCipher implements the AES CTR stream cipher
type AESCTRLayerBlockCipher struct {
keylen int // in bytes
reader io.Reader
encrypt bool
stream cipher.Stream
err error
hmac hash.Hash
expHmac []byte
doneEncrypting bool
}
type aesctrcryptor struct {
bc *AESCTRLayerBlockCipher
}
// NewAESCTRLayerBlockCipher returns a new AES SIV block cipher of 256 or 512 bits
func NewAESCTRLayerBlockCipher(bits int) (LayerBlockCipher, error) {
if bits != 256 {
return nil, errors.New("AES CTR bit count not supported")
}
return &AESCTRLayerBlockCipher{keylen: bits / 8}, nil
}
func (r *aesctrcryptor) Read(p []byte) (int, error) {
var (
o int
)
if r.bc.err != nil {
return 0, r.bc.err
}
o, err := utils.FillBuffer(r.bc.reader, p)
if err != nil {
if err == io.EOF {
r.bc.err = err
} else {
return 0, err
}
}
if !r.bc.encrypt {
if _, err := r.bc.hmac.Write(p[:o]); err != nil {
r.bc.err = errors.Wrapf(err, "could not write to hmac")
return 0, r.bc.err
}
if r.bc.err == io.EOF {
// Before we return EOF we let the HMAC comparison
// provide a verdict
if !hmac.Equal(r.bc.hmac.Sum(nil), r.bc.expHmac) {
r.bc.err = fmt.Errorf("could not properly decrypt byte stream; exp hmac: '%x', actual hmac: '%s'", r.bc.expHmac, r.bc.hmac.Sum(nil))
return 0, r.bc.err
}
}
}
r.bc.stream.XORKeyStream(p[:o], p[:o])
if r.bc.encrypt {
if _, err := r.bc.hmac.Write(p[:o]); err != nil {
r.bc.err = errors.Wrapf(err, "could not write to hmac")
return 0, r.bc.err
}
if r.bc.err == io.EOF {
// Final data encrypted; Do the 'then-MAC' part
r.bc.doneEncrypting = true
}
}
return o, r.bc.err
}
// init initializes an instance
func (bc *AESCTRLayerBlockCipher) init(encrypt bool, reader io.Reader, opts LayerBlockCipherOptions) (LayerBlockCipherOptions, error) {
var (
err error
)
key := opts.Private.SymmetricKey
if len(key) != bc.keylen {
return LayerBlockCipherOptions{}, fmt.Errorf("invalid key length of %d bytes; need %d bytes", len(key), bc.keylen)
}
nonce, ok := opts.GetOpt("nonce")
if !ok {
nonce = make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return LayerBlockCipherOptions{}, errors.Wrap(err, "unable to generate random nonce")
}
}
block, err := aes.NewCipher(key)
if err != nil {
return LayerBlockCipherOptions{}, errors.Wrap(err, "aes.NewCipher failed")
}
bc.reader = reader
bc.encrypt = encrypt
bc.stream = cipher.NewCTR(block, nonce)
bc.err = nil
bc.hmac = hmac.New(sha256.New, key)
bc.expHmac = opts.Public.Hmac
bc.doneEncrypting = false
if !encrypt && len(bc.expHmac) == 0 {
return LayerBlockCipherOptions{}, errors.New("HMAC is not provided for decryption process")
}
lbco := LayerBlockCipherOptions{
Private: PrivateLayerBlockCipherOptions{
SymmetricKey: key,
CipherOptions: map[string][]byte{
"nonce": nonce,
},
},
}
return lbco, nil
}
// GenerateKey creates a synmmetric key
func (bc *AESCTRLayerBlockCipher) GenerateKey() ([]byte, error) {
key := make([]byte, bc.keylen)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return nil, err
}
return key, nil
}
// Encrypt takes in layer data and returns the ciphertext and relevant LayerBlockCipherOptions
func (bc *AESCTRLayerBlockCipher) Encrypt(plainDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, Finalizer, error) {
lbco, err := bc.init(true, plainDataReader, opt)
if err != nil {
return nil, nil, err
}
finalizer := func() (LayerBlockCipherOptions, error) {
if !bc.doneEncrypting {
return LayerBlockCipherOptions{}, errors.New("Read()ing not complete, unable to finalize")
}
if lbco.Public.CipherOptions == nil {
lbco.Public.CipherOptions = map[string][]byte{}
}
lbco.Public.Hmac = bc.hmac.Sum(nil)
return lbco, nil
}
return &aesctrcryptor{bc}, finalizer, nil
}
// Decrypt takes in layer ciphertext data and returns the plaintext and relevant LayerBlockCipherOptions
func (bc *AESCTRLayerBlockCipher) Decrypt(encDataReader io.Reader, opt LayerBlockCipherOptions) (io.Reader, LayerBlockCipherOptions, error) {
lbco, err := bc.init(false, encDataReader, opt)
if err != nil {
return nil, LayerBlockCipherOptions{}, err
}
return utils.NewDelayedReader(&aesctrcryptor{bc}, 1024*10), lbco, nil
}

114
vendor/github.com/containers/ocicrypt/config/config.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package config
// EncryptConfig is the container image PGP encryption configuration holding
// the identifiers of those that will be able to decrypt the container and
// the PGP public keyring file data that contains their public keys.
type EncryptConfig struct {
// map holding 'gpg-recipients', 'gpg-pubkeyringfile', 'pubkeys', 'x509s'
Parameters map[string][][]byte
DecryptConfig DecryptConfig
}
// DecryptConfig wraps the Parameters map that holds the decryption key
type DecryptConfig struct {
// map holding 'privkeys', 'x509s', 'gpg-privatekeys'
Parameters map[string][][]byte
}
// CryptoConfig is a common wrapper for EncryptConfig and DecrypConfig that can
// be passed through functions that share much code for encryption and decryption
type CryptoConfig struct {
EncryptConfig *EncryptConfig
DecryptConfig *DecryptConfig
}
// InitDecryption initialized a CryptoConfig object with parameters used for decryption
func InitDecryption(dcparameters map[string][][]byte) CryptoConfig {
return CryptoConfig{
DecryptConfig: &DecryptConfig{
Parameters: dcparameters,
},
}
}
// InitEncryption initializes a CryptoConfig object with parameters used for encryption
// It also takes dcparameters that may be needed for decryption when adding a recipient
// to an already encrypted image
func InitEncryption(parameters, dcparameters map[string][][]byte) CryptoConfig {
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: parameters,
DecryptConfig: DecryptConfig{
Parameters: dcparameters,
},
},
}
}
// CombineCryptoConfigs takes a CryptoConfig list and creates a single CryptoConfig
// containing the crypto configuration of all the key bundles
func CombineCryptoConfigs(ccs []CryptoConfig) CryptoConfig {
ecparam := map[string][][]byte{}
ecdcparam := map[string][][]byte{}
dcparam := map[string][][]byte{}
for _, cc := range ccs {
if ec := cc.EncryptConfig; ec != nil {
addToMap(ecparam, ec.Parameters)
addToMap(ecdcparam, ec.DecryptConfig.Parameters)
}
if dc := cc.DecryptConfig; dc != nil {
addToMap(dcparam, dc.Parameters)
}
}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ecparam,
DecryptConfig: DecryptConfig{
Parameters: ecdcparam,
},
},
DecryptConfig: &DecryptConfig{
Parameters: dcparam,
},
}
}
// AttachDecryptConfig adds DecryptConfig to the field of EncryptConfig so that
// the decryption parameters can be used to add recipients to an existing image
// if the user is able to decrypt it.
func (ec *EncryptConfig) AttachDecryptConfig(dc *DecryptConfig) {
if dc != nil {
addToMap(ec.DecryptConfig.Parameters, dc.Parameters)
}
}
func addToMap(orig map[string][][]byte, add map[string][][]byte) {
for k, v := range add {
if ov, ok := orig[k]; ok {
orig[k] = append(ov, v...)
} else {
orig[k] = v
}
}
}

View File

@@ -0,0 +1,134 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package config
import (
"github.com/pkg/errors"
)
// EncryptWithJwe returns a CryptoConfig to encrypt with jwe public keys
func EncryptWithJwe(pubKeys [][]byte) (CryptoConfig, error) {
dc := DecryptConfig{}
ep := map[string][][]byte{
"pubkeys": pubKeys,
}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ep,
DecryptConfig: dc,
},
DecryptConfig: &dc,
}, nil
}
// EncryptWithPkcs7 returns a CryptoConfig to encrypt with pkcs7 x509 certs
func EncryptWithPkcs7(x509s [][]byte) (CryptoConfig, error) {
dc := DecryptConfig{}
ep := map[string][][]byte{
"x509s": x509s,
}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ep,
DecryptConfig: dc,
},
DecryptConfig: &dc,
}, nil
}
// EncryptWithGpg returns a CryptoConfig to encrypt with configured gpg parameters
func EncryptWithGpg(gpgRecipients [][]byte, gpgPubRingFile []byte) (CryptoConfig, error) {
dc := DecryptConfig{}
ep := map[string][][]byte{
"gpg-recipients": gpgRecipients,
"gpg-pubkeyringfile": {gpgPubRingFile},
}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ep,
DecryptConfig: dc,
},
DecryptConfig: &dc,
}, nil
}
// DecryptWithPrivKeys returns a CryptoConfig to decrypt with configured private keys
func DecryptWithPrivKeys(privKeys [][]byte, privKeysPasswords [][]byte) (CryptoConfig, error) {
if len(privKeys) != len(privKeysPasswords) {
return CryptoConfig{}, errors.New("Length of privKeys should match length of privKeysPasswords")
}
dc := DecryptConfig{
Parameters: map[string][][]byte{
"privkeys": privKeys,
"privkeys-passwords": privKeysPasswords,
},
}
ep := map[string][][]byte{}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ep,
DecryptConfig: dc,
},
DecryptConfig: &dc,
}, nil
}
// DecryptWithX509s returns a CryptoConfig to decrypt with configured x509 certs
func DecryptWithX509s(x509s [][]byte) (CryptoConfig, error) {
dc := DecryptConfig{
Parameters: map[string][][]byte{
"x509s": x509s,
},
}
ep := map[string][][]byte{}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ep,
DecryptConfig: dc,
},
DecryptConfig: &dc,
}, nil
}
// DecryptWithGpgPrivKeys returns a CryptoConfig to decrypt with configured gpg private keys
func DecryptWithGpgPrivKeys(gpgPrivKeys, gpgPrivKeysPwds [][]byte) (CryptoConfig, error) {
dc := DecryptConfig{
Parameters: map[string][][]byte{
"gpg-privatekeys": gpgPrivKeys,
"gpg-privatekeys-passwords": gpgPrivKeysPwds,
},
}
ep := map[string][][]byte{}
return CryptoConfig{
EncryptConfig: &EncryptConfig{
Parameters: ep,
DecryptConfig: dc,
},
DecryptConfig: &dc,
}, nil
}

328
vendor/github.com/containers/ocicrypt/encryption.go generated vendored Normal file
View File

@@ -0,0 +1,328 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package ocicrypt
import (
"encoding/base64"
"encoding/json"
"io"
"strings"
"github.com/containers/ocicrypt/blockcipher"
"github.com/containers/ocicrypt/config"
"github.com/containers/ocicrypt/keywrap"
"github.com/containers/ocicrypt/keywrap/jwe"
"github.com/containers/ocicrypt/keywrap/pgp"
"github.com/containers/ocicrypt/keywrap/pkcs7"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// EncryptLayerFinalizer is a finalizer run to return the annotations to set for
// the encrypted layer
type EncryptLayerFinalizer func() (map[string]string, error)
func init() {
keyWrappers = make(map[string]keywrap.KeyWrapper)
keyWrapperAnnotations = make(map[string]string)
RegisterKeyWrapper("pgp", pgp.NewKeyWrapper())
RegisterKeyWrapper("jwe", jwe.NewKeyWrapper())
RegisterKeyWrapper("pkcs7", pkcs7.NewKeyWrapper())
}
var keyWrappers map[string]keywrap.KeyWrapper
var keyWrapperAnnotations map[string]string
// RegisterKeyWrapper allows to register key wrappers by their encryption scheme
func RegisterKeyWrapper(scheme string, iface keywrap.KeyWrapper) {
keyWrappers[scheme] = iface
keyWrapperAnnotations[iface.GetAnnotationID()] = scheme
}
// GetKeyWrapper looks up the encryptor interface given an encryption scheme (gpg, jwe)
func GetKeyWrapper(scheme string) keywrap.KeyWrapper {
return keyWrappers[scheme]
}
// GetWrappedKeysMap returns a map of wrappedKeys as values in a
// map with the encryption scheme(s) as the key(s)
func GetWrappedKeysMap(desc ocispec.Descriptor) map[string]string {
wrappedKeysMap := make(map[string]string)
for annotationsID, scheme := range keyWrapperAnnotations {
if annotation, ok := desc.Annotations[annotationsID]; ok {
wrappedKeysMap[scheme] = annotation
}
}
return wrappedKeysMap
}
// EncryptLayer encrypts the layer by running one encryptor after the other
func EncryptLayer(ec *config.EncryptConfig, encOrPlainLayerReader io.Reader, desc ocispec.Descriptor) (io.Reader, EncryptLayerFinalizer, error) {
var (
encLayerReader io.Reader
err error
encrypted bool
bcFin blockcipher.Finalizer
privOptsData []byte
pubOptsData []byte
)
if ec == nil {
return nil, nil, errors.New("EncryptConfig must not be nil")
}
for annotationsID := range keyWrapperAnnotations {
annotation := desc.Annotations[annotationsID]
if annotation != "" {
privOptsData, err = decryptLayerKeyOptsData(&ec.DecryptConfig, desc)
if err != nil {
return nil, nil, err
}
pubOptsData, err = getLayerPubOpts(desc)
if err != nil {
return nil, nil, err
}
// already encrypted!
encrypted = true
}
}
if !encrypted {
encLayerReader, bcFin, err = commonEncryptLayer(encOrPlainLayerReader, desc.Digest, blockcipher.AES256CTR)
if err != nil {
return nil, nil, err
}
}
encLayerFinalizer := func() (map[string]string, error) {
// If layer was already encrypted, bcFin should be nil, use existing optsData
if bcFin != nil {
opts, err := bcFin()
if err != nil {
return nil, err
}
privOptsData, err = json.Marshal(opts.Private)
if err != nil {
return nil, errors.Wrapf(err, "could not JSON marshal opts")
}
pubOptsData, err = json.Marshal(opts.Public)
if err != nil {
return nil, errors.Wrapf(err, "could not JSON marshal opts")
}
}
newAnnotations := make(map[string]string)
for annotationsID, scheme := range keyWrapperAnnotations {
b64Annotations := desc.Annotations[annotationsID]
keywrapper := GetKeyWrapper(scheme)
b64Annotations, err = preWrapKeys(keywrapper, ec, b64Annotations, privOptsData)
if err != nil {
return nil, err
}
if b64Annotations != "" {
newAnnotations[annotationsID] = b64Annotations
}
}
newAnnotations["org.opencontainers.image.enc.pubopts"] = base64.StdEncoding.EncodeToString(pubOptsData)
if len(newAnnotations) == 0 {
return nil, errors.New("no encryptor found to handle encryption")
}
return newAnnotations, err
}
// if nothing was encrypted, we just return encLayer = nil
return encLayerReader, encLayerFinalizer, err
}
// preWrapKeys calls WrapKeys and handles the base64 encoding and concatenation of the
// annotation data
func preWrapKeys(keywrapper keywrap.KeyWrapper, ec *config.EncryptConfig, b64Annotations string, optsData []byte) (string, error) {
newAnnotation, err := keywrapper.WrapKeys(ec, optsData)
if err != nil || len(newAnnotation) == 0 {
return b64Annotations, err
}
b64newAnnotation := base64.StdEncoding.EncodeToString(newAnnotation)
if b64Annotations == "" {
return b64newAnnotation, nil
}
return b64Annotations + "," + b64newAnnotation, nil
}
// DecryptLayer decrypts a layer trying one keywrap.KeyWrapper after the other to see whether it
// can apply the provided private key
// If unwrapOnly is set we will only try to decrypt the layer encryption key and return
func DecryptLayer(dc *config.DecryptConfig, encLayerReader io.Reader, desc ocispec.Descriptor, unwrapOnly bool) (io.Reader, digest.Digest, error) {
if dc == nil {
return nil, "", errors.New("DecryptConfig must not be nil")
}
privOptsData, err := decryptLayerKeyOptsData(dc, desc)
if err != nil || unwrapOnly {
return nil, "", err
}
var pubOptsData []byte
pubOptsData, err = getLayerPubOpts(desc)
if err != nil {
return nil, "", err
}
return commonDecryptLayer(encLayerReader, privOptsData, pubOptsData)
}
func decryptLayerKeyOptsData(dc *config.DecryptConfig, desc ocispec.Descriptor) ([]byte, error) {
privKeyGiven := false
for annotationsID, scheme := range keyWrapperAnnotations {
b64Annotation := desc.Annotations[annotationsID]
if b64Annotation != "" {
keywrapper := GetKeyWrapper(scheme)
if keywrapper.NoPossibleKeys(dc.Parameters) {
continue
}
if len(keywrapper.GetPrivateKeys(dc.Parameters)) > 0 {
privKeyGiven = true
}
optsData, err := preUnwrapKey(keywrapper, dc, b64Annotation)
if err != nil {
// try next keywrap.KeyWrapper
continue
}
if optsData == nil {
// try next keywrap.KeyWrapper
continue
}
return optsData, nil
}
}
if !privKeyGiven {
return nil, errors.New("missing private key needed for decryption")
}
return nil, errors.Errorf("no suitable key unwrapper found or none of the private keys could be used for decryption")
}
func getLayerPubOpts(desc ocispec.Descriptor) ([]byte, error) {
pubOptsString := desc.Annotations["org.opencontainers.image.enc.pubopts"]
if pubOptsString == "" {
return json.Marshal(blockcipher.PublicLayerBlockCipherOptions{})
}
return base64.StdEncoding.DecodeString(pubOptsString)
}
// preUnwrapKey decodes the comma separated base64 strings and calls the Unwrap function
// of the given keywrapper with it and returns the result in case the Unwrap functions
// does not return an error. If all attempts fail, an error is returned.
func preUnwrapKey(keywrapper keywrap.KeyWrapper, dc *config.DecryptConfig, b64Annotations string) ([]byte, error) {
if b64Annotations == "" {
return nil, nil
}
for _, b64Annotation := range strings.Split(b64Annotations, ",") {
annotation, err := base64.StdEncoding.DecodeString(b64Annotation)
if err != nil {
return nil, errors.New("could not base64 decode the annotation")
}
optsData, err := keywrapper.UnwrapKey(dc, annotation)
if err != nil {
continue
}
return optsData, nil
}
return nil, errors.New("no suitable key found for decrypting layer key")
}
// commonEncryptLayer is a function to encrypt the plain layer using a new random
// symmetric key and return the LayerBlockCipherHandler's JSON in string form for
// later use during decryption
func commonEncryptLayer(plainLayerReader io.Reader, d digest.Digest, typ blockcipher.LayerCipherType) (io.Reader, blockcipher.Finalizer, error) {
lbch, err := blockcipher.NewLayerBlockCipherHandler()
if err != nil {
return nil, nil, err
}
encLayerReader, bcFin, err := lbch.Encrypt(plainLayerReader, typ)
if err != nil {
return nil, nil, err
}
newBcFin := func() (blockcipher.LayerBlockCipherOptions, error) {
lbco, err := bcFin()
if err != nil {
return blockcipher.LayerBlockCipherOptions{}, err
}
lbco.Private.Digest = d
return lbco, nil
}
return encLayerReader, newBcFin, err
}
// commonDecryptLayer decrypts an encrypted layer previously encrypted with commonEncryptLayer
// by passing along the optsData
func commonDecryptLayer(encLayerReader io.Reader, privOptsData []byte, pubOptsData []byte) (io.Reader, digest.Digest, error) {
privOpts := blockcipher.PrivateLayerBlockCipherOptions{}
err := json.Unmarshal(privOptsData, &privOpts)
if err != nil {
return nil, "", errors.Wrapf(err, "could not JSON unmarshal privOptsData")
}
lbch, err := blockcipher.NewLayerBlockCipherHandler()
if err != nil {
return nil, "", err
}
pubOpts := blockcipher.PublicLayerBlockCipherOptions{}
if len(pubOptsData) > 0 {
err := json.Unmarshal(pubOptsData, &pubOpts)
if err != nil {
return nil, "", errors.Wrapf(err, "could not JSON unmarshal pubOptsData")
}
}
opts := blockcipher.LayerBlockCipherOptions{
Private: privOpts,
Public: pubOpts,
}
plainLayerReader, opts, err := lbch.Decrypt(encLayerReader, opts)
if err != nil {
return nil, "", err
}
return plainLayerReader, opts.Private.Digest, nil
}
// FilterOutAnnotations filters out the annotations belonging to the image encryption 'namespace'
// and returns a map with those taken out
func FilterOutAnnotations(annotations map[string]string) map[string]string {
a := make(map[string]string)
if len(annotations) > 0 {
for k, v := range annotations {
if strings.HasPrefix(k, "org.opencontainers.image.enc.") {
continue
}
a[k] = v
}
}
return a
}

18
vendor/github.com/containers/ocicrypt/go.mod generated vendored Normal file
View File

@@ -0,0 +1,18 @@
module github.com/containers/ocicrypt
go 1.12
require (
github.com/containerd/containerd v1.2.10
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1
github.com/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/stretchr/testify v1.3.0 // indirect
github.com/urfave/cli v1.22.1
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
google.golang.org/grpc v1.24.0 // indirect
gopkg.in/square/go-jose.v2 v2.3.1
gotest.tools v2.2.0+incompatible // indirect
)

425
vendor/github.com/containers/ocicrypt/gpg.go generated vendored Normal file
View File

@@ -0,0 +1,425 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package ocicrypt
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
)
// GPGVersion enum representing the GPG client version to use.
type GPGVersion int
const (
// GPGv2 signifies gpgv2+
GPGv2 GPGVersion = iota
// GPGv1 signifies gpgv1+
GPGv1
// GPGVersionUndetermined signifies gpg client version undetermined
GPGVersionUndetermined
)
// GPGClient defines an interface for wrapping the gpg command line tools
type GPGClient interface {
// ReadGPGPubRingFile gets the byte sequence of the gpg public keyring
ReadGPGPubRingFile() ([]byte, error)
// GetGPGPrivateKey gets the private key bytes of a keyid given a passphrase
GetGPGPrivateKey(keyid uint64, passphrase string) ([]byte, error)
// GetSecretKeyDetails gets the details of a secret key
GetSecretKeyDetails(keyid uint64) ([]byte, bool, error)
// GetKeyDetails gets the details of a public key
GetKeyDetails(keyid uint64) ([]byte, bool, error)
// ResolveRecipients resolves PGP key ids to user names
ResolveRecipients([]string) []string
}
// gpgClient contains generic gpg client information
type gpgClient struct {
gpgHomeDir string
}
// gpgv2Client is a gpg2 client
type gpgv2Client struct {
gpgClient
}
// gpgv1Client is a gpg client
type gpgv1Client struct {
gpgClient
}
// GuessGPGVersion guesses the version of gpg. Defaults to gpg2 if exists, if
// not defaults to regular gpg.
func GuessGPGVersion() GPGVersion {
if err := exec.Command("gpg2", "--version").Run(); err == nil {
return GPGv2
} else if err := exec.Command("gpg", "--version").Run(); err == nil {
return GPGv1
} else {
return GPGVersionUndetermined
}
}
// NewGPGClient creates a new GPGClient object representing the given version
// and using the given home directory
func NewGPGClient(gpgVersion, gpgHomeDir string) (GPGClient, error) {
v := new(GPGVersion)
switch gpgVersion {
case "v1":
*v = GPGv1
case "v2":
*v = GPGv2
default:
v = nil
}
return newGPGClient(v, gpgHomeDir)
}
func newGPGClient(version *GPGVersion, homedir string) (GPGClient, error) {
var gpgVersion GPGVersion
if version != nil {
gpgVersion = *version
} else {
gpgVersion = GuessGPGVersion()
}
switch gpgVersion {
case GPGv1:
return &gpgv1Client{
gpgClient: gpgClient{gpgHomeDir: homedir},
}, nil
case GPGv2:
return &gpgv2Client{
gpgClient: gpgClient{gpgHomeDir: homedir},
}, nil
case GPGVersionUndetermined:
return nil, fmt.Errorf("unable to determine GPG version")
default:
return nil, fmt.Errorf("unhandled case: NewGPGClient")
}
}
// GetGPGPrivateKey gets the bytes of a specified keyid, supplying a passphrase
func (gc *gpgv2Client) GetGPGPrivateKey(keyid uint64, passphrase string) ([]byte, error) {
var args []string
if gc.gpgHomeDir != "" {
args = append(args, []string{"--homedir", gc.gpgHomeDir}...)
}
rfile, wfile, err := os.Pipe()
if err != nil {
return nil, errors.Wrapf(err, "could not create pipe")
}
defer func() {
rfile.Close()
wfile.Close()
}()
// fill pipe in background
go func(passphrase string) {
_, _ = wfile.Write([]byte(passphrase))
wfile.Close()
}(passphrase)
args = append(args, []string{"--pinentry-mode", "loopback", "--batch", "--passphrase-fd", fmt.Sprintf("%d", 3), "--export-secret-key", fmt.Sprintf("0x%x", keyid)}...)
cmd := exec.Command("gpg2", args...)
cmd.ExtraFiles = []*os.File{rfile}
return runGPGGetOutput(cmd)
}
// ReadGPGPubRingFile reads the GPG public key ring file
func (gc *gpgv2Client) ReadGPGPubRingFile() ([]byte, error) {
var args []string
if gc.gpgHomeDir != "" {
args = append(args, []string{"--homedir", gc.gpgHomeDir}...)
}
args = append(args, []string{"--batch", "--export"}...)
cmd := exec.Command("gpg2", args...)
return runGPGGetOutput(cmd)
}
func (gc *gpgv2Client) getKeyDetails(option string, keyid uint64) ([]byte, bool, error) {
var args []string
if gc.gpgHomeDir != "" {
args = append([]string{"--homedir", gc.gpgHomeDir})
}
args = append(args, option, fmt.Sprintf("0x%x", keyid))
cmd := exec.Command("gpg2", args...)
keydata, err := runGPGGetOutput(cmd)
return keydata, err == nil, err
}
// GetSecretKeyDetails retrives the secret key details of key with keyid.
// returns a byte array of the details and a bool if the key exists
func (gc *gpgv2Client) GetSecretKeyDetails(keyid uint64) ([]byte, bool, error) {
return gc.getKeyDetails("-K", keyid)
}
// GetKeyDetails retrives the public key details of key with keyid.
// returns a byte array of the details and a bool if the key exists
func (gc *gpgv2Client) GetKeyDetails(keyid uint64) ([]byte, bool, error) {
return gc.getKeyDetails("-k", keyid)
}
// ResolveRecipients converts PGP keyids to email addresses, if possible
func (gc *gpgv2Client) ResolveRecipients(recipients []string) []string {
return resolveRecipients(gc, recipients)
}
// GetGPGPrivateKey gets the bytes of a specified keyid, supplying a passphrase
func (gc *gpgv1Client) GetGPGPrivateKey(keyid uint64, _ string) ([]byte, error) {
var args []string
if gc.gpgHomeDir != "" {
args = append(args, []string{"--homedir", gc.gpgHomeDir}...)
}
args = append(args, []string{"--batch", "--export-secret-key", fmt.Sprintf("0x%x", keyid)}...)
cmd := exec.Command("gpg", args...)
return runGPGGetOutput(cmd)
}
// ReadGPGPubRingFile reads the GPG public key ring file
func (gc *gpgv1Client) ReadGPGPubRingFile() ([]byte, error) {
var args []string
if gc.gpgHomeDir != "" {
args = append(args, []string{"--homedir", gc.gpgHomeDir}...)
}
args = append(args, []string{"--batch", "--export"}...)
cmd := exec.Command("gpg", args...)
return runGPGGetOutput(cmd)
}
func (gc *gpgv1Client) getKeyDetails(option string, keyid uint64) ([]byte, bool, error) {
var args []string
if gc.gpgHomeDir != "" {
args = append([]string{"--homedir", gc.gpgHomeDir})
}
args = append(args, option, fmt.Sprintf("0x%x", keyid))
cmd := exec.Command("gpg", args...)
keydata, err := runGPGGetOutput(cmd)
return keydata, err == nil, err
}
// GetSecretKeyDetails retrives the secret key details of key with keyid.
// returns a byte array of the details and a bool if the key exists
func (gc *gpgv1Client) GetSecretKeyDetails(keyid uint64) ([]byte, bool, error) {
return gc.getKeyDetails("-K", keyid)
}
// GetKeyDetails retrives the public key details of key with keyid.
// returns a byte array of the details and a bool if the key exists
func (gc *gpgv1Client) GetKeyDetails(keyid uint64) ([]byte, bool, error) {
return gc.getKeyDetails("-k", keyid)
}
// ResolveRecipients converts PGP keyids to email addresses, if possible
func (gc *gpgv1Client) ResolveRecipients(recipients []string) []string {
return resolveRecipients(gc, recipients)
}
// runGPGGetOutput runs the GPG commandline and returns stdout as byte array
// and any stderr in the error
func runGPGGetOutput(cmd *exec.Cmd) ([]byte, error) {
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
stdoutstr, err2 := ioutil.ReadAll(stdout)
stderrstr, _ := ioutil.ReadAll(stderr)
if err := cmd.Wait(); err != nil {
return nil, fmt.Errorf("error from %s: %s", cmd.Path, string(stderrstr))
}
return stdoutstr, err2
}
// resolveRecipients walks the list of recipients and attempts to convert
// all keyIds to email addresses; if something goes wrong during the
// conversion of a recipient, the original string is returned for that
// recpient
func resolveRecipients(gc GPGClient, recipients []string) []string {
var result []string
for _, recipient := range recipients {
keyID, err := strconv.ParseUint(recipient, 0, 64)
if err != nil {
result = append(result, recipient)
} else {
details, found, _ := gc.GetKeyDetails(keyID)
if !found {
result = append(result, recipient)
} else {
email := extractEmailFromDetails(details)
if email == "" {
result = append(result, recipient)
} else {
result = append(result, email)
}
}
}
}
return result
}
var emailPattern = regexp.MustCompile(`uid\s+\[.*\]\s.*\s<(?P<email>.+)>`)
func extractEmailFromDetails(details []byte) string {
loc := emailPattern.FindSubmatchIndex(details)
if len(loc) == 0 {
return ""
}
return string(emailPattern.Expand(nil, []byte("$email"), details, loc))
}
// uint64ToStringArray converts an array of uint64's to an array of strings
// by applying a format string to each uint64
func uint64ToStringArray(format string, in []uint64) []string {
var ret []string
for _, v := range in {
ret = append(ret, fmt.Sprintf(format, v))
}
return ret
}
// GPGGetPrivateKey walks the list of layerInfos and tries to decrypt the
// wrapped symmetric keys. For this it determines whether a private key is
// in the GPGVault or on this system and prompts for the passwords for those
// that are available. If we do not find a private key on the system for
// getting to the symmetric key of a layer then an error is generated.
func GPGGetPrivateKey(descs []ocispec.Descriptor, gpgClient GPGClient, gpgVault GPGVault, mustFindKey bool) (gpgPrivKeys [][]byte, gpgPrivKeysPwds [][]byte, err error) {
// PrivateKeyData describes a private key
type PrivateKeyData struct {
KeyData []byte
KeyDataPassword []byte
}
var pkd PrivateKeyData
keyIDPasswordMap := make(map[uint64]PrivateKeyData)
for _, desc := range descs {
for scheme, b64pgpPackets := range GetWrappedKeysMap(desc) {
if scheme != "pgp" {
continue
}
keywrapper := GetKeyWrapper(scheme)
if keywrapper == nil {
return nil, nil, errors.Errorf("could not get KeyWrapper for %s\n", scheme)
}
keyIds, err := keywrapper.GetKeyIdsFromPacket(b64pgpPackets)
if err != nil {
return nil, nil, err
}
found := false
for _, keyid := range keyIds {
// do we have this key? -- first check the vault
if gpgVault != nil {
_, keydata := gpgVault.GetGPGPrivateKey(keyid)
if len(keydata) > 0 {
pkd = PrivateKeyData{
KeyData: keydata,
KeyDataPassword: nil, // password not supported in this case
}
keyIDPasswordMap[keyid] = pkd
found = true
break
}
} else if gpgClient != nil {
// check the local system's gpg installation
keyinfo, haveKey, _ := gpgClient.GetSecretKeyDetails(keyid)
// this may fail if the key is not here; we ignore the error
if !haveKey {
// key not on this system
continue
}
_, found = keyIDPasswordMap[keyid]
if !found {
fmt.Printf("Passphrase required for Key id 0x%x: \n%v", keyid, string(keyinfo))
fmt.Printf("Enter passphrase for key with Id 0x%x: ", keyid)
password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
fmt.Printf("\n")
if err != nil {
return nil, nil, err
}
keydata, err := gpgClient.GetGPGPrivateKey(keyid, string(password))
if err != nil {
return nil, nil, err
}
pkd = PrivateKeyData{
KeyData: keydata,
KeyDataPassword: password,
}
keyIDPasswordMap[keyid] = pkd
found = true
}
break
} else {
return nil, nil, errors.New("no GPGVault or GPGClient passed")
}
}
if !found && len(b64pgpPackets) > 0 && mustFindKey {
ids := uint64ToStringArray("0x%x", keyIds)
return nil, nil, errors.Errorf("missing key for decryption of layer %x of %s. Need one of the following keys: %s", desc.Digest, desc.Platform, strings.Join(ids, ", "))
}
}
}
for _, pkd := range keyIDPasswordMap {
gpgPrivKeys = append(gpgPrivKeys, pkd.KeyData)
gpgPrivKeysPwds = append(gpgPrivKeysPwds, pkd.KeyDataPassword)
}
return gpgPrivKeys, gpgPrivKeysPwds, nil
}

100
vendor/github.com/containers/ocicrypt/gpgvault.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package ocicrypt
import (
"bytes"
"io/ioutil"
"github.com/pkg/errors"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
// GPGVault defines an interface for wrapping multiple secret key rings
type GPGVault interface {
// AddSecretKeyRingData adds a secret keyring via its raw byte array
AddSecretKeyRingData(gpgSecretKeyRingData []byte) error
// AddSecretKeyRingDataArray adds secret keyring via its raw byte arrays
AddSecretKeyRingDataArray(gpgSecretKeyRingDataArray [][]byte) error
// AddSecretKeyRingFiles adds secret keyrings given their filenames
AddSecretKeyRingFiles(filenames []string) error
// GetGPGPrivateKey gets the private key bytes of a keyid given a passphrase
GetGPGPrivateKey(keyid uint64) ([]openpgp.Key, []byte)
}
// gpgVault wraps an array of gpgSecretKeyRing
type gpgVault struct {
entityLists []openpgp.EntityList
keyDataList [][]byte // the raw data original passed in
}
// NewGPGVault creates an empty GPGVault
func NewGPGVault() GPGVault {
return &gpgVault{}
}
// AddSecretKeyRingData adds a secret keyring's to the gpgVault; the raw byte
// array read from the file must be passed and will be parsed by this function
func (g *gpgVault) AddSecretKeyRingData(gpgSecretKeyRingData []byte) error {
// read the private keys
r := bytes.NewReader(gpgSecretKeyRingData)
entityList, err := openpgp.ReadKeyRing(r)
if err != nil {
return errors.Wrapf(err, "could not read keyring")
}
g.entityLists = append(g.entityLists, entityList)
g.keyDataList = append(g.keyDataList, gpgSecretKeyRingData)
return nil
}
// AddSecretKeyRingDataArray adds secret keyrings to the gpgVault; the raw byte
// arrays read from files must be passed
func (g *gpgVault) AddSecretKeyRingDataArray(gpgSecretKeyRingDataArray [][]byte) error {
for _, gpgSecretKeyRingData := range gpgSecretKeyRingDataArray {
if err := g.AddSecretKeyRingData(gpgSecretKeyRingData); err != nil {
return err
}
}
return nil
}
// AddSecretKeyRingFiles adds the secret key rings given their filenames
func (g *gpgVault) AddSecretKeyRingFiles(filenames []string) error {
for _, filename := range filenames {
gpgSecretKeyRingData, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
err = g.AddSecretKeyRingData(gpgSecretKeyRingData)
if err != nil {
return err
}
}
return nil
}
// GetGPGPrivateKey gets the bytes of a specified keyid, supplying a passphrase
func (g *gpgVault) GetGPGPrivateKey(keyid uint64) ([]openpgp.Key, []byte) {
for i, el := range g.entityLists {
decKeys := el.KeysByIdUsage(keyid, packet.KeyFlagEncryptCommunications)
if len(decKeys) > 0 {
return decKeys, g.keyDataList[i]
}
}
return nil, nil
}

View File

@@ -0,0 +1,136 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package jwe
import (
"crypto/ecdsa"
"github.com/containers/ocicrypt/config"
"github.com/containers/ocicrypt/keywrap"
"github.com/containers/ocicrypt/utils"
"github.com/pkg/errors"
jose "gopkg.in/square/go-jose.v2"
)
type jweKeyWrapper struct {
}
func (kw *jweKeyWrapper) GetAnnotationID() string {
return "org.opencontainers.image.enc.keys.jwe"
}
// NewKeyWrapper returns a new key wrapping interface using jwe
func NewKeyWrapper() keywrap.KeyWrapper {
return &jweKeyWrapper{}
}
// WrapKeys wraps the session key for recpients and encrypts the optsData, which
// describe the symmetric key used for encrypting the layer
func (kw *jweKeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([]byte, error) {
var joseRecipients []jose.Recipient
err := addPubKeys(&joseRecipients, ec.Parameters["pubkeys"])
if err != nil {
return nil, err
}
// no recipients is not an error...
if len(joseRecipients) == 0 {
return nil, nil
}
encrypter, err := jose.NewMultiEncrypter(jose.A256GCM, joseRecipients, nil)
if err != nil {
return nil, errors.Wrapf(err, "jose.NewMultiEncrypter failed")
}
jwe, err := encrypter.Encrypt(optsData)
if err != nil {
return nil, errors.Wrapf(err, "JWE Encrypt failed")
}
return []byte(jwe.FullSerialize()), nil
}
func (kw *jweKeyWrapper) UnwrapKey(dc *config.DecryptConfig, jweString []byte) ([]byte, error) {
jwe, err := jose.ParseEncrypted(string(jweString))
if err != nil {
return nil, errors.New("jose.ParseEncrypted failed")
}
privKeys := kw.GetPrivateKeys(dc.Parameters)
if len(privKeys) == 0 {
return nil, errors.New("No private keys found for JWE decryption")
}
privKeysPasswords := kw.getPrivateKeysPasswords(dc.Parameters)
if len(privKeysPasswords) != len(privKeys) {
return nil, errors.New("Private key password array length must be same as that of private keys")
}
for idx, privKey := range privKeys {
key, err := utils.ParsePrivateKey(privKey, privKeysPasswords[idx], "JWE")
if err != nil {
return nil, err
}
_, _, plain, err := jwe.DecryptMulti(key)
if err == nil {
return plain, nil
}
}
return nil, errors.New("JWE: No suitable private key found for decryption")
}
func (kw *jweKeyWrapper) NoPossibleKeys(dcparameters map[string][][]byte) bool {
return len(kw.GetPrivateKeys(dcparameters)) == 0
}
func (kw *jweKeyWrapper) GetPrivateKeys(dcparameters map[string][][]byte) [][]byte {
return dcparameters["privkeys"]
}
func (kw *jweKeyWrapper) getPrivateKeysPasswords(dcparameters map[string][][]byte) [][]byte {
return dcparameters["privkeys-passwords"]
}
func (kw *jweKeyWrapper) GetKeyIdsFromPacket(b64jwes string) ([]uint64, error) {
return nil, nil
}
func (kw *jweKeyWrapper) GetRecipients(b64jwes string) ([]string, error) {
return []string{"[jwe]"}, nil
}
func addPubKeys(joseRecipients *[]jose.Recipient, pubKeys [][]byte) error {
if len(pubKeys) == 0 {
return nil
}
for _, pubKey := range pubKeys {
key, err := utils.ParsePublicKey(pubKey, "JWE")
if err != nil {
return err
}
alg := jose.RSA_OAEP
switch key.(type) {
case *ecdsa.PublicKey:
alg = jose.ECDH_ES_A256KW
}
*joseRecipients = append(*joseRecipients, jose.Recipient{
Algorithm: alg,
Key: key,
})
}
return nil
}

View File

@@ -0,0 +1,48 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package keywrap
import (
"github.com/containers/ocicrypt/config"
)
// KeyWrapper is the interface used for wrapping keys using
// a specific encryption technology (pgp, jwe)
type KeyWrapper interface {
WrapKeys(ec *config.EncryptConfig, optsData []byte) ([]byte, error)
UnwrapKey(dc *config.DecryptConfig, annotation []byte) ([]byte, error)
GetAnnotationID() string
// NoPossibleKeys returns true if there is no possibility of performing
// decryption for parameters provided.
NoPossibleKeys(dcparameters map[string][][]byte) bool
// GetPrivateKeys (optional) gets the array of private keys. It is an optional implementation
// as in some key services, a private key may not be exportable (i.e. HSM)
// If not implemented, return nil
GetPrivateKeys(dcparameters map[string][][]byte) [][]byte
// GetKeyIdsFromPacket (optional) gets a list of key IDs. This is optional as some encryption
// schemes may not have a notion of key IDs
// If not implemented, return the nil slice
GetKeyIdsFromPacket(packet string) ([]uint64, error)
// GetRecipients (optional) gets a list of recipients. It is optional due to the validity of
// recipients in a particular encryptiong scheme
// If not implemented, return the nil slice
GetRecipients(packet string) ([]string, error)
}

View File

@@ -0,0 +1,273 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package pgp
import (
"bytes"
"crypto"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/mail"
"strconv"
"strings"
"github.com/containers/ocicrypt/config"
"github.com/containers/ocicrypt/keywrap"
"github.com/pkg/errors"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
type gpgKeyWrapper struct {
}
// NewKeyWrapper returns a new key wrapping interface for pgp
func NewKeyWrapper() keywrap.KeyWrapper {
return &gpgKeyWrapper{}
}
var (
// GPGDefaultEncryptConfig is the default configuration for layer encryption/decryption
GPGDefaultEncryptConfig = &packet.Config{
Rand: rand.Reader,
DefaultHash: crypto.SHA256,
DefaultCipher: packet.CipherAES256,
CompressionConfig: &packet.CompressionConfig{Level: 0}, // No compression
RSABits: 2048,
}
)
func (kw *gpgKeyWrapper) GetAnnotationID() string {
return "org.opencontainers.image.enc.keys.pgp"
}
// WrapKeys wraps the session key for recpients and encrypts the optsData, which
// describe the symmetric key used for encrypting the layer
func (kw *gpgKeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([]byte, error) {
ciphertext := new(bytes.Buffer)
el, err := kw.createEntityList(ec)
if err != nil {
return nil, errors.Wrap(err, "unable to create entity list")
}
if len(el) == 0 {
// nothing to do -- not an error
return nil, nil
}
plaintextWriter, err := openpgp.Encrypt(ciphertext,
el, /*EntityList*/
nil, /* Sign*/
nil, /* FileHint */
GPGDefaultEncryptConfig)
if err != nil {
return nil, err
}
if _, err = plaintextWriter.Write(optsData); err != nil {
return nil, err
} else if err = plaintextWriter.Close(); err != nil {
return nil, err
}
return ciphertext.Bytes(), err
}
// UnwrapKey unwraps the symmetric key with which the layer is encrypted
// This symmetric key is encrypted in the PGP payload.
func (kw *gpgKeyWrapper) UnwrapKey(dc *config.DecryptConfig, pgpPacket []byte) ([]byte, error) {
pgpPrivateKeys, pgpPrivateKeysPwd, err := kw.getKeyParameters(dc.Parameters)
if err != nil {
return nil, err
}
for idx, pgpPrivateKey := range pgpPrivateKeys {
r := bytes.NewBuffer(pgpPrivateKey)
entityList, err := openpgp.ReadKeyRing(r)
if err != nil {
return nil, errors.Wrap(err, "unable to parse private keys")
}
var prompt openpgp.PromptFunction
if len(pgpPrivateKeysPwd) > idx {
responded := false
prompt = func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
if responded {
return nil, fmt.Errorf("don't seem to have the right password")
}
responded = true
for _, key := range keys {
if key.PrivateKey != nil {
_ = key.PrivateKey.Decrypt(pgpPrivateKeysPwd[idx])
}
}
return pgpPrivateKeysPwd[idx], nil
}
}
r = bytes.NewBuffer(pgpPacket)
md, err := openpgp.ReadMessage(r, entityList, prompt, GPGDefaultEncryptConfig)
if err != nil {
continue
}
// we get the plain key options back
optsData, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
continue
}
return optsData, nil
}
return nil, errors.New("PGP: No suitable key found to unwrap key")
}
// GetKeyIdsFromWrappedKeys converts the base64 encoded PGPPacket to uint64 keyIds
func (kw *gpgKeyWrapper) GetKeyIdsFromPacket(b64pgpPackets string) ([]uint64, error) {
var keyids []uint64
for _, b64pgpPacket := range strings.Split(b64pgpPackets, ",") {
pgpPacket, err := base64.StdEncoding.DecodeString(b64pgpPacket)
if err != nil {
return nil, errors.Wrapf(err, "could not decode base64 encoded PGP packet")
}
newids, err := kw.getKeyIDs(pgpPacket)
if err != nil {
return nil, err
}
keyids = append(keyids, newids...)
}
return keyids, nil
}
// getKeyIDs parses a PGPPacket and gets the list of recipients' key IDs
func (kw *gpgKeyWrapper) getKeyIDs(pgpPacket []byte) ([]uint64, error) {
var keyids []uint64
kbuf := bytes.NewBuffer(pgpPacket)
packets := packet.NewReader(kbuf)
ParsePackets:
for {
p, err := packets.Next()
if err == io.EOF {
break ParsePackets
}
if err != nil {
return []uint64{}, errors.Wrapf(err, "packets.Next() failed")
}
switch p := p.(type) {
case *packet.EncryptedKey:
keyids = append(keyids, p.KeyId)
case *packet.SymmetricallyEncrypted:
break ParsePackets
}
}
return keyids, nil
}
// GetRecipients converts the wrappedKeys to an array of recipients
func (kw *gpgKeyWrapper) GetRecipients(b64pgpPackets string) ([]string, error) {
keyIds, err := kw.GetKeyIdsFromPacket(b64pgpPackets)
if err != nil {
return nil, err
}
var array []string
for _, keyid := range keyIds {
array = append(array, "0x"+strconv.FormatUint(keyid, 16))
}
return array, nil
}
func (kw *gpgKeyWrapper) NoPossibleKeys(dcparameters map[string][][]byte) bool {
return len(kw.GetPrivateKeys(dcparameters)) == 0
}
func (kw *gpgKeyWrapper) GetPrivateKeys(dcparameters map[string][][]byte) [][]byte {
return dcparameters["gpg-privatekeys"]
}
func (kw *gpgKeyWrapper) getKeyParameters(dcparameters map[string][][]byte) ([][]byte, [][]byte, error) {
privKeys := kw.GetPrivateKeys(dcparameters)
if len(privKeys) == 0 {
return nil, nil, errors.New("GPG: Missing private key parameter")
}
return privKeys, dcparameters["gpg-privatekeys-passwords"], nil
}
// createEntityList creates the opengpg EntityList by reading the KeyRing
// first and then filtering out recipients' keys
func (kw *gpgKeyWrapper) createEntityList(ec *config.EncryptConfig) (openpgp.EntityList, error) {
pgpPubringFile := ec.Parameters["gpg-pubkeyringfile"]
if len(pgpPubringFile) == 0 {
return nil, nil
}
r := bytes.NewReader(pgpPubringFile[0])
entityList, err := openpgp.ReadKeyRing(r)
if err != nil {
return nil, err
}
gpgRecipients := ec.Parameters["gpg-recipients"]
if len(gpgRecipients) == 0 {
return nil, nil
}
rSet := make(map[string]int)
for _, r := range gpgRecipients {
rSet[string(r)] = 0
}
var filteredList openpgp.EntityList
for _, entity := range entityList {
for k := range entity.Identities {
addr, err := mail.ParseAddress(k)
if err != nil {
return nil, err
}
for _, r := range gpgRecipients {
recp := string(r)
if strings.Compare(addr.Name, recp) == 0 || strings.Compare(addr.Address, recp) == 0 {
filteredList = append(filteredList, entity)
rSet[recp] = rSet[recp] + 1
}
}
}
}
// make sure we found keys for all the Recipients...
var buffer bytes.Buffer
notFound := false
buffer.WriteString("PGP: No key found for the following recipients: ")
for k, v := range rSet {
if v == 0 {
if notFound {
buffer.WriteString(", ")
}
buffer.WriteString(k)
notFound = true
}
}
if notFound {
return nil, errors.New(buffer.String())
}
return filteredList, nil
}

View File

@@ -0,0 +1,136 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package pkcs7
import (
"crypto"
"crypto/x509"
"github.com/containers/ocicrypt/config"
"github.com/containers/ocicrypt/keywrap"
"github.com/containers/ocicrypt/utils"
"github.com/fullsailor/pkcs7"
"github.com/pkg/errors"
)
type pkcs7KeyWrapper struct {
}
// NewKeyWrapper returns a new key wrapping interface using jwe
func NewKeyWrapper() keywrap.KeyWrapper {
return &pkcs7KeyWrapper{}
}
func (kw *pkcs7KeyWrapper) GetAnnotationID() string {
return "org.opencontainers.image.enc.keys.pkcs7"
}
// WrapKeys wraps the session key for recpients and encrypts the optsData, which
// describe the symmetric key used for encrypting the layer
func (kw *pkcs7KeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([]byte, error) {
x509Certs, err := collectX509s(ec.Parameters["x509s"])
if err != nil {
return nil, err
}
// no recipients is not an error...
if len(x509Certs) == 0 {
return nil, nil
}
pkcs7.ContentEncryptionAlgorithm = pkcs7.EncryptionAlgorithmAES128GCM
return pkcs7.Encrypt(optsData, x509Certs)
}
func collectX509s(x509s [][]byte) ([]*x509.Certificate, error) {
if len(x509s) == 0 {
return nil, nil
}
var x509Certs []*x509.Certificate
for _, x509 := range x509s {
x509Cert, err := utils.ParseCertificate(x509, "PKCS7")
if err != nil {
return nil, err
}
x509Certs = append(x509Certs, x509Cert)
}
return x509Certs, nil
}
func (kw *pkcs7KeyWrapper) NoPossibleKeys(dcparameters map[string][][]byte) bool {
return len(kw.GetPrivateKeys(dcparameters)) == 0
}
func (kw *pkcs7KeyWrapper) GetPrivateKeys(dcparameters map[string][][]byte) [][]byte {
return dcparameters["privkeys"]
}
func (kw *pkcs7KeyWrapper) getPrivateKeysPasswords(dcparameters map[string][][]byte) [][]byte {
return dcparameters["privkeys-passwords"]
}
// UnwrapKey unwraps the symmetric key with which the layer is encrypted
// This symmetric key is encrypted in the PKCS7 payload.
func (kw *pkcs7KeyWrapper) UnwrapKey(dc *config.DecryptConfig, pkcs7Packet []byte) ([]byte, error) {
privKeys := kw.GetPrivateKeys(dc.Parameters)
if len(privKeys) == 0 {
return nil, errors.New("no private keys found for PKCS7 decryption")
}
privKeysPasswords := kw.getPrivateKeysPasswords(dc.Parameters)
if len(privKeysPasswords) != len(privKeys) {
return nil, errors.New("private key password array length must be same as that of private keys")
}
x509Certs, err := collectX509s(dc.Parameters["x509s"])
if err != nil {
return nil, err
}
if len(x509Certs) == 0 {
return nil, errors.New("no x509 certificates found needed for PKCS7 decryption")
}
p7, err := pkcs7.Parse(pkcs7Packet)
if err != nil {
return nil, errors.Wrapf(err, "could not parse PKCS7 packet")
}
for idx, privKey := range privKeys {
key, err := utils.ParsePrivateKey(privKey, privKeysPasswords[idx], "PKCS7")
if err != nil {
return nil, err
}
for _, x509Cert := range x509Certs {
optsData, err := p7.Decrypt(x509Cert, crypto.PrivateKey(key))
if err != nil {
continue
}
return optsData, nil
}
}
return nil, errors.New("PKCS7: No suitable private key found for decryption")
}
// GetKeyIdsFromWrappedKeys converts the base64 encoded Packet to uint64 keyIds;
// We cannot do this with pkcs7
func (kw *pkcs7KeyWrapper) GetKeyIdsFromPacket(b64pkcs7Packets string) ([]uint64, error) {
return nil, nil
}
// GetRecipients converts the wrappedKeys to an array of recipients
// We cannot do this with pkcs7
func (kw *pkcs7KeyWrapper) GetRecipients(b64pkcs7Packets string) ([]string, error) {
return []string{"[pkcs7]"}, nil
}

40
vendor/github.com/containers/ocicrypt/reader.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package ocicrypt
import (
"io"
)
type readerAtReader struct {
r io.ReaderAt
off int64
}
// ReaderFromReaderAt takes an io.ReaderAt and returns an io.Reader
func ReaderFromReaderAt(r io.ReaderAt) io.Reader {
return &readerAtReader{
r: r,
off: 0,
}
}
func (rar *readerAtReader) Read(p []byte) (n int, err error) {
n, err = rar.r.ReadAt(p, rar.off)
rar.off += int64(n)
return n, err
}

12
vendor/github.com/containers/ocicrypt/spec/spec.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
package spec
const (
// MediaTypeLayerEnc is MIME type used for encrypted layers.
MediaTypeLayerEnc = "application/vnd.oci.image.layer.v1.tar+encrypted"
// MediaTypeLayerGzipEnc is MIME type used for encrypted compressed layers.
MediaTypeLayerGzipEnc = "application/vnd.oci.image.layer.v1.tar+gzip+encrypted"
// MediaTypeLayerNonDistributableEnc is MIME type used for non distributable encrypted layers.
MediaTypeLayerNonDistributableEnc = "application/vnd.oci.image.layer.nondistributable.v1.tar+encrypted"
// MediaTypeLayerGzipEnc is MIME type used for non distributable encrypted compressed layers.
MediaTypeLayerNonDistributableGzipEnc = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip+encrypted"
)

View File

@@ -0,0 +1,109 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package utils
import (
"io"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
// DelayedReader wraps a io.Reader and allows a client to use the Reader
// interface. The DelayedReader holds back some buffer to the client
// so that it can report any error that occurred on the Reader it wraps
// early to the client while it may still have held some data back.
type DelayedReader struct {
reader io.Reader // Reader to Read() bytes from and delay them
err error // error that occurred on the reader
buffer []byte // delay buffer
bufbytes int // number of bytes in the delay buffer to give to Read(); on '0' we return 'EOF' to caller
bufoff int // offset in the delay buffer to give to Read()
}
// NewDelayedReader wraps a io.Reader and allocates a delay buffer of bufsize bytes
func NewDelayedReader(reader io.Reader, bufsize uint) io.Reader {
return &DelayedReader{
reader: reader,
buffer: make([]byte, bufsize),
}
}
// Read implements the io.Reader interface
func (dr *DelayedReader) Read(p []byte) (int, error) {
if dr.err != nil && dr.err != io.EOF {
return 0, dr.err
}
// if we are completely drained, return io.EOF
if dr.err == io.EOF && dr.bufbytes == 0 {
return 0, io.EOF
}
// only at the beginning we fill our delay buffer in an extra step
if dr.bufbytes < len(dr.buffer) && dr.err == nil {
dr.bufbytes, dr.err = FillBuffer(dr.reader, dr.buffer)
if dr.err != nil && dr.err != io.EOF {
return 0, dr.err
}
}
// dr.err != nil means we have EOF and can drain the delay buffer
// otherwise we need to still read from the reader
var tmpbuf []byte
tmpbufbytes := 0
if dr.err == nil {
tmpbuf = make([]byte, len(p))
tmpbufbytes, dr.err = FillBuffer(dr.reader, tmpbuf)
if dr.err != nil && dr.err != io.EOF {
return 0, dr.err
}
}
// copy out of the delay buffer into 'p'
tocopy1 := min(len(p), dr.bufbytes)
c1 := copy(p[:tocopy1], dr.buffer[dr.bufoff:])
dr.bufoff += c1
dr.bufbytes -= c1
c2 := 0
// can p still hold more data?
if c1 < len(p) {
// copy out of the tmpbuf into 'p'
c2 = copy(p[tocopy1:], tmpbuf[:tmpbufbytes])
}
// if tmpbuf holds data we need to hold onto, copy them
// into the delay buffer
if tmpbufbytes-c2 > 0 {
// left-shift the delay buffer and append the tmpbuf's remaining data
dr.buffer = dr.buffer[dr.bufoff : dr.bufoff+dr.bufbytes]
dr.buffer = append(dr.buffer, tmpbuf[c2:tmpbufbytes]...)
dr.bufoff = 0
dr.bufbytes = len(dr.buffer)
}
var err error
if dr.bufbytes == 0 {
err = io.EOF
}
return c1 + c2, err
}

31
vendor/github.com/containers/ocicrypt/utils/ioutils.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package utils
import (
"io"
)
// FillBuffer fills the given buffer with as many bytes from the reader as possible. It returns
// EOF if an EOF was encountered or any other error.
func FillBuffer(reader io.Reader, buffer []byte) (int, error) {
n, err := io.ReadFull(reader, buffer)
if err == io.ErrUnexpectedEOF {
return n, io.EOF
}
return n, err
}

166
vendor/github.com/containers/ocicrypt/utils/testing.go generated vendored Normal file
View File

@@ -0,0 +1,166 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package utils
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"time"
"github.com/pkg/errors"
)
// CreateRSAKey creates an RSA key
func CreateRSAKey(bits int) (*rsa.PrivateKey, error) {
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, errors.Wrap(err, "rsa.GenerateKey failed")
}
return key, nil
}
// CreateRSATestKey creates an RSA key of the given size and returns
// the public and private key in PEM or DER format
func CreateRSATestKey(bits int, password []byte, pemencode bool) ([]byte, []byte, error) {
key, err := CreateRSAKey(bits)
if err != nil {
return nil, nil, err
}
pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
if err != nil {
return nil, nil, errors.Wrap(err, "x509.MarshalPKIXPublicKey failed")
}
privData := x509.MarshalPKCS1PrivateKey(key)
// no more encoding needed for DER
if !pemencode {
return pubData, privData, nil
}
publicKey := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: pubData,
})
var block *pem.Block
typ := "RSA PRIVATE KEY"
if len(password) > 0 {
block, err = x509.EncryptPEMBlock(rand.Reader, typ, privData, password, x509.PEMCipherAES256)
if err != nil {
return nil, nil, errors.Wrap(err, "x509.EncryptPEMBlock failed")
}
} else {
block = &pem.Block{
Type: typ,
Bytes: privData,
}
}
privateKey := pem.EncodeToMemory(block)
return publicKey, privateKey, nil
}
// CreateECDSATestKey creates and elliptic curve key for the given curve and returns
// the public and private key in DER format
func CreateECDSATestKey(curve elliptic.Curve) ([]byte, []byte, error) {
key, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, errors.Wrapf(err, "ecdsa.GenerateKey failed")
}
pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
if err != nil {
return nil, nil, errors.Wrapf(err, "x509.MarshalPKIXPublicKey failed")
}
privData, err := x509.MarshalECPrivateKey(key)
if err != nil {
return nil, nil, errors.Wrapf(err, "x509.MarshalECPrivateKey failed")
}
return pubData, privData, nil
}
// CreateTestCA creates a root CA for testing
func CreateTestCA() (*rsa.PrivateKey, *x509.Certificate, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, errors.Wrap(err, "rsa.GenerateKey failed")
}
ca := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "test-ca",
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0),
IsCA: true,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
caCert, err := certifyKey(&key.PublicKey, ca, key, ca)
return key, caCert, err
}
// CertifyKey certifies a public key using the given CA's private key and cert;
// The certificate template for the public key is optional
func CertifyKey(pubbytes []byte, template *x509.Certificate, caKey *rsa.PrivateKey, caCert *x509.Certificate) (*x509.Certificate, error) {
pubKey, err := ParsePublicKey(pubbytes, "CertifyKey")
if err != nil {
return nil, err
}
return certifyKey(pubKey, template, caKey, caCert)
}
func certifyKey(pub interface{}, template *x509.Certificate, caKey *rsa.PrivateKey, caCert *x509.Certificate) (*x509.Certificate, error) {
if template == nil {
template = &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "testkey",
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour),
IsCA: false,
KeyUsage: x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
}
certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, pub, caKey)
if err != nil {
return nil, errors.Wrap(err, "x509.CreateCertificate failed")
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
return nil, errors.Wrap(err, "x509.ParseCertificate failed")
}
return cert, nil
}

220
vendor/github.com/containers/ocicrypt/utils/utils.go generated vendored Normal file
View File

@@ -0,0 +1,220 @@
/*
Copyright The ocicrypt Authors.
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.
*/
package utils
import (
"bytes"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"strings"
"github.com/pkg/errors"
"golang.org/x/crypto/openpgp"
json "gopkg.in/square/go-jose.v2"
)
// parseJWKPrivateKey parses the input byte array as a JWK and makes sure it's a private key
func parseJWKPrivateKey(privKey []byte, prefix string) (interface{}, error) {
jwk := json.JSONWebKey{}
err := jwk.UnmarshalJSON(privKey)
if err != nil {
return nil, errors.Wrapf(err, "%s: Could not parse input as JWK", prefix)
}
if jwk.IsPublic() {
return nil, fmt.Errorf("%s: JWK is not a private key", prefix)
}
return &jwk, nil
}
// parseJWKPublicKey parses the input byte array as a JWK
func parseJWKPublicKey(privKey []byte, prefix string) (interface{}, error) {
jwk := json.JSONWebKey{}
err := jwk.UnmarshalJSON(privKey)
if err != nil {
return nil, errors.Wrapf(err, "%s: Could not parse input as JWK", prefix)
}
if !jwk.IsPublic() {
return nil, fmt.Errorf("%s: JWK is not a public key", prefix)
}
return &jwk, nil
}
// IsPasswordError checks whether an error is related to a missing or wrong
// password
func IsPasswordError(err error) bool {
if err == nil {
return false
}
msg := strings.ToLower(err.Error())
return strings.Contains(msg, "password") &&
(strings.Contains(msg, "missing") || strings.Contains(msg, "wrong"))
}
// ParsePrivateKey tries to parse a private key in DER format first and
// PEM format after, returning an error if the parsing failed
func ParsePrivateKey(privKey, privKeyPassword []byte, prefix string) (interface{}, error) {
key, err := x509.ParsePKCS8PrivateKey(privKey)
if err != nil {
key, err = x509.ParsePKCS1PrivateKey(privKey)
if err != nil {
key, err = x509.ParseECPrivateKey(privKey)
}
}
if err != nil {
block, _ := pem.Decode(privKey)
if block != nil {
var der []byte
if x509.IsEncryptedPEMBlock(block) {
if privKeyPassword == nil {
return nil, errors.Errorf("%s: Missing password for encrypted private key", prefix)
}
der, err = x509.DecryptPEMBlock(block, privKeyPassword)
if err != nil {
return nil, errors.Errorf("%s: Wrong password: could not decrypt private key", prefix)
}
} else {
der = block.Bytes
}
key, err = x509.ParsePKCS8PrivateKey(der)
if err != nil {
key, err = x509.ParsePKCS1PrivateKey(der)
if err != nil {
return nil, errors.Wrapf(err, "%s: Could not parse private key", prefix)
}
}
} else {
key, err = parseJWKPrivateKey(privKey, prefix)
}
}
return key, err
}
// IsPrivateKey returns true in case the given byte array represents a private key
// It returns an error if for example the password is wrong
func IsPrivateKey(data []byte, password []byte) (bool, error) {
_, err := ParsePrivateKey(data, password, "")
return err == nil, err
}
// ParsePublicKey tries to parse a public key in DER format first and
// PEM format after, returning an error if the parsing failed
func ParsePublicKey(pubKey []byte, prefix string) (interface{}, error) {
key, err := x509.ParsePKIXPublicKey(pubKey)
if err != nil {
block, _ := pem.Decode(pubKey)
if block != nil {
key, err = x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, errors.Wrapf(err, "%s: Could not parse public key", prefix)
}
} else {
key, err = parseJWKPublicKey(pubKey, prefix)
}
}
return key, err
}
// IsPublicKey returns true in case the given byte array represents a public key
func IsPublicKey(data []byte) bool {
_, err := ParsePublicKey(data, "")
return err == nil
}
// ParseCertificate tries to parse a public key in DER format first and
// PEM format after, returning an error if the parsing failed
func ParseCertificate(certBytes []byte, prefix string) (*x509.Certificate, error) {
x509Cert, err := x509.ParseCertificate(certBytes)
if err != nil {
block, _ := pem.Decode(certBytes)
if block == nil {
return nil, fmt.Errorf("%s: Could not PEM decode x509 certificate", prefix)
}
x509Cert, err = x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, errors.Wrapf(err, "%s: Could not parse x509 certificate", prefix)
}
}
return x509Cert, err
}
// IsCertificate returns true in case the given byte array represents an x.509 certificate
func IsCertificate(data []byte) bool {
_, err := ParseCertificate(data, "")
return err == nil
}
// IsGPGPrivateKeyRing returns true in case the given byte array represents a GPG private key ring file
func IsGPGPrivateKeyRing(data []byte) bool {
r := bytes.NewBuffer(data)
_, err := openpgp.ReadKeyRing(r)
return err == nil
}
// SortDecryptionKeys parses a list of comma separated base64 entries and sorts the data into
// a map. Each entry in the list may be either a GPG private key ring, private key, or x.509
// certificate
func SortDecryptionKeys(b64ItemList string) (map[string][][]byte, error) {
dcparameters := make(map[string][][]byte)
for _, b64Item := range strings.Split(b64ItemList, ",") {
var password []byte
b64Data := strings.Split(b64Item, ":")
keyData, err := base64.StdEncoding.DecodeString(b64Data[0])
if err != nil {
return nil, errors.New("Could not base64 decode a passed decryption key")
}
if len(b64Data) == 2 {
password, err = base64.StdEncoding.DecodeString(b64Data[1])
if err != nil {
return nil, errors.New("Could not base64 decode a passed decryption key password")
}
}
var key string
isPrivKey, err := IsPrivateKey(keyData, password)
if IsPasswordError(err) {
return nil, err
}
if isPrivKey {
key = "privkeys"
if _, ok := dcparameters["privkeys-passwords"]; !ok {
dcparameters["privkeys-passwords"] = [][]byte{password}
} else {
dcparameters["privkeys-passwords"] = append(dcparameters["privkeys-passwords"], password)
}
} else if IsCertificate(keyData) {
key = "x509s"
} else if IsGPGPrivateKeyRing(keyData) {
key = "gpg-privatekeys"
}
if key != "" {
values := dcparameters[key]
if values == nil {
dcparameters[key] = [][]byte{keyData}
} else {
dcparameters[key] = append(dcparameters[key], keyData)
}
} else {
return nil, errors.New("Unknown decryption key type")
}
}
return dcparameters, nil
}

22
vendor/github.com/fullsailor/pkcs7/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Smith
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.

8
vendor/github.com/fullsailor/pkcs7/README.md generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# pkcs7
[![GoDoc](https://godoc.org/github.com/fullsailor/pkcs7?status.svg)](https://godoc.org/github.com/fullsailor/pkcs7)
[![Build Status](https://travis-ci.org/fullsailor/pkcs7.svg?branch=master)](https://travis-ci.org/fullsailor/pkcs7)
pkcs7 implements parsing and creating signed and enveloped messages.
- Documentation on [GoDoc](http://godoc.org/github.com/fullsailor/pkcs7)

248
vendor/github.com/fullsailor/pkcs7/ber.go generated vendored Normal file
View File

@@ -0,0 +1,248 @@
package pkcs7
import (
"bytes"
"errors"
)
var encodeIndent = 0
type asn1Object interface {
EncodeTo(writer *bytes.Buffer) error
}
type asn1Structured struct {
tagBytes []byte
content []asn1Object
}
func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
encodeIndent++
inner := new(bytes.Buffer)
for _, obj := range s.content {
err := obj.EncodeTo(inner)
if err != nil {
return err
}
}
encodeIndent--
out.Write(s.tagBytes)
encodeLength(out, inner.Len())
out.Write(inner.Bytes())
return nil
}
type asn1Primitive struct {
tagBytes []byte
length int
content []byte
}
func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
_, err := out.Write(p.tagBytes)
if err != nil {
return err
}
if err = encodeLength(out, p.length); err != nil {
return err
}
//fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length)
//fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content))
out.Write(p.content)
return nil
}
func ber2der(ber []byte) ([]byte, error) {
if len(ber) == 0 {
return nil, errors.New("ber2der: input ber is empty")
}
//fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber))
out := new(bytes.Buffer)
obj, _, err := readObject(ber, 0)
if err != nil {
return nil, err
}
obj.EncodeTo(out)
// if offset < len(ber) {
// return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))
//}
return out.Bytes(), nil
}
// encodes lengths that are longer than 127 into string of bytes
func marshalLongLength(out *bytes.Buffer, i int) (err error) {
n := lengthLength(i)
for ; n > 0; n-- {
err = out.WriteByte(byte(i >> uint((n-1)*8)))
if err != nil {
return
}
}
return nil
}
// computes the byte length of an encoded length value
func lengthLength(i int) (numBytes int) {
numBytes = 1
for i > 255 {
numBytes++
i >>= 8
}
return
}
// encodes the length in DER format
// If the length fits in 7 bits, the value is encoded directly.
//
// Otherwise, the number of bytes to encode the length is first determined.
// This number is likely to be 4 or less for a 32bit length. This number is
// added to 0x80. The length is encoded in big endian encoding follow after
//
// Examples:
// length | byte 1 | bytes n
// 0 | 0x00 | -
// 120 | 0x78 | -
// 200 | 0x81 | 0xC8
// 500 | 0x82 | 0x01 0xF4
//
func encodeLength(out *bytes.Buffer, length int) (err error) {
if length >= 128 {
l := lengthLength(length)
err = out.WriteByte(0x80 | byte(l))
if err != nil {
return
}
err = marshalLongLength(out, length)
if err != nil {
return
}
} else {
err = out.WriteByte(byte(length))
if err != nil {
return
}
}
return
}
func readObject(ber []byte, offset int) (asn1Object, int, error) {
//fmt.Printf("\n====> Starting readObject at offset: %d\n\n", offset)
tagStart := offset
b := ber[offset]
offset++
tag := b & 0x1F // last 5 bits
if tag == 0x1F {
tag = 0
for ber[offset] >= 0x80 {
tag = tag*128 + ber[offset] - 0x80
offset++
}
tag = tag*128 + ber[offset] - 0x80
offset++
}
tagEnd := offset
kind := b & 0x20
/*
if kind == 0 {
fmt.Print("--> Primitive\n")
} else {
fmt.Print("--> Constructed\n")
}
*/
// read length
var length int
l := ber[offset]
offset++
indefinite := false
if l > 0x80 {
numberOfBytes := (int)(l & 0x7F)
if numberOfBytes > 4 { // int is only guaranteed to be 32bit
return nil, 0, errors.New("ber2der: BER tag length too long")
}
if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F {
return nil, 0, errors.New("ber2der: BER tag length is negative")
}
if 0x0 == (int)(ber[offset]) {
return nil, 0, errors.New("ber2der: BER tag length has leading zero")
}
//fmt.Printf("--> (compute length) indicator byte: %x\n", l)
//fmt.Printf("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes])
for i := 0; i < numberOfBytes; i++ {
length = length*256 + (int)(ber[offset])
offset++
}
} else if l == 0x80 {
indefinite = true
} else {
length = (int)(l)
}
//fmt.Printf("--> length : %d\n", length)
contentEnd := offset + length
if contentEnd > len(ber) {
return nil, 0, errors.New("ber2der: BER tag length is more than available data")
}
//fmt.Printf("--> content start : %d\n", offset)
//fmt.Printf("--> content end : %d\n", contentEnd)
//fmt.Printf("--> content : % X\n", ber[offset:contentEnd])
var obj asn1Object
if indefinite && kind == 0 {
return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding")
}
if kind == 0 {
obj = asn1Primitive{
tagBytes: ber[tagStart:tagEnd],
length: length,
content: ber[offset:contentEnd],
}
} else {
var subObjects []asn1Object
for (offset < contentEnd) || indefinite {
var subObj asn1Object
var err error
subObj, offset, err = readObject(ber, offset)
if err != nil {
return nil, 0, err
}
subObjects = append(subObjects, subObj)
if indefinite {
terminated, err := isIndefiniteTermination(ber, offset)
if err != nil {
return nil, 0, err
}
if terminated {
break
}
}
}
obj = asn1Structured{
tagBytes: ber[tagStart:tagEnd],
content: subObjects,
}
}
// Apply indefinite form length with 0x0000 terminator.
if indefinite {
contentEnd = offset + 2
}
return obj, contentEnd, nil
}
func isIndefiniteTermination(ber []byte, offset int) (bool, error) {
if len(ber) - offset < 2 {
return false, errors.New("ber2der: Invalid BER format")
}
return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil
}

962
vendor/github.com/fullsailor/pkcs7/pkcs7.go generated vendored Normal file
View File

@@ -0,0 +1,962 @@
// Package pkcs7 implements parsing and generation of some PKCS#7 structures.
package pkcs7
import (
"bytes"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"math/big"
"sort"
"time"
_ "crypto/sha1" // for crypto.SHA1
)
// PKCS7 Represents a PKCS7 structure
type PKCS7 struct {
Content []byte
Certificates []*x509.Certificate
CRLs []pkix.CertificateList
Signers []signerInfo
raw interface{}
}
type contentInfo struct {
ContentType asn1.ObjectIdentifier
Content asn1.RawValue `asn1:"explicit,optional,tag:0"`
}
// ErrUnsupportedContentType is returned when a PKCS7 content is not supported.
// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2),
// and Enveloped Data are supported (1.2.840.113549.1.7.3)
var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type")
type unsignedData []byte
var (
oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
oidEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3}
oidSignedAndEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4}
oidDigestedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5}
oidEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6}
oidAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
oidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
oidAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
)
type signedData struct {
Version int `asn1:"default:1"`
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
ContentInfo contentInfo
Certificates rawCertificates `asn1:"optional,tag:0"`
CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
SignerInfos []signerInfo `asn1:"set"`
}
type rawCertificates struct {
Raw asn1.RawContent
}
type envelopedData struct {
Version int
RecipientInfos []recipientInfo `asn1:"set"`
EncryptedContentInfo encryptedContentInfo
}
type recipientInfo struct {
Version int
IssuerAndSerialNumber issuerAndSerial
KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedKey []byte
}
type encryptedContentInfo struct {
ContentType asn1.ObjectIdentifier
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedContent asn1.RawValue `asn1:"tag:0,optional"`
}
type attribute struct {
Type asn1.ObjectIdentifier
Value asn1.RawValue `asn1:"set"`
}
type issuerAndSerial struct {
IssuerName asn1.RawValue
SerialNumber *big.Int
}
// MessageDigestMismatchError is returned when the signer data digest does not
// match the computed digest for the contained content
type MessageDigestMismatchError struct {
ExpectedDigest []byte
ActualDigest []byte
}
func (err *MessageDigestMismatchError) Error() string {
return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest)
}
type signerInfo struct {
Version int `asn1:"default:1"`
IssuerAndSerialNumber issuerAndSerial
DigestAlgorithm pkix.AlgorithmIdentifier
AuthenticatedAttributes []attribute `asn1:"optional,tag:0"`
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedDigest []byte
UnauthenticatedAttributes []attribute `asn1:"optional,tag:1"`
}
// Parse decodes a DER encoded PKCS7 package
func Parse(data []byte) (p7 *PKCS7, err error) {
if len(data) == 0 {
return nil, errors.New("pkcs7: input data is empty")
}
var info contentInfo
der, err := ber2der(data)
if err != nil {
return nil, err
}
rest, err := asn1.Unmarshal(der, &info)
if len(rest) > 0 {
err = asn1.SyntaxError{Msg: "trailing data"}
return
}
if err != nil {
return
}
// fmt.Printf("--> Content Type: %s", info.ContentType)
switch {
case info.ContentType.Equal(oidSignedData):
return parseSignedData(info.Content.Bytes)
case info.ContentType.Equal(oidEnvelopedData):
return parseEnvelopedData(info.Content.Bytes)
}
return nil, ErrUnsupportedContentType
}
func parseSignedData(data []byte) (*PKCS7, error) {
var sd signedData
asn1.Unmarshal(data, &sd)
certs, err := sd.Certificates.Parse()
if err != nil {
return nil, err
}
// fmt.Printf("--> Signed Data Version %d\n", sd.Version)
var compound asn1.RawValue
var content unsignedData
// The Content.Bytes maybe empty on PKI responses.
if len(sd.ContentInfo.Content.Bytes) > 0 {
if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil {
return nil, err
}
}
// Compound octet string
if compound.IsCompound {
if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
return nil, err
}
} else {
// assuming this is tag 04
content = compound.Bytes
}
return &PKCS7{
Content: content,
Certificates: certs,
CRLs: sd.CRLs,
Signers: sd.SignerInfos,
raw: sd}, nil
}
func (raw rawCertificates) Parse() ([]*x509.Certificate, error) {
if len(raw.Raw) == 0 {
return nil, nil
}
var val asn1.RawValue
if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil {
return nil, err
}
return x509.ParseCertificates(val.Bytes)
}
func parseEnvelopedData(data []byte) (*PKCS7, error) {
var ed envelopedData
if _, err := asn1.Unmarshal(data, &ed); err != nil {
return nil, err
}
return &PKCS7{
raw: ed,
}, nil
}
// Verify checks the signatures of a PKCS7 object
// WARNING: Verify does not check signing time or verify certificate chains at
// this time.
func (p7 *PKCS7) Verify() (err error) {
if len(p7.Signers) == 0 {
return errors.New("pkcs7: Message has no signers")
}
for _, signer := range p7.Signers {
if err := verifySignature(p7, signer); err != nil {
return err
}
}
return nil
}
func verifySignature(p7 *PKCS7, signer signerInfo) error {
signedData := p7.Content
hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
if err != nil {
return err
}
if len(signer.AuthenticatedAttributes) > 0 {
// TODO(fullsailor): First check the content type match
var digest []byte
err := unmarshalAttribute(signer.AuthenticatedAttributes, oidAttributeMessageDigest, &digest)
if err != nil {
return err
}
h := hash.New()
h.Write(p7.Content)
computed := h.Sum(nil)
if !hmac.Equal(digest, computed) {
return &MessageDigestMismatchError{
ExpectedDigest: digest,
ActualDigest: computed,
}
}
// TODO(fullsailor): Optionally verify certificate chain
// TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore
signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
if err != nil {
return err
}
}
cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
if cert == nil {
return errors.New("pkcs7: No certificate for signer")
}
algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm)
if algo == x509.UnknownSignatureAlgorithm {
// I'm not sure what the spec here is, and the openssl sources were not
// helpful. But, this is what App Store receipts appear to do.
// The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)"
// But we're expecting a digest + encryption algorithm. So... we're going
// to determine an algorithm based on the DigestAlgorithm and this
// encryption algorithm.
if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) {
algo = getRSASignatureAlgorithmForDigestAlgorithm(hash)
}
}
return cert.CheckSignature(algo, signedData, signer.EncryptedDigest)
}
func marshalAttributes(attrs []attribute) ([]byte, error) {
encodedAttributes, err := asn1.Marshal(struct {
A []attribute `asn1:"set"`
}{A: attrs})
if err != nil {
return nil, err
}
// Remove the leading sequence octets
var raw asn1.RawValue
asn1.Unmarshal(encodedAttributes, &raw)
return raw.Bytes, nil
}
var (
oidDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
oidEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
)
func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate {
for _, cert := range certs {
if isCertMatchForIssuerAndSerial(cert, ias) {
return cert
}
}
return nil
}
func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) {
switch {
case oid.Equal(oidDigestAlgorithmSHA1):
return crypto.SHA1, nil
case oid.Equal(oidSHA256):
return crypto.SHA256, nil
}
return crypto.Hash(0), ErrUnsupportedAlgorithm
}
func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm {
for _, details := range signatureAlgorithmDetails {
if details.pubKeyAlgo == x509.RSA && details.hash == hash {
return details.algo
}
}
return x509.UnknownSignatureAlgorithm
}
// GetOnlySigner returns an x509.Certificate for the first signer of the signed
// data payload. If there are more or less than one signer, nil is returned
func (p7 *PKCS7) GetOnlySigner() *x509.Certificate {
if len(p7.Signers) != 1 {
return nil
}
signer := p7.Signers[0]
return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
}
// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed
var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported")
// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data
var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type")
// Decrypt decrypts encrypted content info for recipient cert and private key
func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, error) {
data, ok := p7.raw.(envelopedData)
if !ok {
return nil, ErrNotEncryptedContent
}
recipient := selectRecipientForCertificate(data.RecipientInfos, cert)
if recipient.EncryptedKey == nil {
return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
}
if priv := pk.(*rsa.PrivateKey); priv != nil {
var contentKey []byte
contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, recipient.EncryptedKey)
if err != nil {
return nil, err
}
return data.EncryptedContentInfo.decrypt(contentKey)
}
fmt.Printf("Unsupported Private Key: %v\n", pk)
return nil, ErrUnsupportedAlgorithm
}
var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}
var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6}
var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) {
alg := eci.ContentEncryptionAlgorithm.Algorithm
if !alg.Equal(oidEncryptionAlgorithmDESCBC) &&
!alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) &&
!alg.Equal(oidEncryptionAlgorithmAES256CBC) &&
!alg.Equal(oidEncryptionAlgorithmAES128CBC) &&
!alg.Equal(oidEncryptionAlgorithmAES128GCM) {
fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg)
return nil, ErrUnsupportedAlgorithm
}
// EncryptedContent can either be constructed of multple OCTET STRINGs
// or _be_ a tagged OCTET STRING
var cyphertext []byte
if eci.EncryptedContent.IsCompound {
// Complex case to concat all of the children OCTET STRINGs
var buf bytes.Buffer
cypherbytes := eci.EncryptedContent.Bytes
for {
var part []byte
cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part)
buf.Write(part)
if cypherbytes == nil {
break
}
}
cyphertext = buf.Bytes()
} else {
// Simple case, the bytes _are_ the cyphertext
cyphertext = eci.EncryptedContent.Bytes
}
var block cipher.Block
var err error
switch {
case alg.Equal(oidEncryptionAlgorithmDESCBC):
block, err = des.NewCipher(key)
case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC):
block, err = des.NewTripleDESCipher(key)
case alg.Equal(oidEncryptionAlgorithmAES256CBC):
fallthrough
case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC):
block, err = aes.NewCipher(key)
}
if err != nil {
return nil, err
}
if alg.Equal(oidEncryptionAlgorithmAES128GCM) {
params := aesGCMParameters{}
paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes
_, err := asn1.Unmarshal(paramBytes, &params)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(params.Nonce) != gcm.NonceSize() {
return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
}
if params.ICVLen != gcm.Overhead() {
return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
}
plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes
if len(iv) != block.BlockSize() {
return nil, errors.New("pkcs7: encryption algorithm parameters are malformed")
}
mode := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(cyphertext))
mode.CryptBlocks(plaintext, cyphertext)
if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil {
return nil, err
}
return plaintext, nil
}
func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo {
for _, recp := range recipients {
if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
return recp
}
}
return recipientInfo{}
}
func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool {
return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Compare(cert.RawIssuer, ias.IssuerName.FullBytes) == 0
}
func pad(data []byte, blocklen int) ([]byte, error) {
if blocklen < 1 {
return nil, fmt.Errorf("invalid blocklen %d", blocklen)
}
padlen := blocklen - (len(data) % blocklen)
if padlen == 0 {
padlen = blocklen
}
pad := bytes.Repeat([]byte{byte(padlen)}, padlen)
return append(data, pad...), nil
}
func unpad(data []byte, blocklen int) ([]byte, error) {
if blocklen < 1 {
return nil, fmt.Errorf("invalid blocklen %d", blocklen)
}
if len(data)%blocklen != 0 || len(data) == 0 {
return nil, fmt.Errorf("invalid data len %d", len(data))
}
// the last byte is the length of padding
padlen := int(data[len(data)-1])
// check padding integrity, all bytes should be the same
pad := data[len(data)-padlen:]
for _, padbyte := range pad {
if padbyte != byte(padlen) {
return nil, errors.New("invalid padding")
}
}
return data[:len(data)-padlen], nil
}
func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error {
for _, attr := range attrs {
if attr.Type.Equal(attributeType) {
_, err := asn1.Unmarshal(attr.Value.Bytes, out)
return err
}
}
return errors.New("pkcs7: attribute type not in attributes")
}
// UnmarshalSignedAttribute decodes a single attribute from the signer info
func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
sd, ok := p7.raw.(signedData)
if !ok {
return errors.New("pkcs7: payload is not signedData content")
}
if len(sd.SignerInfos) < 1 {
return errors.New("pkcs7: payload has no signers")
}
attributes := sd.SignerInfos[0].AuthenticatedAttributes
return unmarshalAttribute(attributes, attributeType, out)
}
// SignedData is an opaque data structure for creating signed data payloads
type SignedData struct {
sd signedData
certs []*x509.Certificate
messageDigest []byte
}
// Attribute represents a key value pair attribute. Value must be marshalable byte
// `encoding/asn1`
type Attribute struct {
Type asn1.ObjectIdentifier
Value interface{}
}
// SignerInfoConfig are optional values to include when adding a signer
type SignerInfoConfig struct {
ExtraSignedAttributes []Attribute
}
// NewSignedData initializes a SignedData with content
func NewSignedData(data []byte) (*SignedData, error) {
content, err := asn1.Marshal(data)
if err != nil {
return nil, err
}
ci := contentInfo{
ContentType: oidData,
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
}
digAlg := pkix.AlgorithmIdentifier{
Algorithm: oidDigestAlgorithmSHA1,
}
h := crypto.SHA1.New()
h.Write(data)
md := h.Sum(nil)
sd := signedData{
ContentInfo: ci,
Version: 1,
DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digAlg},
}
return &SignedData{sd: sd, messageDigest: md}, nil
}
type attributes struct {
types []asn1.ObjectIdentifier
values []interface{}
}
// Add adds the attribute, maintaining insertion order
func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) {
attrs.types = append(attrs.types, attrType)
attrs.values = append(attrs.values, value)
}
type sortableAttribute struct {
SortKey []byte
Attribute attribute
}
type attributeSet []sortableAttribute
func (sa attributeSet) Len() int {
return len(sa)
}
func (sa attributeSet) Less(i, j int) bool {
return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0
}
func (sa attributeSet) Swap(i, j int) {
sa[i], sa[j] = sa[j], sa[i]
}
func (sa attributeSet) Attributes() []attribute {
attrs := make([]attribute, len(sa))
for i, attr := range sa {
attrs[i] = attr.Attribute
}
return attrs
}
func (attrs *attributes) ForMarshaling() ([]attribute, error) {
sortables := make(attributeSet, len(attrs.types))
for i := range sortables {
attrType := attrs.types[i]
attrValue := attrs.values[i]
asn1Value, err := asn1.Marshal(attrValue)
if err != nil {
return nil, err
}
attr := attribute{
Type: attrType,
Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag
}
encoded, err := asn1.Marshal(attr)
if err != nil {
return nil, err
}
sortables[i] = sortableAttribute{
SortKey: encoded,
Attribute: attr,
}
}
sort.Sort(sortables)
return sortables.Attributes(), nil
}
// AddSigner signs attributes about the content and adds certificate to payload
func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
attrs := &attributes{}
attrs.Add(oidAttributeContentType, sd.sd.ContentInfo.ContentType)
attrs.Add(oidAttributeMessageDigest, sd.messageDigest)
attrs.Add(oidAttributeSigningTime, time.Now())
for _, attr := range config.ExtraSignedAttributes {
attrs.Add(attr.Type, attr.Value)
}
finalAttrs, err := attrs.ForMarshaling()
if err != nil {
return err
}
signature, err := signAttributes(finalAttrs, pkey, crypto.SHA1)
if err != nil {
return err
}
ias, err := cert2issuerAndSerial(cert)
if err != nil {
return err
}
signer := signerInfo{
AuthenticatedAttributes: finalAttrs,
DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1},
DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA},
IssuerAndSerialNumber: ias,
EncryptedDigest: signature,
Version: 1,
}
// create signature of signed attributes
sd.certs = append(sd.certs, cert)
sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
return nil
}
// AddCertificate adds the certificate to the payload. Useful for parent certificates
func (sd *SignedData) AddCertificate(cert *x509.Certificate) {
sd.certs = append(sd.certs, cert)
}
// Detach removes content from the signed data struct to make it a detached signature.
// This must be called right before Finish()
func (sd *SignedData) Detach() {
sd.sd.ContentInfo = contentInfo{ContentType: oidData}
}
// Finish marshals the content and its signers
func (sd *SignedData) Finish() ([]byte, error) {
sd.sd.Certificates = marshalCertificates(sd.certs)
inner, err := asn1.Marshal(sd.sd)
if err != nil {
return nil, err
}
outer := contentInfo{
ContentType: oidSignedData,
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
}
return asn1.Marshal(outer)
}
func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) {
var ias issuerAndSerial
// The issuer RDNSequence has to match exactly the sequence in the certificate
// We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence
ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer}
ias.SerialNumber = cert.SerialNumber
return ias, nil
}
// signs the DER encoded form of the attributes with the private key
func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hash crypto.Hash) ([]byte, error) {
attrBytes, err := marshalAttributes(attrs)
if err != nil {
return nil, err
}
h := hash.New()
h.Write(attrBytes)
hashed := h.Sum(nil)
switch priv := pkey.(type) {
case *rsa.PrivateKey:
return rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hashed)
}
return nil, ErrUnsupportedAlgorithm
}
// concats and wraps the certificates in the RawValue structure
func marshalCertificates(certs []*x509.Certificate) rawCertificates {
var buf bytes.Buffer
for _, cert := range certs {
buf.Write(cert.Raw)
}
rawCerts, _ := marshalCertificateBytes(buf.Bytes())
return rawCerts
}
// Even though, the tag & length are stripped out during marshalling the
// RawContent, we have to encode it into the RawContent. If its missing,
// then `asn1.Marshal()` will strip out the certificate wrapper instead.
func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
b, err := asn1.Marshal(val)
if err != nil {
return rawCertificates{}, err
}
return rawCertificates{Raw: b}, nil
}
// DegenerateCertificate creates a signed data structure containing only the
// provided certificate or certificate chain.
func DegenerateCertificate(cert []byte) ([]byte, error) {
rawCert, err := marshalCertificateBytes(cert)
if err != nil {
return nil, err
}
emptyContent := contentInfo{ContentType: oidData}
sd := signedData{
Version: 1,
ContentInfo: emptyContent,
Certificates: rawCert,
CRLs: []pkix.CertificateList{},
}
content, err := asn1.Marshal(sd)
if err != nil {
return nil, err
}
signedContent := contentInfo{
ContentType: oidSignedData,
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
}
return asn1.Marshal(signedContent)
}
const (
EncryptionAlgorithmDESCBC = iota
EncryptionAlgorithmAES128GCM
)
// ContentEncryptionAlgorithm determines the algorithm used to encrypt the
// plaintext message. Change the value of this variable to change which
// algorithm is used in the Encrypt() function.
var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC
// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
// content with an unsupported algorithm.
var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported")
const nonceSize = 12
type aesGCMParameters struct {
Nonce []byte `asn1:"tag:4"`
ICVLen int
}
func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) {
// Create AES key and nonce
key := make([]byte, 16)
nonce := make([]byte, nonceSize)
_, err := rand.Read(key)
if err != nil {
return nil, nil, err
}
_, err = rand.Read(nonce)
if err != nil {
return nil, nil, err
}
// Encrypt content
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, err
}
ciphertext := gcm.Seal(nil, nonce, content, nil)
// Prepare ASN.1 Encrypted Content Info
paramSeq := aesGCMParameters{
Nonce: nonce,
ICVLen: gcm.Overhead(),
}
paramBytes, err := asn1.Marshal(paramSeq)
if err != nil {
return nil, nil, err
}
eci := encryptedContentInfo{
ContentType: oidData,
ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oidEncryptionAlgorithmAES128GCM,
Parameters: asn1.RawValue{
Tag: asn1.TagSequence,
Bytes: paramBytes,
},
},
EncryptedContent: marshalEncryptedContent(ciphertext),
}
return key, &eci, nil
}
func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) {
// Create DES key & CBC IV
key := make([]byte, 8)
iv := make([]byte, des.BlockSize)
_, err := rand.Read(key)
if err != nil {
return nil, nil, err
}
_, err = rand.Read(iv)
if err != nil {
return nil, nil, err
}
// Encrypt padded content
block, err := des.NewCipher(key)
if err != nil {
return nil, nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
plaintext, err := pad(content, mode.BlockSize())
cyphertext := make([]byte, len(plaintext))
mode.CryptBlocks(cyphertext, plaintext)
// Prepare ASN.1 Encrypted Content Info
eci := encryptedContentInfo{
ContentType: oidData,
ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oidEncryptionAlgorithmDESCBC,
Parameters: asn1.RawValue{Tag: 4, Bytes: iv},
},
EncryptedContent: marshalEncryptedContent(cyphertext),
}
return key, &eci, nil
}
// Encrypt creates and returns an envelope data PKCS7 structure with encrypted
// recipient keys for each recipient public key.
//
// The algorithm used to perform encryption is determined by the current value
// of the global ContentEncryptionAlgorithm package variable. By default, the
// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the
// value before calling Encrypt(). For example:
//
// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM
//
// TODO(fullsailor): Add support for encrypting content with other algorithms
func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) {
var eci *encryptedContentInfo
var key []byte
var err error
// Apply chosen symmetric encryption method
switch ContentEncryptionAlgorithm {
case EncryptionAlgorithmDESCBC:
key, eci, err = encryptDESCBC(content)
case EncryptionAlgorithmAES128GCM:
key, eci, err = encryptAES128GCM(content)
default:
return nil, ErrUnsupportedEncryptionAlgorithm
}
if err != nil {
return nil, err
}
// Prepare each recipient's encrypted cipher key
recipientInfos := make([]recipientInfo, len(recipients))
for i, recipient := range recipients {
encrypted, err := encryptKey(key, recipient)
if err != nil {
return nil, err
}
ias, err := cert2issuerAndSerial(recipient)
if err != nil {
return nil, err
}
info := recipientInfo{
Version: 0,
IssuerAndSerialNumber: ias,
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oidEncryptionAlgorithmRSA,
},
EncryptedKey: encrypted,
}
recipientInfos[i] = info
}
// Prepare envelope content
envelope := envelopedData{
EncryptedContentInfo: *eci,
Version: 0,
RecipientInfos: recipientInfos,
}
innerContent, err := asn1.Marshal(envelope)
if err != nil {
return nil, err
}
// Prepare outer payload structure
wrapper := contentInfo{
ContentType: oidEnvelopedData,
Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
}
return asn1.Marshal(wrapper)
}
func marshalEncryptedContent(content []byte) asn1.RawValue {
asn1Content, _ := asn1.Marshal(content)
return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
}
func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) {
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
}
return nil, ErrUnsupportedAlgorithm
}

133
vendor/github.com/fullsailor/pkcs7/x509.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the go/golang LICENSE file.
package pkcs7
// These are private constants and functions from the crypto/x509 package that
// are useful when dealing with signatures verified by x509 certificates
import (
"bytes"
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
)
var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
// but it's specified by ISO. Microsoft's makecert.exe has been known
// to produce certificates with this OID.
oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
)
var signatureAlgorithmDetails = []struct {
algo x509.SignatureAlgorithm
name string
oid asn1.ObjectIdentifier
pubKeyAlgo x509.PublicKeyAlgorithm
hash crypto.Hash
}{
{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
}
// pssParameters reflects the parameters in an AlgorithmIdentifier that
// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
type pssParameters struct {
// The following three fields are not marked as
// optional because the default values specify SHA-1,
// which is no longer suitable for use in signatures.
Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
SaltLength int `asn1:"explicit,tag:2"`
TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
}
// asn1.NullBytes is not available prior to Go 1.9
var nullBytes = []byte{5, 0}
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
for _, details := range signatureAlgorithmDetails {
if ai.Algorithm.Equal(details.oid) {
return details.algo
}
}
return x509.UnknownSignatureAlgorithm
}
// RSA PSS is special because it encodes important parameters
// in the Parameters.
var params pssParameters
if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, &params); err != nil {
return x509.UnknownSignatureAlgorithm
}
var mgf1HashFunc pkix.AlgorithmIdentifier
if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
return x509.UnknownSignatureAlgorithm
}
// PSS is greatly overburdened with options. This code forces
// them into three buckets by requiring that the MGF1 hash
// function always match the message hash function (as
// recommended in
// https://tools.ietf.org/html/rfc3447#section-8.1), that the
// salt length matches the hash length, and that the trailer
// field has the default value.
if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) ||
!params.MGF.Algorithm.Equal(oidMGF1) ||
!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
!bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) ||
params.TrailerField != 1 {
return x509.UnknownSignatureAlgorithm
}
switch {
case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
return x509.SHA256WithRSAPSS
case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
return x509.SHA384WithRSAPSS
case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
return x509.SHA512WithRSAPSS
}
return x509.UnknownSignatureAlgorithm
}