Add vendor for github.com/containerd/imgcrypt
Updates to version using containerd v2.0.0-rc Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
1
vendor/github.com/containers/ocicrypt/.gitignore
generated
vendored
Normal file
1
vendor/github.com/containers/ocicrypt/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*~
|
||||
35
vendor/github.com/containers/ocicrypt/.golangci.yml
generated
vendored
Normal file
35
vendor/github.com/containers/ocicrypt/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
linters:
|
||||
enable:
|
||||
- depguard
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- gofmt
|
||||
- goimports
|
||||
- revive
|
||||
- ineffassign
|
||||
- vet
|
||||
- unused
|
||||
- misspell
|
||||
|
||||
linters-settings:
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
files:
|
||||
- $all
|
||||
deny:
|
||||
- pkg: "io/ioutil"
|
||||
|
||||
revive:
|
||||
severity: error
|
||||
rules:
|
||||
- name: indent-error-flow
|
||||
severity: warning
|
||||
disabled: false
|
||||
|
||||
- name: error-strings
|
||||
disabled: false
|
||||
|
||||
staticcheck:
|
||||
# Suppress reports of deprecated packages
|
||||
checks: ["-SA1019"]
|
||||
10
vendor/github.com/containers/ocicrypt/ADOPTERS.md
generated
vendored
Normal file
10
vendor/github.com/containers/ocicrypt/ADOPTERS.md
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
Below are list of adopters of the `ocicrypt` library or supports use of OCI encrypted images:
|
||||
- [skopeo](https://github.com/containers/skopeo)
|
||||
- [buildah](https://github.com/containers/buildah)
|
||||
- [containerd](https://github.com/containerd/imgcrypt)
|
||||
- [nerdctl](https://github.com/containerd/nerdctl)
|
||||
- [distribution](https://github.com/distribution/distribution)
|
||||
|
||||
Below are the list of projects that are in the process of adopting support:
|
||||
- [quay](https://github.com/quay/quay)
|
||||
- [kata-containers](https://github.com/kata-containers/kata-containers)
|
||||
3
vendor/github.com/containers/ocicrypt/CODE-OF-CONDUCT.md
generated
vendored
Normal file
3
vendor/github.com/containers/ocicrypt/CODE-OF-CONDUCT.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
## The OCIcrypt Library Project Community Code of Conduct
|
||||
|
||||
The OCIcrypt Library project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md).
|
||||
189
vendor/github.com/containers/ocicrypt/LICENSE
generated
vendored
Normal file
189
vendor/github.com/containers/ocicrypt/LICENSE
generated
vendored
Normal 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.
|
||||
6
vendor/github.com/containers/ocicrypt/MAINTAINERS
generated
vendored
Normal file
6
vendor/github.com/containers/ocicrypt/MAINTAINERS
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# ocicrypt maintainers
|
||||
#
|
||||
# Github ID, Name, Email Address
|
||||
lumjjb, Brandon Lum, lumjjb@gmail.com
|
||||
stefanberger, Stefan Berger, stefanb@linux.ibm.com
|
||||
arronwy, Arron Wang, arron.wang@intel.com
|
||||
35
vendor/github.com/containers/ocicrypt/Makefile
generated
vendored
Normal file
35
vendor/github.com/containers/ocicrypt/Makefile
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# 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.
|
||||
|
||||
.PHONY: check build decoder generate-protobuf
|
||||
|
||||
all: build
|
||||
|
||||
FORCE:
|
||||
|
||||
check:
|
||||
golangci-lint run
|
||||
|
||||
build: vendor
|
||||
go build ./...
|
||||
|
||||
vendor:
|
||||
go mod tidy
|
||||
|
||||
test:
|
||||
go clean -testcache
|
||||
go test ./... -test.v
|
||||
|
||||
generate-protobuf:
|
||||
protoc -I utils/keyprovider/ utils/keyprovider/keyprovider.proto --go_out=plugins=grpc:utils/keyprovider
|
||||
50
vendor/github.com/containers/ocicrypt/README.md
generated
vendored
Normal file
50
vendor/github.com/containers/ocicrypt/README.md
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# 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.
|
||||
|
||||
Consumers of OCIcrypt:
|
||||
|
||||
- [containerd/imgcrypt](https://github.com/containerd/imgcrypt)
|
||||
- [cri-o](https://github.com/cri-o/cri-o)
|
||||
- [skopeo](https://github.com/containers/skopeo)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
There are various levels of usage for this library. The main consumers of these would be runtime/build 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 responsibility 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 asymmetric 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.
|
||||
|
||||
|
||||
#### Keyprovider interface
|
||||
|
||||
As part of the keywrap interface, there is a [keyprovider](https://github.com/containers/ocicrypt/blob/main/docs/keyprovider.md) implementation that allows one to call out to a binary or service.
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
## Ocicrypt Pkcs11 Support
|
||||
|
||||
Ocicrypt Pkcs11 support is currently experiemental. For more details, please refer to the [this document](docs/pkcs11.md).
|
||||
3
vendor/github.com/containers/ocicrypt/SECURITY.md
generated
vendored
Normal file
3
vendor/github.com/containers/ocicrypt/SECURITY.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
## Security and Disclosure Information Policy for the OCIcrypt Library Project
|
||||
|
||||
The OCIcrypt Library Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects.
|
||||
161
vendor/github.com/containers/ocicrypt/blockcipher/blockcipher.go
generated
vendored
Normal file
161
vendor/github.com/containers/ocicrypt/blockcipher/blockcipher.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// 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, fmt.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[typ]; ok {
|
||||
return c.Decrypt(encDataReader, opt)
|
||||
}
|
||||
return nil, LayerBlockCipherOptions{}, fmt.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, fmt.Errorf("unable to set up Cipher AES-256-CTR: %w", err)
|
||||
}
|
||||
|
||||
return &h, nil
|
||||
}
|
||||
193
vendor/github.com/containers/ocicrypt/blockcipher/blockcipher_aes_ctr.go
generated
vendored
Normal file
193
vendor/github.com/containers/ocicrypt/blockcipher/blockcipher_aes_ctr.go
generated
vendored
Normal 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/containers/ocicrypt/utils"
|
||||
)
|
||||
|
||||
// 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 = fmt.Errorf("could not write to hmac: %w", err)
|
||||
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 = fmt.Errorf("could not write to hmac: %w", err)
|
||||
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{}, fmt.Errorf("unable to generate random nonce: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return LayerBlockCipherOptions{}, fmt.Errorf("aes.NewCipher failed: %w", err)
|
||||
}
|
||||
|
||||
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
114
vendor/github.com/containers/ocicrypt/config/config.go
generated
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
246
vendor/github.com/containers/ocicrypt/config/constructors.go
generated
vendored
Normal file
246
vendor/github.com/containers/ocicrypt/config/constructors.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/ocicrypt/crypto/pkcs11"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// EncryptWithPkcs11 returns a CryptoConfig to encrypt with configured pkcs11 parameters
|
||||
func EncryptWithPkcs11(pkcs11Config *pkcs11.Pkcs11Config, pkcs11Pubkeys, pkcs11Yamls [][]byte) (CryptoConfig, error) {
|
||||
dc := DecryptConfig{}
|
||||
ep := map[string][][]byte{}
|
||||
|
||||
if len(pkcs11Yamls) > 0 {
|
||||
if pkcs11Config == nil {
|
||||
return CryptoConfig{}, errors.New("pkcs11Config must not be nil")
|
||||
}
|
||||
p11confYaml, err := yaml.Marshal(pkcs11Config)
|
||||
if err != nil {
|
||||
return CryptoConfig{}, fmt.Errorf("Could not marshal Pkcs11Config to Yaml: %w", err)
|
||||
}
|
||||
|
||||
dc = DecryptConfig{
|
||||
Parameters: map[string][][]byte{
|
||||
"pkcs11-config": {p11confYaml},
|
||||
},
|
||||
}
|
||||
ep["pkcs11-yamls"] = pkcs11Yamls
|
||||
}
|
||||
if len(pkcs11Pubkeys) > 0 {
|
||||
ep["pkcs11-pubkeys"] = pkcs11Pubkeys
|
||||
}
|
||||
|
||||
return CryptoConfig{
|
||||
EncryptConfig: &EncryptConfig{
|
||||
Parameters: ep,
|
||||
DecryptConfig: dc,
|
||||
},
|
||||
DecryptConfig: &dc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EncryptWithKeyProvider returns a CryptoConfig to encrypt with configured keyprovider parameters
|
||||
func EncryptWithKeyProvider(keyProviders [][]byte) (CryptoConfig, error) {
|
||||
dc := DecryptConfig{}
|
||||
ep := make(map[string][][]byte)
|
||||
for _, keyProvider := range keyProviders {
|
||||
keyProvidersStr := string(keyProvider)
|
||||
idx := strings.Index(keyProvidersStr, ":")
|
||||
if idx > 0 {
|
||||
ep[keyProvidersStr[:idx]] = append(ep[keyProvidersStr[:idx]], []byte(keyProvidersStr[idx+1:]))
|
||||
} else {
|
||||
ep[keyProvidersStr] = append(ep[keyProvidersStr], []byte("Enabled"))
|
||||
}
|
||||
}
|
||||
|
||||
return CryptoConfig{
|
||||
EncryptConfig: &EncryptConfig{
|
||||
Parameters: ep,
|
||||
DecryptConfig: dc,
|
||||
},
|
||||
DecryptConfig: &dc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecryptWithKeyProvider returns a CryptoConfig to decrypt with configured keyprovider parameters
|
||||
func DecryptWithKeyProvider(keyProviders [][]byte) (CryptoConfig, error) {
|
||||
dp := make(map[string][][]byte)
|
||||
ep := map[string][][]byte{}
|
||||
for _, keyProvider := range keyProviders {
|
||||
keyProvidersStr := string(keyProvider)
|
||||
idx := strings.Index(keyProvidersStr, ":")
|
||||
if idx > 0 {
|
||||
dp[keyProvidersStr[:idx]] = append(dp[keyProvidersStr[:idx]], []byte(keyProvidersStr[idx+1:]))
|
||||
} else {
|
||||
dp[keyProvidersStr] = append(dp[keyProvidersStr], []byte("Enabled"))
|
||||
}
|
||||
}
|
||||
dc := DecryptConfig{
|
||||
Parameters: dp,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// DecryptWithPkcs11Yaml returns a CryptoConfig to decrypt with pkcs11 YAML formatted key files
|
||||
func DecryptWithPkcs11Yaml(pkcs11Config *pkcs11.Pkcs11Config, pkcs11Yamls [][]byte) (CryptoConfig, error) {
|
||||
p11confYaml, err := yaml.Marshal(pkcs11Config)
|
||||
if err != nil {
|
||||
return CryptoConfig{}, fmt.Errorf("Could not marshal Pkcs11Config to Yaml: %w", err)
|
||||
}
|
||||
|
||||
dc := DecryptConfig{
|
||||
Parameters: map[string][][]byte{
|
||||
"pkcs11-yamls": pkcs11Yamls,
|
||||
"pkcs11-config": {p11confYaml},
|
||||
},
|
||||
}
|
||||
|
||||
ep := map[string][][]byte{}
|
||||
|
||||
return CryptoConfig{
|
||||
EncryptConfig: &EncryptConfig{
|
||||
Parameters: ep,
|
||||
DecryptConfig: dc,
|
||||
},
|
||||
DecryptConfig: &dc,
|
||||
}, nil
|
||||
}
|
||||
80
vendor/github.com/containers/ocicrypt/config/keyprovider-config/config.go
generated
vendored
Normal file
80
vendor/github.com/containers/ocicrypt/config/keyprovider-config/config.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Command describes the structure of command, it consist of path and args, where path defines the location of
|
||||
// binary executable and args are passed on to the binary executable
|
||||
type Command struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
// KeyProviderAttrs describes the structure of key provider, it defines the way of invocation to key provider
|
||||
type KeyProviderAttrs struct {
|
||||
Command *Command `json:"cmd,omitempty"`
|
||||
Grpc string `json:"grpc,omitempty"`
|
||||
}
|
||||
|
||||
// OcicryptConfig represents the format of an ocicrypt_provider.conf config file
|
||||
type OcicryptConfig struct {
|
||||
KeyProviderConfig map[string]KeyProviderAttrs `json:"key-providers"`
|
||||
}
|
||||
|
||||
const ENVVARNAME = "OCICRYPT_KEYPROVIDER_CONFIG"
|
||||
|
||||
// parseConfigFile parses a configuration file; it is not an error if the configuration file does
|
||||
// not exist, so no error is returned.
|
||||
func parseConfigFile(filename string) (*OcicryptConfig, error) {
|
||||
// a non-existent config file is not an error
|
||||
_, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ic := &OcicryptConfig{}
|
||||
err = json.Unmarshal(data, ic)
|
||||
return ic, err
|
||||
}
|
||||
|
||||
// getConfiguration tries to read the configuration file at the following locations
|
||||
// ${OCICRYPT_KEYPROVIDER_CONFIG} == "/etc/ocicrypt_keyprovider.yaml"
|
||||
// If no configuration file could be found or read a null pointer is returned
|
||||
func GetConfiguration() (*OcicryptConfig, error) {
|
||||
var ic *OcicryptConfig
|
||||
var err error
|
||||
filename := os.Getenv(ENVVARNAME)
|
||||
if len(filename) > 0 {
|
||||
ic, err = parseConfigFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while parsing keyprovider config file: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
return ic, nil
|
||||
}
|
||||
134
vendor/github.com/containers/ocicrypt/crypto/pkcs11/common.go
generated
vendored
Normal file
134
vendor/github.com/containers/ocicrypt/crypto/pkcs11/common.go
generated
vendored
Normal 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 pkcs11
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
pkcs11uri "github.com/stefanberger/go-pkcs11uri"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Pkcs11KeyFile describes the format of the pkcs11 (private) key file.
|
||||
// It also carries pkcs11 module related environment variables that are transferred to the
|
||||
// Pkcs11URI object and activated when the pkcs11 module is used.
|
||||
type Pkcs11KeyFile struct {
|
||||
Pkcs11 struct {
|
||||
Uri string `yaml:"uri"`
|
||||
} `yaml:"pkcs11"`
|
||||
Module struct {
|
||||
Env map[string]string `yaml:"env,omitempty"`
|
||||
} `yaml:"module"`
|
||||
}
|
||||
|
||||
// Pkcs11KeyFileObject is a representation of the Pkcs11KeyFile with the pkcs11 URI as an object
|
||||
type Pkcs11KeyFileObject struct {
|
||||
Uri *pkcs11uri.Pkcs11URI
|
||||
}
|
||||
|
||||
// ParsePkcs11Uri parses a pkcs11 URI
|
||||
func ParsePkcs11Uri(uri string) (*pkcs11uri.Pkcs11URI, error) {
|
||||
p11uri := pkcs11uri.New()
|
||||
err := p11uri.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not parse Pkcs11URI from file: %w", err)
|
||||
}
|
||||
return p11uri, err
|
||||
}
|
||||
|
||||
// ParsePkcs11KeyFile parses a pkcs11 key file holding a pkcs11 URI describing a private key.
|
||||
// The file has the following yaml format:
|
||||
// pkcs11:
|
||||
// - uri : <pkcs11 uri>
|
||||
// An error is returned if the pkcs11 URI is malformed
|
||||
func ParsePkcs11KeyFile(yamlstr []byte) (*Pkcs11KeyFileObject, error) {
|
||||
p11keyfile := Pkcs11KeyFile{}
|
||||
|
||||
err := yaml.Unmarshal(yamlstr, &p11keyfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not unmarshal pkcs11 keyfile: %w", err)
|
||||
}
|
||||
|
||||
p11uri, err := ParsePkcs11Uri(p11keyfile.Pkcs11.Uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p11uri.SetEnvMap(p11keyfile.Module.Env)
|
||||
|
||||
return &Pkcs11KeyFileObject{Uri: p11uri}, err
|
||||
}
|
||||
|
||||
// IsPkcs11PrivateKey checks whether the given YAML represents a Pkcs11 private key
|
||||
func IsPkcs11PrivateKey(yamlstr []byte) bool {
|
||||
_, err := ParsePkcs11KeyFile(yamlstr)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsPkcs11PublicKey checks whether the given YAML represents a Pkcs11 public key
|
||||
func IsPkcs11PublicKey(yamlstr []byte) bool {
|
||||
_, err := ParsePkcs11KeyFile(yamlstr)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Pkcs11Config describes the layout of a pkcs11 config file
|
||||
// The file has the following yaml format:
|
||||
// module-directories:
|
||||
// - /usr/lib64/pkcs11/
|
||||
// allowd-module-paths
|
||||
// - /usr/lib64/pkcs11/libsofthsm2.so
|
||||
type Pkcs11Config struct {
|
||||
ModuleDirectories []string `yaml:"module-directories"`
|
||||
AllowedModulePaths []string `yaml:"allowed-module-paths"`
|
||||
}
|
||||
|
||||
// GetDefaultModuleDirectories returns module directories covering
|
||||
// a variety of Linux distros
|
||||
func GetDefaultModuleDirectories() []string {
|
||||
dirs := []string{
|
||||
"/usr/lib64/pkcs11/", // Fedora,RHEL,openSUSE
|
||||
"/usr/lib/pkcs11/", // Fedora,ArchLinux
|
||||
"/usr/local/lib/pkcs11/",
|
||||
"/usr/lib/softhsm/", // Debian,Ubuntu
|
||||
}
|
||||
|
||||
// Debian directory: /usr/lib/(x86_64|aarch64|arm|powerpc64le|riscv64|s390x)-linux-gnu/
|
||||
hosttype, ostype, q := getHostAndOsType()
|
||||
if len(hosttype) > 0 {
|
||||
dir := fmt.Sprintf("/usr/lib/%s-%s-%s/", hosttype, ostype, q)
|
||||
dirs = append(dirs, dir)
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// GetDefaultModuleDirectoresFormatted returns the default module directories formatted for YAML
|
||||
func GetDefaultModuleDirectoriesYaml(indent string) string {
|
||||
res := ""
|
||||
|
||||
for _, dir := range GetDefaultModuleDirectories() {
|
||||
res += indent + "- " + dir + "\n"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ParsePkcs11ConfigFile parses a pkcs11 config file hat influences the module search behavior
|
||||
// as well as the set of modules that users are allowed to use
|
||||
func ParsePkcs11ConfigFile(yamlstr []byte) (*Pkcs11Config, error) {
|
||||
p11conf := Pkcs11Config{}
|
||||
|
||||
err := yaml.Unmarshal(yamlstr, &p11conf)
|
||||
if err != nil {
|
||||
return &p11conf, fmt.Errorf("Could not parse Pkcs11Config: %w", err)
|
||||
}
|
||||
return &p11conf, nil
|
||||
}
|
||||
485
vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers.go
generated
vendored
Normal file
485
vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers.go
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
//go:build cgo
|
||||
// +build cgo
|
||||
|
||||
/*
|
||||
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 pkcs11
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
pkcs11uri "github.com/stefanberger/go-pkcs11uri"
|
||||
)
|
||||
|
||||
var (
|
||||
// OAEPLabel defines the label we use for OAEP encryption; this cannot be changed
|
||||
OAEPLabel = []byte("")
|
||||
|
||||
// OAEPSha1Params describes the OAEP parameters with sha1 hash algorithm; needed by SoftHSM
|
||||
OAEPSha1Params = &pkcs11.OAEPParams{
|
||||
HashAlg: pkcs11.CKM_SHA_1,
|
||||
MGF: pkcs11.CKG_MGF1_SHA1,
|
||||
SourceType: pkcs11.CKZ_DATA_SPECIFIED,
|
||||
SourceData: OAEPLabel,
|
||||
}
|
||||
// OAEPSha256Params describes the OAEP parameters with sha256 hash algorithm
|
||||
OAEPSha256Params = &pkcs11.OAEPParams{
|
||||
HashAlg: pkcs11.CKM_SHA256,
|
||||
MGF: pkcs11.CKG_MGF1_SHA256,
|
||||
SourceType: pkcs11.CKZ_DATA_SPECIFIED,
|
||||
SourceData: OAEPLabel,
|
||||
}
|
||||
)
|
||||
|
||||
// rsaPublicEncryptOAEP encrypts the given plaintext with the given *rsa.PublicKey; the
|
||||
// environment variable OCICRYPT_OAEP_HASHALG can be set to 'sha1' to force usage of sha1 for OAEP (SoftHSM).
|
||||
// This function is needed by clients who are using a public key file for pkcs11 encryption
|
||||
func rsaPublicEncryptOAEP(pubKey *rsa.PublicKey, plaintext []byte) ([]byte, string, error) {
|
||||
var (
|
||||
hashfunc hash.Hash
|
||||
hashalg string
|
||||
)
|
||||
|
||||
oaephash := os.Getenv("OCICRYPT_OAEP_HASHALG")
|
||||
// The default is sha256 (previously was sha1)
|
||||
switch strings.ToLower(oaephash) {
|
||||
case "sha1":
|
||||
hashfunc = sha1.New()
|
||||
hashalg = "sha1"
|
||||
case "sha256", "":
|
||||
hashfunc = sha256.New()
|
||||
hashalg = "sha256"
|
||||
default:
|
||||
return nil, "", fmt.Errorf("Unsupported OAEP hash '%s'", oaephash)
|
||||
}
|
||||
ciphertext, err := rsa.EncryptOAEP(hashfunc, rand.Reader, pubKey, plaintext, OAEPLabel)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("rss.EncryptOAEP failed: %w", err)
|
||||
}
|
||||
|
||||
return ciphertext, hashalg, nil
|
||||
}
|
||||
|
||||
// pkcs11UriGetLoginParameters gets the parameters necessary for login from the Pkcs11URI
|
||||
// PIN and module are mandatory; slot-id is optional and if not found -1 will be returned
|
||||
// For a privateKeyOperation a PIN is required and if none is given, this function will return an error
|
||||
func pkcs11UriGetLoginParameters(p11uri *pkcs11uri.Pkcs11URI, privateKeyOperation bool) (string, string, int64, error) {
|
||||
var (
|
||||
pin string
|
||||
err error
|
||||
)
|
||||
if privateKeyOperation {
|
||||
if !p11uri.HasPIN() {
|
||||
return "", "", 0, errors.New("Missing PIN for private key operation")
|
||||
}
|
||||
}
|
||||
// some devices require a PIN to find a *public* key object, others don't
|
||||
pin, _ = p11uri.GetPIN()
|
||||
|
||||
module, err := p11uri.GetModule()
|
||||
if err != nil {
|
||||
return "", "", 0, fmt.Errorf("No module available in pkcs11 URI: %w", err)
|
||||
}
|
||||
|
||||
slotid := int64(-1)
|
||||
|
||||
slot, ok := p11uri.GetPathAttribute("slot-id", false)
|
||||
if ok {
|
||||
slotid, err = strconv.ParseInt(slot, 10, 64)
|
||||
if err != nil {
|
||||
return "", "", 0, fmt.Errorf("slot-id is not a valid number: %w", err)
|
||||
}
|
||||
if slotid < 0 {
|
||||
return "", "", 0, fmt.Errorf("slot-id is a negative number")
|
||||
}
|
||||
if uint64(slotid) > 0xffffffff {
|
||||
return "", "", 0, fmt.Errorf("slot-id is larger than 32 bit")
|
||||
}
|
||||
}
|
||||
|
||||
return pin, module, slotid, nil
|
||||
}
|
||||
|
||||
// pkcs11UriGetKeyIdAndLabel gets the key label by retrieving the value of the 'object' attribute
|
||||
func pkcs11UriGetKeyIdAndLabel(p11uri *pkcs11uri.Pkcs11URI) (string, string, error) {
|
||||
keyid, ok2 := p11uri.GetPathAttribute("id", false)
|
||||
label, ok1 := p11uri.GetPathAttribute("object", false)
|
||||
if !ok1 && !ok2 {
|
||||
return "", "", errors.New("Neither 'id' nor 'object' attributes were found in pkcs11 URI")
|
||||
}
|
||||
return keyid, label, nil
|
||||
}
|
||||
|
||||
// pkcs11OpenSession opens a session with a pkcs11 device at the given slot and logs in with the given PIN
|
||||
func pkcs11OpenSession(p11ctx *pkcs11.Ctx, slotid uint, pin string) (session pkcs11.SessionHandle, err error) {
|
||||
session, err = p11ctx.OpenSession(slotid, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("OpenSession to slot %d failed: %w", slotid, err)
|
||||
}
|
||||
if len(pin) > 0 {
|
||||
err = p11ctx.Login(session, pkcs11.CKU_USER, pin)
|
||||
if err != nil {
|
||||
_ = p11ctx.CloseSession(session)
|
||||
return 0, fmt.Errorf("Could not login to device: %w", err)
|
||||
}
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// pkcs11UriLogin uses the given pkcs11 URI to select the pkcs11 module (shared library) and to get
|
||||
// the PIN to use for login; if the URI contains a slot-id, the given slot-id will be used, otherwise
|
||||
// one slot after the other will be attempted and the first one where login succeeds will be used
|
||||
func pkcs11UriLogin(p11uri *pkcs11uri.Pkcs11URI, privateKeyOperation bool) (ctx *pkcs11.Ctx, session pkcs11.SessionHandle, err error) {
|
||||
pin, module, slotid, err := pkcs11UriGetLoginParameters(p11uri, privateKeyOperation)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
p11ctx := pkcs11.New(module)
|
||||
if p11ctx == nil {
|
||||
return nil, 0, errors.New("Please check module path, input is: " + module)
|
||||
}
|
||||
|
||||
err = p11ctx.Initialize()
|
||||
if err != nil {
|
||||
p11Err := err.(pkcs11.Error)
|
||||
if p11Err != pkcs11.CKR_CRYPTOKI_ALREADY_INITIALIZED {
|
||||
return nil, 0, fmt.Errorf("Initialize failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if slotid >= 0 {
|
||||
session, err := pkcs11OpenSession(p11ctx, uint(slotid), pin)
|
||||
return p11ctx, session, err
|
||||
}
|
||||
|
||||
slots, err := p11ctx.GetSlotList(true)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("GetSlotList failed: %w", err)
|
||||
}
|
||||
|
||||
tokenlabel, ok := p11uri.GetPathAttribute("token", false)
|
||||
if !ok {
|
||||
return nil, 0, errors.New("Missing 'token' attribute since 'slot-id' was not given")
|
||||
}
|
||||
|
||||
for _, slot := range slots {
|
||||
ti, err := p11ctx.GetTokenInfo(slot)
|
||||
if err != nil || ti.Label != tokenlabel {
|
||||
continue
|
||||
}
|
||||
|
||||
session, err = pkcs11OpenSession(p11ctx, slot, pin)
|
||||
if err == nil {
|
||||
return p11ctx, session, err
|
||||
}
|
||||
}
|
||||
if len(pin) > 0 {
|
||||
return nil, 0, errors.New("Could not create session to any slot and/or log in")
|
||||
}
|
||||
return nil, 0, errors.New("Could not create session to any slot")
|
||||
}
|
||||
|
||||
func pkcs11Logout(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) {
|
||||
_ = ctx.Logout(session)
|
||||
_ = ctx.CloseSession(session)
|
||||
_ = ctx.Finalize()
|
||||
ctx.Destroy()
|
||||
}
|
||||
|
||||
// findObject finds an object of the given class with the given keyid and/or label
|
||||
func findObject(p11ctx *pkcs11.Ctx, session pkcs11.SessionHandle, class uint, keyid, label string) (pkcs11.ObjectHandle, error) {
|
||||
msg := ""
|
||||
|
||||
template := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, class),
|
||||
}
|
||||
if len(label) > 0 {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label))
|
||||
msg = fmt.Sprintf("label '%s'", label)
|
||||
}
|
||||
if len(keyid) > 0 {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_ID, keyid))
|
||||
if len(msg) > 0 {
|
||||
msg += " and "
|
||||
}
|
||||
msg += url.PathEscape(keyid)
|
||||
}
|
||||
|
||||
if err := p11ctx.FindObjectsInit(session, template); err != nil {
|
||||
return 0, fmt.Errorf("FindObjectsInit failed: %w", err)
|
||||
}
|
||||
|
||||
obj, _, err := p11ctx.FindObjects(session, 100)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("FindObjects failed: %w", err)
|
||||
}
|
||||
|
||||
if err := p11ctx.FindObjectsFinal(session); err != nil {
|
||||
return 0, fmt.Errorf("FindObjectsFinal failed: %w", err)
|
||||
}
|
||||
if len(obj) > 1 {
|
||||
return 0, fmt.Errorf("There are too many (=%d) keys with %s", len(obj), msg)
|
||||
} else if len(obj) == 1 {
|
||||
return obj[0], nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("Could not find any object with %s", msg)
|
||||
}
|
||||
|
||||
// publicEncryptOAEP uses a public key described by a pkcs11 URI to OAEP encrypt the given plaintext
|
||||
func publicEncryptOAEP(pubKey *Pkcs11KeyFileObject, plaintext []byte) ([]byte, string, error) {
|
||||
oldenv, err := setEnvVars(pubKey.Uri.GetEnvMap())
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer restoreEnv(oldenv)
|
||||
|
||||
p11ctx, session, err := pkcs11UriLogin(pubKey.Uri, false)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer pkcs11Logout(p11ctx, session)
|
||||
|
||||
keyid, label, err := pkcs11UriGetKeyIdAndLabel(pubKey.Uri)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
p11PubKey, err := findObject(p11ctx, session, pkcs11.CKO_PUBLIC_KEY, keyid, label)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var hashalg string
|
||||
|
||||
var oaep *pkcs11.OAEPParams
|
||||
oaephash := os.Getenv("OCICRYPT_OAEP_HASHALG")
|
||||
// The default is sha256 (previously was sha1)
|
||||
switch strings.ToLower(oaephash) {
|
||||
case "sha1":
|
||||
oaep = OAEPSha1Params
|
||||
hashalg = "sha1"
|
||||
case "sha256", "":
|
||||
oaep = OAEPSha256Params
|
||||
hashalg = "sha256"
|
||||
default:
|
||||
return nil, "", fmt.Errorf("Unsupported OAEP hash '%s'", oaephash)
|
||||
}
|
||||
|
||||
err = p11ctx.EncryptInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_OAEP, oaep)}, p11PubKey)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("EncryptInit error: %w", err)
|
||||
}
|
||||
|
||||
ciphertext, err := p11ctx.Encrypt(session, plaintext)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("Encrypt failed: %w", err)
|
||||
}
|
||||
return ciphertext, hashalg, nil
|
||||
}
|
||||
|
||||
// privateDecryptOAEP uses a pkcs11 URI describing a private key to OAEP decrypt a ciphertext
|
||||
func privateDecryptOAEP(privKeyObj *Pkcs11KeyFileObject, ciphertext []byte, hashalg string) ([]byte, error) {
|
||||
oldenv, err := setEnvVars(privKeyObj.Uri.GetEnvMap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer restoreEnv(oldenv)
|
||||
|
||||
p11ctx, session, err := pkcs11UriLogin(privKeyObj.Uri, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer pkcs11Logout(p11ctx, session)
|
||||
|
||||
keyid, label, err := pkcs11UriGetKeyIdAndLabel(privKeyObj.Uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p11PrivKey, err := findObject(p11ctx, session, pkcs11.CKO_PRIVATE_KEY, keyid, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var oaep *pkcs11.OAEPParams
|
||||
|
||||
// An empty string from the Hash in the JSON historically defaults to sha1.
|
||||
switch hashalg {
|
||||
case "sha1", "":
|
||||
oaep = OAEPSha1Params
|
||||
case "sha256":
|
||||
oaep = OAEPSha256Params
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported hash algorithm '%s' for decryption", hashalg)
|
||||
}
|
||||
|
||||
err = p11ctx.DecryptInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_OAEP, oaep)}, p11PrivKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("DecryptInit failed: %w", err)
|
||||
}
|
||||
plaintext, err := p11ctx.Decrypt(session, ciphertext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Decrypt failed: %w", err)
|
||||
}
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
//
|
||||
// The following part deals with the JSON formatted message for multiple pkcs11 recipients
|
||||
//
|
||||
|
||||
// Pkcs11Blob holds the encrypted blobs for all recipients; this is what we will put into the image's annotations
|
||||
type Pkcs11Blob struct {
|
||||
Version uint `json:"version"`
|
||||
Recipients []Pkcs11Recipient `json:"recipients"`
|
||||
}
|
||||
|
||||
// Pkcs11Recipient holds the b64-encoded and encrypted blob for a particular recipient
|
||||
type Pkcs11Recipient struct {
|
||||
Version uint `json:"version"`
|
||||
Blob string `json:"blob"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptMultiple encrypts for one or multiple pkcs11 devices; the public keys passed to this function
|
||||
// may either be *rsa.PublicKey or *pkcs11uri.Pkcs11URI; the returned byte array is a JSON string of the
|
||||
// following format:
|
||||
// {
|
||||
// recipients: [ // recipient list
|
||||
// {
|
||||
// "version": 0,
|
||||
// "blob": <base64 encoded RSA OAEP encrypted blob>,
|
||||
// "hash": <hash used for OAEP other than 'sha256'>
|
||||
// } ,
|
||||
// {
|
||||
// "version": 0,
|
||||
// "blob": <base64 encoded RSA OAEP encrypted blob>,
|
||||
// "hash": <hash used for OAEP other than 'sha256'>
|
||||
// } ,
|
||||
// [...]
|
||||
// ]
|
||||
// }
|
||||
func EncryptMultiple(pubKeys []interface{}, data []byte) ([]byte, error) {
|
||||
var (
|
||||
ciphertext []byte
|
||||
err error
|
||||
pkcs11blob Pkcs11Blob = Pkcs11Blob{Version: 0}
|
||||
hashalg string
|
||||
)
|
||||
|
||||
for _, pubKey := range pubKeys {
|
||||
switch pkey := pubKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
ciphertext, hashalg, err = rsaPublicEncryptOAEP(pkey, data)
|
||||
case *Pkcs11KeyFileObject:
|
||||
ciphertext, hashalg, err = publicEncryptOAEP(pkey, data)
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported key object type for pkcs11 public key")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recipient := Pkcs11Recipient{
|
||||
Version: 0,
|
||||
Blob: base64.StdEncoding.EncodeToString(ciphertext),
|
||||
Hash: hashalg,
|
||||
}
|
||||
|
||||
pkcs11blob.Recipients = append(pkcs11blob.Recipients, recipient)
|
||||
}
|
||||
return json.Marshal(&pkcs11blob)
|
||||
}
|
||||
|
||||
// Decrypt tries to decrypt one of the recipients' blobs using a pkcs11 private key.
|
||||
// The input pkcs11blobstr is a string with the following format:
|
||||
// {
|
||||
// recipients: [ // recipient list
|
||||
// {
|
||||
// "version": 0,
|
||||
// "blob": <base64 encoded RSA OAEP encrypted blob>,
|
||||
// "hash": <hash used for OAEP other than 'sha1'>
|
||||
// } ,
|
||||
// {
|
||||
// "version": 0,
|
||||
// "blob": <base64 encoded RSA OAEP encrypted blob>,
|
||||
// "hash": <hash used for OAEP other than 'sha1'>
|
||||
// } ,
|
||||
// [...]
|
||||
// }
|
||||
// Note: More recent versions of this code explicitly write 'sha1'
|
||||
// while older versions left it empty in case of 'sha1'.
|
||||
func Decrypt(privKeyObjs []*Pkcs11KeyFileObject, pkcs11blobstr []byte) ([]byte, error) {
|
||||
pkcs11blob := Pkcs11Blob{}
|
||||
err := json.Unmarshal(pkcs11blobstr, &pkcs11blob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not parse Pkcs11Blob: %w", err)
|
||||
}
|
||||
switch pkcs11blob.Version {
|
||||
case 0:
|
||||
// latest supported version
|
||||
default:
|
||||
return nil, fmt.Errorf("found Pkcs11Blob with version %d but maximum supported version is 0", pkcs11blob.Version)
|
||||
}
|
||||
// since we do trial and error, collect all encountered errors
|
||||
errs := ""
|
||||
|
||||
for _, recipient := range pkcs11blob.Recipients {
|
||||
switch recipient.Version {
|
||||
case 0:
|
||||
// last supported version
|
||||
default:
|
||||
return nil, fmt.Errorf("found Pkcs11Recipient with version %d but maximum supported version is 0", recipient.Version)
|
||||
}
|
||||
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(recipient.Blob)
|
||||
if err != nil || len(ciphertext) == 0 {
|
||||
// This should never happen... we skip over decoding issues
|
||||
errs += fmt.Sprintf("Base64 decoding failed: %s\n", err)
|
||||
continue
|
||||
}
|
||||
// try all keys until one works
|
||||
for _, privKeyObj := range privKeyObjs {
|
||||
plaintext, err := privateDecryptOAEP(privKeyObj, ciphertext, recipient.Hash)
|
||||
if err == nil {
|
||||
return plaintext, nil
|
||||
}
|
||||
if uri, err2 := privKeyObj.Uri.Format(); err2 == nil {
|
||||
errs += fmt.Sprintf("%s : %s\n", uri, err)
|
||||
} else {
|
||||
errs += fmt.Sprintf("%s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Could not find a pkcs11 key for decryption:\n%s", errs)
|
||||
}
|
||||
30
vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers_nocgo.go
generated
vendored
Normal file
30
vendor/github.com/containers/ocicrypt/crypto/pkcs11/pkcs11helpers_nocgo.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
//go:build !cgo
|
||||
// +build !cgo
|
||||
|
||||
/*
|
||||
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 pkcs11
|
||||
|
||||
import "fmt"
|
||||
|
||||
func EncryptMultiple(pubKeys []interface{}, data []byte) ([]byte, error) {
|
||||
return nil, fmt.Errorf("ocicrypt pkcs11 not supported on this build")
|
||||
}
|
||||
|
||||
func Decrypt(privKeyObjs []*Pkcs11KeyFileObject, pkcs11blobstr []byte) ([]byte, error) {
|
||||
return nil, fmt.Errorf("ocicrypt pkcs11 not supported on this build")
|
||||
}
|
||||
115
vendor/github.com/containers/ocicrypt/crypto/pkcs11/utils.go
generated
vendored
Normal file
115
vendor/github.com/containers/ocicrypt/crypto/pkcs11/utils.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
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 pkcs11
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
envLock sync.Mutex
|
||||
)
|
||||
|
||||
// setEnvVars sets the environment variables given in the map and locks the environment from
|
||||
// modification with the same function; if successful, you *must* call restoreEnv with the return
|
||||
// value from this function
|
||||
func setEnvVars(env map[string]string) ([]string, error) {
|
||||
envLock.Lock()
|
||||
|
||||
if len(env) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
oldenv := os.Environ()
|
||||
|
||||
for k, v := range env {
|
||||
err := os.Setenv(k, v)
|
||||
if err != nil {
|
||||
restoreEnv(oldenv)
|
||||
return nil, fmt.Errorf("Could not set environment variable '%s' to '%s': %w", k, v, err)
|
||||
}
|
||||
}
|
||||
|
||||
return oldenv, nil
|
||||
}
|
||||
|
||||
func arrayToMap(elements []string) map[string]string {
|
||||
o := make(map[string]string)
|
||||
|
||||
for _, element := range elements {
|
||||
p := strings.SplitN(element, "=", 2)
|
||||
if len(p) == 2 {
|
||||
o[p[0]] = p[1]
|
||||
}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// restoreEnv restores the environment to be exactly as given in the array of strings
|
||||
// and unlocks the lock
|
||||
func restoreEnv(envs []string) {
|
||||
if envs != nil && len(envs) >= 0 {
|
||||
target := arrayToMap(envs)
|
||||
curr := arrayToMap(os.Environ())
|
||||
|
||||
for nc, vc := range curr {
|
||||
vt, ok := target[nc]
|
||||
if !ok {
|
||||
os.Unsetenv(nc)
|
||||
} else if vc == vt {
|
||||
delete(target, nc)
|
||||
}
|
||||
}
|
||||
|
||||
for nt, vt := range target {
|
||||
os.Setenv(nt, vt)
|
||||
}
|
||||
}
|
||||
|
||||
envLock.Unlock()
|
||||
}
|
||||
|
||||
func getHostAndOsType() (string, string, string) {
|
||||
ht := ""
|
||||
ot := ""
|
||||
st := ""
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
ot = "linux"
|
||||
st = "gnu"
|
||||
switch runtime.GOARCH {
|
||||
case "arm":
|
||||
ht = "arm"
|
||||
case "arm64":
|
||||
ht = "aarch64"
|
||||
case "amd64":
|
||||
ht = "x86_64"
|
||||
case "ppc64le":
|
||||
ht = "powerpc64le"
|
||||
case "riscv64":
|
||||
ht = "riscv64"
|
||||
case "s390x":
|
||||
ht = "s390x"
|
||||
}
|
||||
}
|
||||
return ht, ot, st
|
||||
}
|
||||
356
vendor/github.com/containers/ocicrypt/encryption.go
generated
vendored
Normal file
356
vendor/github.com/containers/ocicrypt/encryption.go
generated
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/ocicrypt/blockcipher"
|
||||
"github.com/containers/ocicrypt/config"
|
||||
keyproviderconfig "github.com/containers/ocicrypt/config/keyprovider-config"
|
||||
"github.com/containers/ocicrypt/keywrap"
|
||||
"github.com/containers/ocicrypt/keywrap/jwe"
|
||||
"github.com/containers/ocicrypt/keywrap/keyprovider"
|
||||
"github.com/containers/ocicrypt/keywrap/pgp"
|
||||
"github.com/containers/ocicrypt/keywrap/pkcs11"
|
||||
"github.com/containers/ocicrypt/keywrap/pkcs7"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// 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())
|
||||
RegisterKeyWrapper("pkcs11", pkcs11.NewKeyWrapper())
|
||||
ic, err := keyproviderconfig.GetConfiguration()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else if ic != nil {
|
||||
for provider, attrs := range ic.KeyProviderConfig {
|
||||
RegisterKeyWrapper("provider."+provider, keyprovider.NewKeyWrapper(provider, attrs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, fmt.Errorf("could not JSON marshal opts: %w", err)
|
||||
}
|
||||
pubOptsData, err = json.Marshal(opts.Public)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not JSON marshal opts: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
newAnnotations := make(map[string]string)
|
||||
keysWrapped := false
|
||||
if len(keyWrapperAnnotations) == 0 {
|
||||
return nil, errors.New("missing Annotations needed for decryption")
|
||||
}
|
||||
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 != "" {
|
||||
keysWrapped = true
|
||||
newAnnotations[annotationsID] = b64Annotations
|
||||
}
|
||||
}
|
||||
|
||||
if !keysWrapped {
|
||||
return nil, errors.New("no wrapped keys produced by encryption")
|
||||
}
|
||||
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
|
||||
errs := ""
|
||||
if len(keyWrapperAnnotations) == 0 {
|
||||
return nil, errors.New("missing Annotations needed for decryption")
|
||||
}
|
||||
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
|
||||
errs += fmt.Sprintf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
if optsData == nil {
|
||||
// try next keywrap.KeyWrapper
|
||||
continue
|
||||
}
|
||||
return optsData, nil
|
||||
}
|
||||
}
|
||||
if !privKeyGiven {
|
||||
return nil, fmt.Errorf("missing private key needed for decryption:\n%s", errs)
|
||||
}
|
||||
return nil, fmt.Errorf("no suitable key unwrapper found or none of the private keys could be used for decryption:\n%s", errs)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
errs := ""
|
||||
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 {
|
||||
errs += fmt.Sprintf("- %s\n", err)
|
||||
continue
|
||||
}
|
||||
return optsData, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no suitable key found for decrypting layer key:\n%s", errs)
|
||||
}
|
||||
|
||||
// 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, "", fmt.Errorf("could not JSON unmarshal privOptsData: %w", err)
|
||||
}
|
||||
|
||||
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, "", fmt.Errorf("could not JSON unmarshal pubOptsData: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
432
vendor/github.com/containers/ocicrypt/gpg.go
generated
vendored
Normal file
432
vendor/github.com/containers/ocicrypt/gpg.go
generated
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// 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, fmt.Errorf("could not create pipe: %w", err)
|
||||
}
|
||||
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 = []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 retrieves 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 retrieves 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 = []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 retrieves 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 retrieves 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 := io.ReadAll(stdout)
|
||||
stderrstr, _ := io.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 (
|
||||
onceRegexp sync.Once
|
||||
emailPattern *regexp.Regexp
|
||||
)
|
||||
|
||||
func extractEmailFromDetails(details []byte) string {
|
||||
onceRegexp.Do(func() {
|
||||
emailPattern = regexp.MustCompile(`uid\s+\[.*\]\s.*\s<(?P<email>.+)>`)
|
||||
})
|
||||
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, fmt.Errorf("could not get KeyWrapper for %s", 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 := term.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, fmt.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
100
vendor/github.com/containers/ocicrypt/gpgvault.go
generated
vendored
Normal 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"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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 fmt.Errorf("could not read keyring: %w", err)
|
||||
}
|
||||
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 := os.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
|
||||
}
|
||||
152
vendor/github.com/containers/ocicrypt/keywrap/jwe/keywrapper_jwe.go
generated
vendored
Normal file
152
vendor/github.com/containers/ocicrypt/keywrap/jwe/keywrapper_jwe.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/ocicrypt/config"
|
||||
"github.com/containers/ocicrypt/keywrap"
|
||||
"github.com/containers/ocicrypt/utils"
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
)
|
||||
|
||||
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, fmt.Errorf("jose.NewMultiEncrypter failed: %w", err)
|
||||
}
|
||||
jwe, err := encrypter.Encrypt(optsData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JWE Encrypt failed: %w", err)
|
||||
}
|
||||
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 := key.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
alg = jose.ECDH_ES_A256KW
|
||||
case *jose.JSONWebKey:
|
||||
if key.Algorithm != "" {
|
||||
alg = jose.KeyAlgorithm(key.Algorithm)
|
||||
switch alg {
|
||||
/* accepted algorithms */
|
||||
case jose.RSA_OAEP:
|
||||
case jose.RSA_OAEP_256:
|
||||
case jose.ECDH_ES_A128KW:
|
||||
case jose.ECDH_ES_A192KW:
|
||||
case jose.ECDH_ES_A256KW:
|
||||
/* all others are rejected */
|
||||
default:
|
||||
return fmt.Errorf("%s is an unsupported JWE key algorithm", alg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*joseRecipients = append(*joseRecipients, jose.Recipient{
|
||||
Algorithm: alg,
|
||||
Key: key,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
244
vendor/github.com/containers/ocicrypt/keywrap/keyprovider/keyprovider.go
generated
vendored
Normal file
244
vendor/github.com/containers/ocicrypt/keywrap/keyprovider/keyprovider.go
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
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 keyprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/ocicrypt/config"
|
||||
keyproviderconfig "github.com/containers/ocicrypt/config/keyprovider-config"
|
||||
"github.com/containers/ocicrypt/keywrap"
|
||||
"github.com/containers/ocicrypt/utils"
|
||||
keyproviderpb "github.com/containers/ocicrypt/utils/keyprovider"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type keyProviderKeyWrapper struct {
|
||||
provider string
|
||||
attrs keyproviderconfig.KeyProviderAttrs
|
||||
}
|
||||
|
||||
func (kw *keyProviderKeyWrapper) GetAnnotationID() string {
|
||||
return "org.opencontainers.image.enc.keys.provider." + kw.provider
|
||||
}
|
||||
|
||||
// NewKeyWrapper returns a new key wrapping interface using keyprovider
|
||||
func NewKeyWrapper(p string, a keyproviderconfig.KeyProviderAttrs) keywrap.KeyWrapper {
|
||||
return &keyProviderKeyWrapper{provider: p, attrs: a}
|
||||
}
|
||||
|
||||
type KeyProviderKeyWrapProtocolOperation string
|
||||
|
||||
var (
|
||||
OpKeyWrap KeyProviderKeyWrapProtocolOperation = "keywrap"
|
||||
OpKeyUnwrap KeyProviderKeyWrapProtocolOperation = "keyunwrap"
|
||||
)
|
||||
|
||||
// KeyProviderKeyWrapProtocolInput defines the input to the key provider binary or grpc method.
|
||||
type KeyProviderKeyWrapProtocolInput struct {
|
||||
// Operation is either "keywrap" or "keyunwrap"
|
||||
Operation KeyProviderKeyWrapProtocolOperation `json:"op"`
|
||||
// KeyWrapParams encodes the arguments to key wrap if operation is set to wrap
|
||||
KeyWrapParams KeyWrapParams `json:"keywrapparams,omitempty"`
|
||||
// KeyUnwrapParams encodes the arguments to key unwrap if operation is set to unwrap
|
||||
KeyUnwrapParams KeyUnwrapParams `json:"keyunwrapparams,omitempty"`
|
||||
}
|
||||
|
||||
// KeyProviderKeyWrapProtocolOutput defines the output of the key provider binary or grpc method.
|
||||
type KeyProviderKeyWrapProtocolOutput struct {
|
||||
// KeyWrapResult encodes the results to key wrap if operation is to wrap
|
||||
KeyWrapResults KeyWrapResults `json:"keywrapresults,omitempty"`
|
||||
// KeyUnwrapResult encodes the result to key unwrap if operation is to unwrap
|
||||
KeyUnwrapResults KeyUnwrapResults `json:"keyunwrapresults,omitempty"`
|
||||
}
|
||||
|
||||
type KeyWrapParams struct {
|
||||
Ec *config.EncryptConfig `json:"ec"`
|
||||
OptsData []byte `json:"optsdata"`
|
||||
}
|
||||
|
||||
type KeyUnwrapParams struct {
|
||||
Dc *config.DecryptConfig `json:"dc"`
|
||||
Annotation []byte `json:"annotation"`
|
||||
}
|
||||
|
||||
type KeyUnwrapResults struct {
|
||||
OptsData []byte `json:"optsdata"`
|
||||
}
|
||||
|
||||
type KeyWrapResults struct {
|
||||
Annotation []byte `json:"annotation"`
|
||||
}
|
||||
|
||||
var runner utils.CommandExecuter
|
||||
|
||||
func init() {
|
||||
runner = utils.Runner{}
|
||||
}
|
||||
|
||||
// WrapKeys calls appropriate binary executable/grpc server for wrapping the session key for recipients and gets encrypted optsData, which
|
||||
// describe the symmetric key used for encrypting the layer
|
||||
func (kw *keyProviderKeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([]byte, error) {
|
||||
|
||||
input, err := json.Marshal(KeyProviderKeyWrapProtocolInput{
|
||||
Operation: OpKeyWrap,
|
||||
KeyWrapParams: KeyWrapParams{
|
||||
Ec: ec,
|
||||
OptsData: optsData,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := ec.Parameters[kw.provider]; ok {
|
||||
if kw.attrs.Command != nil {
|
||||
protocolOuput, err := getProviderCommandOutput(input, kw.attrs.Command)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while retrieving keyprovider protocol command output: %w", err)
|
||||
}
|
||||
return protocolOuput.KeyWrapResults.Annotation, nil
|
||||
} else if kw.attrs.Grpc != "" {
|
||||
protocolOuput, err := getProviderGRPCOutput(input, kw.attrs.Grpc, OpKeyWrap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while retrieving keyprovider protocol grpc output: %w", err)
|
||||
}
|
||||
|
||||
return protocolOuput.KeyWrapResults.Annotation, nil
|
||||
} else {
|
||||
return nil, errors.New("Unsupported keyprovider invocation. Supported invocation methods are grpc and cmd")
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UnwrapKey calls appropriate binary executable/grpc server for unwrapping the session key based on the protocol given in annotation for recipients and gets decrypted optsData,
|
||||
// which describe the symmetric key used for decrypting the layer
|
||||
func (kw *keyProviderKeyWrapper) UnwrapKey(dc *config.DecryptConfig, jsonString []byte) ([]byte, error) {
|
||||
input, err := json.Marshal(KeyProviderKeyWrapProtocolInput{
|
||||
Operation: OpKeyUnwrap,
|
||||
KeyUnwrapParams: KeyUnwrapParams{
|
||||
Dc: dc,
|
||||
Annotation: jsonString,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if kw.attrs.Command != nil {
|
||||
protocolOuput, err := getProviderCommandOutput(input, kw.attrs.Command)
|
||||
if err != nil {
|
||||
// If err is not nil, then ignore it and continue with rest of the given keyproviders
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return protocolOuput.KeyUnwrapResults.OptsData, nil
|
||||
} else if kw.attrs.Grpc != "" {
|
||||
protocolOuput, err := getProviderGRPCOutput(input, kw.attrs.Grpc, OpKeyUnwrap)
|
||||
if err != nil {
|
||||
// If err is not nil, then ignore it and continue with rest of the given keyproviders
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return protocolOuput.KeyUnwrapResults.OptsData, nil
|
||||
} else {
|
||||
return nil, errors.New("Unsupported keyprovider invocation. Supported invocation methods are grpc and cmd")
|
||||
}
|
||||
}
|
||||
|
||||
func getProviderGRPCOutput(input []byte, connString string, operation KeyProviderKeyWrapProtocolOperation) (*KeyProviderKeyWrapProtocolOutput, error) {
|
||||
var protocolOuput KeyProviderKeyWrapProtocolOutput
|
||||
var grpcOutput *keyproviderpb.KeyProviderKeyWrapProtocolOutput
|
||||
cc, err := grpc.Dial(connString, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while dialing rpc server: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
derr := cc.Close()
|
||||
if derr != nil {
|
||||
log.WithError(derr).Error("Error closing grpc socket")
|
||||
}
|
||||
}()
|
||||
|
||||
client := keyproviderpb.NewKeyProviderServiceClient(cc)
|
||||
req := &keyproviderpb.KeyProviderKeyWrapProtocolInput{
|
||||
KeyProviderKeyWrapProtocolInput: input,
|
||||
}
|
||||
|
||||
if operation == OpKeyWrap {
|
||||
grpcOutput, err = client.WrapKey(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error from grpc method: %w", err)
|
||||
}
|
||||
} else if operation == OpKeyUnwrap {
|
||||
grpcOutput, err = client.UnWrapKey(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error from grpc method: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("Unsupported operation")
|
||||
}
|
||||
|
||||
respBytes := grpcOutput.GetKeyProviderKeyWrapProtocolOutput()
|
||||
err = json.Unmarshal(respBytes, &protocolOuput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while unmarshalling grpc method output: %w", err)
|
||||
}
|
||||
|
||||
return &protocolOuput, nil
|
||||
}
|
||||
|
||||
func getProviderCommandOutput(input []byte, command *keyproviderconfig.Command) (*KeyProviderKeyWrapProtocolOutput, error) {
|
||||
var protocolOuput KeyProviderKeyWrapProtocolOutput
|
||||
// Convert interface to command structure
|
||||
respBytes, err := runner.Exec(command.Path, command.Args, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(respBytes, &protocolOuput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while unmarshalling binary executable command output: %w", err)
|
||||
}
|
||||
return &protocolOuput, nil
|
||||
}
|
||||
|
||||
// Return false as it is not applicable to keyprovider protocol
|
||||
func (kw *keyProviderKeyWrapper) NoPossibleKeys(dcparameters map[string][][]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Return nil as it is not applicable to keyprovider protocol
|
||||
func (kw *keyProviderKeyWrapper) GetPrivateKeys(dcparameters map[string][][]byte) [][]byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return nil as it is not applicable to keyprovider protocol
|
||||
func (kw *keyProviderKeyWrapper) GetKeyIdsFromPacket(_ string) ([]uint64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Return nil as it is not applicable to keyprovider protocol
|
||||
func (kw *keyProviderKeyWrapper) GetRecipients(_ string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
48
vendor/github.com/containers/ocicrypt/keywrap/keywrap.go
generated
vendored
Normal file
48
vendor/github.com/containers/ocicrypt/keywrap/keywrap.go
generated
vendored
Normal 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)
|
||||
}
|
||||
272
vendor/github.com/containers/ocicrypt/keywrap/pgp/keywrapper_gpg.go
generated
vendored
Normal file
272
vendor/github.com/containers/ocicrypt/keywrap/pgp/keywrapper_gpg.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/mail"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/ocicrypt/config"
|
||||
"github.com/containers/ocicrypt/keywrap"
|
||||
"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, fmt.Errorf("unable to create entity list: %w", err)
|
||||
}
|
||||
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, fmt.Errorf("unable to parse private keys: %w", err)
|
||||
}
|
||||
|
||||
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 := io.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, fmt.Errorf("could not decode base64 encoded PGP packet: %w", err)
|
||||
}
|
||||
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{}, fmt.Errorf("packets.Next() failed: %w", err)
|
||||
}
|
||||
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
|
||||
}
|
||||
152
vendor/github.com/containers/ocicrypt/keywrap/pkcs11/keywrapper_pkcs11.go
generated
vendored
Normal file
152
vendor/github.com/containers/ocicrypt/keywrap/pkcs11/keywrapper_pkcs11.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
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 pkcs11
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/ocicrypt/config"
|
||||
"github.com/containers/ocicrypt/crypto/pkcs11"
|
||||
"github.com/containers/ocicrypt/keywrap"
|
||||
"github.com/containers/ocicrypt/utils"
|
||||
)
|
||||
|
||||
type pkcs11KeyWrapper struct {
|
||||
}
|
||||
|
||||
func (kw *pkcs11KeyWrapper) GetAnnotationID() string {
|
||||
return "org.opencontainers.image.enc.keys.pkcs11"
|
||||
}
|
||||
|
||||
// NewKeyWrapper returns a new key wrapping interface using pkcs11
|
||||
func NewKeyWrapper() keywrap.KeyWrapper {
|
||||
return &pkcs11KeyWrapper{}
|
||||
}
|
||||
|
||||
// WrapKeys wraps the session key for recpients and encrypts the optsData, which
|
||||
// describe the symmetric key used for encrypting the layer
|
||||
func (kw *pkcs11KeyWrapper) WrapKeys(ec *config.EncryptConfig, optsData []byte) ([]byte, error) {
|
||||
// append({}, ...) allocates a fresh backing array, and that's necessary to guarantee concurrent calls to WrapKeys (as in c/image/copy.Image)
|
||||
// can't race writing to the same backing array.
|
||||
pubKeys := append([][]byte{}, ec.Parameters["pkcs11-pubkeys"]...) // In Go 1.21, slices.Clone(ec.Parameters["pkcs11-pubkeys"])
|
||||
pubKeys = append(pubKeys, ec.Parameters["pkcs11-yamls"]...)
|
||||
pkcs11Recipients, err := addPubKeys(&ec.DecryptConfig, pubKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// no recipients is not an error...
|
||||
if len(pkcs11Recipients) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
jsonString, err := pkcs11.EncryptMultiple(pkcs11Recipients, optsData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("PKCS11 EncryptMulitple failed: %w", err)
|
||||
}
|
||||
return jsonString, nil
|
||||
}
|
||||
|
||||
func (kw *pkcs11KeyWrapper) UnwrapKey(dc *config.DecryptConfig, jsonString []byte) ([]byte, error) {
|
||||
var pkcs11PrivKeys []*pkcs11.Pkcs11KeyFileObject
|
||||
|
||||
privKeys := kw.GetPrivateKeys(dc.Parameters)
|
||||
if len(privKeys) == 0 {
|
||||
return nil, errors.New("No private keys found for PKCS11 decryption")
|
||||
}
|
||||
|
||||
p11conf, err := p11confFromParameters(dc.Parameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, privKey := range privKeys {
|
||||
key, err := utils.ParsePrivateKey(privKey, nil, "PKCS11")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch pkcs11PrivKey := key.(type) {
|
||||
case *pkcs11.Pkcs11KeyFileObject:
|
||||
if p11conf != nil {
|
||||
pkcs11PrivKey.Uri.SetModuleDirectories(p11conf.ModuleDirectories)
|
||||
pkcs11PrivKey.Uri.SetAllowedModulePaths(p11conf.AllowedModulePaths)
|
||||
}
|
||||
pkcs11PrivKeys = append(pkcs11PrivKeys, pkcs11PrivKey)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
plaintext, err := pkcs11.Decrypt(pkcs11PrivKeys, jsonString)
|
||||
if err == nil {
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("PKCS11: No suitable private key found for decryption: %w", err)
|
||||
}
|
||||
|
||||
func (kw *pkcs11KeyWrapper) NoPossibleKeys(dcparameters map[string][][]byte) bool {
|
||||
return len(kw.GetPrivateKeys(dcparameters)) == 0
|
||||
}
|
||||
|
||||
func (kw *pkcs11KeyWrapper) GetPrivateKeys(dcparameters map[string][][]byte) [][]byte {
|
||||
return dcparameters["pkcs11-yamls"]
|
||||
}
|
||||
|
||||
func (kw *pkcs11KeyWrapper) GetKeyIdsFromPacket(_ string) ([]uint64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (kw *pkcs11KeyWrapper) GetRecipients(_ string) ([]string, error) {
|
||||
return []string{"[pkcs11]"}, nil
|
||||
}
|
||||
|
||||
func addPubKeys(dc *config.DecryptConfig, pubKeys [][]byte) ([]interface{}, error) {
|
||||
var pkcs11Keys []interface{}
|
||||
|
||||
if len(pubKeys) == 0 {
|
||||
return pkcs11Keys, nil
|
||||
}
|
||||
|
||||
p11conf, err := p11confFromParameters(dc.Parameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, pubKey := range pubKeys {
|
||||
key, err := utils.ParsePublicKey(pubKey, "PKCS11")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch pkcs11PubKey := key.(type) {
|
||||
case *pkcs11.Pkcs11KeyFileObject:
|
||||
if p11conf != nil {
|
||||
pkcs11PubKey.Uri.SetModuleDirectories(p11conf.ModuleDirectories)
|
||||
pkcs11PubKey.Uri.SetAllowedModulePaths(p11conf.AllowedModulePaths)
|
||||
}
|
||||
}
|
||||
pkcs11Keys = append(pkcs11Keys, key)
|
||||
}
|
||||
return pkcs11Keys, nil
|
||||
}
|
||||
|
||||
func p11confFromParameters(dcparameters map[string][][]byte) (*pkcs11.Pkcs11Config, error) {
|
||||
if _, ok := dcparameters["pkcs11-config"]; ok {
|
||||
return pkcs11.ParsePkcs11ConfigFile(dcparameters["pkcs11-config"][0])
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
137
vendor/github.com/containers/ocicrypt/keywrap/pkcs7/keywrapper_pkcs7.go
generated
vendored
Normal file
137
vendor/github.com/containers/ocicrypt/keywrap/pkcs7/keywrapper_pkcs7.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/ocicrypt/config"
|
||||
"github.com/containers/ocicrypt/keywrap"
|
||||
"github.com/containers/ocicrypt/utils"
|
||||
"go.mozilla.org/pkcs7"
|
||||
)
|
||||
|
||||
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, fmt.Errorf("could not parse PKCS7 packet: %w", err)
|
||||
}
|
||||
|
||||
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
40
vendor/github.com/containers/ocicrypt/reader.go
generated
vendored
Normal 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
|
||||
}
|
||||
20
vendor/github.com/containers/ocicrypt/spec/spec.go
generated
vendored
Normal file
20
vendor/github.com/containers/ocicrypt/spec/spec.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
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 gzip-compressed layers.
|
||||
MediaTypeLayerGzipEnc = "application/vnd.oci.image.layer.v1.tar+gzip+encrypted"
|
||||
// MediaTypeLayerZstdEnc is MIME type used for encrypted zstd-compressed layers.
|
||||
MediaTypeLayerZstdEnc = "application/vnd.oci.image.layer.v1.tar+zstd+encrypted"
|
||||
// MediaTypeLayerNonDistributableEnc is MIME type used for non distributable encrypted layers.
|
||||
MediaTypeLayerNonDistributableEnc = "application/vnd.oci.image.layer.nondistributable.v1.tar+encrypted"
|
||||
// MediaTypeLayerNonDistributableGzipEnc is MIME type used for non distributable encrypted gzip-compressed layers.
|
||||
MediaTypeLayerNonDistributableGzipEnc = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip+encrypted"
|
||||
// MediaTypeLayerNonDistributableZstdEnc is MIME type used for non distributable encrypted zstd-compressed layers.
|
||||
MediaTypeLayerNonDistributableZstdEnc = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd+encrypted"
|
||||
// MediaTypeLayerNonDistributableZsdtEnc is MIME type used for non distributable encrypted zstd-compressed layers.
|
||||
//
|
||||
// Deprecated: Use [MediaTypeLayerNonDistributableZstdEnc].
|
||||
MediaTypeLayerNonDistributableZsdtEnc = MediaTypeLayerNonDistributableZstdEnc
|
||||
)
|
||||
109
vendor/github.com/containers/ocicrypt/utils/delayedreader.go
generated
vendored
Normal file
109
vendor/github.com/containers/ocicrypt/utils/delayedreader.go
generated
vendored
Normal 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
|
||||
}
|
||||
58
vendor/github.com/containers/ocicrypt/utils/ioutils.go
generated
vendored
Normal file
58
vendor/github.com/containers/ocicrypt/utils/ioutils.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// first argument is the command, like cat or echo,
|
||||
// the second is the list of args to pass to it
|
||||
type CommandExecuter interface {
|
||||
Exec(string, []string, []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type Runner struct{}
|
||||
|
||||
// ExecuteCommand is used to execute a linux command line command and return the output of the command with an error if it exists.
|
||||
func (r Runner) Exec(cmdName string, args []string, input []byte) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
stdInputBuffer := bytes.NewBuffer(input)
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
cmd.Stdin = stdInputBuffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while running command: %s. stderr: %s: %w", cmdName, stderr.String(), err)
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
243
vendor/github.com/containers/ocicrypt/utils/keyprovider/keyprovider.pb.go
generated
vendored
Normal file
243
vendor/github.com/containers/ocicrypt/utils/keyprovider/keyprovider.pb.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: keyprovider.proto
|
||||
|
||||
package keyprovider
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type KeyProviderKeyWrapProtocolInput struct {
|
||||
KeyProviderKeyWrapProtocolInput []byte `protobuf:"bytes,1,opt,name=KeyProviderKeyWrapProtocolInput,proto3" json:"KeyProviderKeyWrapProtocolInput,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *KeyProviderKeyWrapProtocolInput) Reset() { *m = KeyProviderKeyWrapProtocolInput{} }
|
||||
func (m *KeyProviderKeyWrapProtocolInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*KeyProviderKeyWrapProtocolInput) ProtoMessage() {}
|
||||
func (*KeyProviderKeyWrapProtocolInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_da74c8e785ad390c, []int{0}
|
||||
}
|
||||
|
||||
func (m *KeyProviderKeyWrapProtocolInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Merge(m, src)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolInput) XXX_Size() int {
|
||||
return xxx_messageInfo_KeyProviderKeyWrapProtocolInput.Size(m)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_KeyProviderKeyWrapProtocolInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_KeyProviderKeyWrapProtocolInput proto.InternalMessageInfo
|
||||
|
||||
func (m *KeyProviderKeyWrapProtocolInput) GetKeyProviderKeyWrapProtocolInput() []byte {
|
||||
if m != nil {
|
||||
return m.KeyProviderKeyWrapProtocolInput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type KeyProviderKeyWrapProtocolOutput struct {
|
||||
KeyProviderKeyWrapProtocolOutput []byte `protobuf:"bytes,1,opt,name=KeyProviderKeyWrapProtocolOutput,proto3" json:"KeyProviderKeyWrapProtocolOutput,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) Reset() { *m = KeyProviderKeyWrapProtocolOutput{} }
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) String() string { return proto.CompactTextString(m) }
|
||||
func (*KeyProviderKeyWrapProtocolOutput) ProtoMessage() {}
|
||||
func (*KeyProviderKeyWrapProtocolOutput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_da74c8e785ad390c, []int{1}
|
||||
}
|
||||
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Merge(m, src)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) XXX_Size() int {
|
||||
return xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.Size(m)
|
||||
}
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_KeyProviderKeyWrapProtocolOutput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_KeyProviderKeyWrapProtocolOutput proto.InternalMessageInfo
|
||||
|
||||
func (m *KeyProviderKeyWrapProtocolOutput) GetKeyProviderKeyWrapProtocolOutput() []byte {
|
||||
if m != nil {
|
||||
return m.KeyProviderKeyWrapProtocolOutput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*KeyProviderKeyWrapProtocolInput)(nil), "keyprovider.keyProviderKeyWrapProtocolInput")
|
||||
proto.RegisterType((*KeyProviderKeyWrapProtocolOutput)(nil), "keyprovider.keyProviderKeyWrapProtocolOutput")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("keyprovider.proto", fileDescriptor_da74c8e785ad390c)
|
||||
}
|
||||
|
||||
var fileDescriptor_da74c8e785ad390c = []byte{
|
||||
// 169 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xcc, 0x4e, 0xad, 0x2c,
|
||||
0x28, 0xca, 0x2f, 0xcb, 0x4c, 0x49, 0x2d, 0xd2, 0x03, 0x32, 0x4a, 0xf2, 0x85, 0xb8, 0x91, 0x84,
|
||||
0x94, 0xb2, 0xb9, 0xe4, 0x81, 0xdc, 0x00, 0x28, 0xd7, 0x3b, 0xb5, 0x32, 0xbc, 0x28, 0xb1, 0x20,
|
||||
0x00, 0xa4, 0x2e, 0x39, 0x3f, 0xc7, 0x33, 0xaf, 0xa0, 0xb4, 0x44, 0xc8, 0x83, 0x4b, 0xde, 0x1b,
|
||||
0xbf, 0x12, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x42, 0xca, 0x94, 0xf2, 0xb8, 0x14, 0x70,
|
||||
0x5b, 0xe6, 0x5f, 0x5a, 0x02, 0xb2, 0xcd, 0x8b, 0x4b, 0xc1, 0x9b, 0x80, 0x1a, 0xa8, 0x75, 0x04,
|
||||
0xd5, 0x19, 0xbd, 0x62, 0xe4, 0x12, 0x42, 0x52, 0x14, 0x9c, 0x5a, 0x54, 0x96, 0x99, 0x9c, 0x2a,
|
||||
0x94, 0xc1, 0xc5, 0x0e, 0x52, 0x0c, 0x94, 0x11, 0xd2, 0xd1, 0x43, 0x0e, 0x1f, 0x02, 0x21, 0x21,
|
||||
0xa5, 0x4b, 0xa4, 0x6a, 0x88, 0xf5, 0x4a, 0x0c, 0x42, 0x59, 0x5c, 0x9c, 0xa1, 0x79, 0xf4, 0xb1,
|
||||
0xcb, 0x89, 0x37, 0x0a, 0x39, 0x62, 0x93, 0xd8, 0xc0, 0x91, 0x6d, 0x0c, 0x08, 0x00, 0x00, 0xff,
|
||||
0xff, 0x9a, 0x10, 0xcb, 0xf9, 0x01, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// KeyProviderServiceClient is the client API for KeyProviderService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type KeyProviderServiceClient interface {
|
||||
WrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error)
|
||||
UnWrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error)
|
||||
}
|
||||
|
||||
type keyProviderServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewKeyProviderServiceClient(cc grpc.ClientConnInterface) KeyProviderServiceClient {
|
||||
return &keyProviderServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *keyProviderServiceClient) WrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error) {
|
||||
out := new(KeyProviderKeyWrapProtocolOutput)
|
||||
err := c.cc.Invoke(ctx, "/keyprovider.KeyProviderService/WrapKey", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *keyProviderServiceClient) UnWrapKey(ctx context.Context, in *KeyProviderKeyWrapProtocolInput, opts ...grpc.CallOption) (*KeyProviderKeyWrapProtocolOutput, error) {
|
||||
out := new(KeyProviderKeyWrapProtocolOutput)
|
||||
err := c.cc.Invoke(ctx, "/keyprovider.KeyProviderService/UnWrapKey", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// KeyProviderServiceServer is the server API for KeyProviderService service.
|
||||
type KeyProviderServiceServer interface {
|
||||
WrapKey(context.Context, *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error)
|
||||
UnWrapKey(context.Context, *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error)
|
||||
}
|
||||
|
||||
// UnimplementedKeyProviderServiceServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedKeyProviderServiceServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedKeyProviderServiceServer) WrapKey(ctx context.Context, req *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method WrapKey not implemented")
|
||||
}
|
||||
func (*UnimplementedKeyProviderServiceServer) UnWrapKey(ctx context.Context, req *KeyProviderKeyWrapProtocolInput) (*KeyProviderKeyWrapProtocolOutput, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UnWrapKey not implemented")
|
||||
}
|
||||
|
||||
func RegisterKeyProviderServiceServer(s *grpc.Server, srv KeyProviderServiceServer) {
|
||||
s.RegisterService(&_KeyProviderService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _KeyProviderService_WrapKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(KeyProviderKeyWrapProtocolInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KeyProviderServiceServer).WrapKey(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/keyprovider.KeyProviderService/WrapKey",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KeyProviderServiceServer).WrapKey(ctx, req.(*KeyProviderKeyWrapProtocolInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _KeyProviderService_UnWrapKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(KeyProviderKeyWrapProtocolInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KeyProviderServiceServer).UnWrapKey(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/keyprovider.KeyProviderService/UnWrapKey",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KeyProviderServiceServer).UnWrapKey(ctx, req.(*KeyProviderKeyWrapProtocolInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _KeyProviderService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "keyprovider.KeyProviderService",
|
||||
HandlerType: (*KeyProviderServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "WrapKey",
|
||||
Handler: _KeyProviderService_WrapKey_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UnWrapKey",
|
||||
Handler: _KeyProviderService_UnWrapKey_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "keyprovider.proto",
|
||||
}
|
||||
17
vendor/github.com/containers/ocicrypt/utils/keyprovider/keyprovider.proto
generated
vendored
Normal file
17
vendor/github.com/containers/ocicrypt/utils/keyprovider/keyprovider.proto
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package keyprovider;
|
||||
option go_package = "keyprovider";
|
||||
|
||||
message keyProviderKeyWrapProtocolInput {
|
||||
bytes KeyProviderKeyWrapProtocolInput = 1;
|
||||
}
|
||||
|
||||
message keyProviderKeyWrapProtocolOutput {
|
||||
bytes KeyProviderKeyWrapProtocolOutput = 1;
|
||||
}
|
||||
|
||||
service KeyProviderService {
|
||||
rpc WrapKey(keyProviderKeyWrapProtocolInput) returns (keyProviderKeyWrapProtocolOutput) {};
|
||||
rpc UnWrapKey(keyProviderKeyWrapProtocolInput) returns (keyProviderKeyWrapProtocolOutput) {};
|
||||
}
|
||||
174
vendor/github.com/containers/ocicrypt/utils/testing.go
generated
vendored
Normal file
174
vendor/github.com/containers/ocicrypt/utils/testing.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
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"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CreateRSAKey creates an RSA key
|
||||
func CreateRSAKey(bits int) (*rsa.PrivateKey, error) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rsa.GenerateKey failed: %w", err)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// CreateECDSAKey creates an elliptic curve key for the given curve
|
||||
func CreateECDSAKey(curve elliptic.Curve) (*ecdsa.PrivateKey, error) {
|
||||
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ecdsa.GenerateKey failed: %w", err)
|
||||
}
|
||||
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, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err)
|
||||
}
|
||||
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) //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("x509.EncryptPEMBlock failed: %w", err)
|
||||
}
|
||||
} 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 := CreateECDSAKey(curve)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err)
|
||||
}
|
||||
|
||||
privData, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("x509.MarshalECPrivateKey failed: %w", err)
|
||||
}
|
||||
|
||||
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, fmt.Errorf("rsa.GenerateKey failed: %w", err)
|
||||
}
|
||||
|
||||
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, fmt.Errorf("x509.CreateCertificate failed: %w", err)
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("x509.ParseCertificate failed: %w", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
249
vendor/github.com/containers/ocicrypt/utils/utils.go
generated
vendored
Normal file
249
vendor/github.com/containers/ocicrypt/utils/utils.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/ocicrypt/crypto/pkcs11"
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
)
|
||||
|
||||
// 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 := jose.JSONWebKey{}
|
||||
err := jwk.UnmarshalJSON(privKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: Could not parse input as JWK: %w", prefix, err)
|
||||
}
|
||||
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 := jose.JSONWebKey{}
|
||||
err := jwk.UnmarshalJSON(privKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: Could not parse input as JWK: %w", prefix, err)
|
||||
}
|
||||
if !jwk.IsPublic() {
|
||||
return nil, fmt.Errorf("%s: JWK is not a public key", prefix)
|
||||
}
|
||||
return &jwk, nil
|
||||
}
|
||||
|
||||
// parsePkcs11PrivateKeyYaml parses the input byte array as pkcs11 key file yaml format)
|
||||
func parsePkcs11PrivateKeyYaml(yaml []byte, prefix string) (*pkcs11.Pkcs11KeyFileObject, error) {
|
||||
// if the URI does not have enough attributes, we will throw an error when decrypting
|
||||
return pkcs11.ParsePkcs11KeyFile(yaml)
|
||||
}
|
||||
|
||||
// parsePkcs11URIPublicKey parses the input byte array as a pkcs11 key file yaml
|
||||
func parsePkcs11PublicKeyYaml(yaml []byte, prefix string) (*pkcs11.Pkcs11KeyFileObject, error) {
|
||||
// if the URI does not have enough attributes, we will throw an error when decrypting
|
||||
return pkcs11.ParsePkcs11KeyFile(yaml)
|
||||
}
|
||||
|
||||
// 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) { //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility
|
||||
if privKeyPassword == nil {
|
||||
return nil, fmt.Errorf("%s: Missing password for encrypted private key", prefix)
|
||||
}
|
||||
der, err = x509.DecryptPEMBlock(block, privKeyPassword) //nolint:staticcheck // ignore SA1019, which is kept for backward compatibility
|
||||
if err != nil {
|
||||
return nil, fmt.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, fmt.Errorf("%s: Could not parse private key: %w", prefix, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
key, err = parseJWKPrivateKey(privKey, prefix)
|
||||
if err != nil {
|
||||
key, err = parsePkcs11PrivateKeyYaml(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
|
||||
}
|
||||
|
||||
// IsPkcs11PrivateKey returns true in case the given byte array represents a pkcs11 private key
|
||||
func IsPkcs11PrivateKey(data []byte) bool {
|
||||
return pkcs11.IsPkcs11PrivateKey(data)
|
||||
}
|
||||
|
||||
// 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, fmt.Errorf("%s: Could not parse public key: %w", prefix, err)
|
||||
}
|
||||
} else {
|
||||
key, err = parseJWKPublicKey(pubKey, prefix)
|
||||
if err != nil {
|
||||
key, err = parsePkcs11PublicKeyYaml(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
|
||||
}
|
||||
|
||||
// IsPkcs11PublicKey returns true in case the given byte array represents a pkcs11 public key
|
||||
func IsPkcs11PublicKey(data []byte) bool {
|
||||
return pkcs11.IsPkcs11PublicKey(data)
|
||||
}
|
||||
|
||||
// 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, fmt.Errorf("%s: Could not parse x509 certificate: %w", prefix, err)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user