Add Dapr crypto library to dapr/kit (#38)

Signed-off-by: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com>
This commit is contained in:
Alessandro (Ale) Segala 2023-03-07 11:22:05 -08:00 committed by GitHub
parent fcb09958bf
commit 39814af4c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 3909 additions and 16 deletions

View File

@ -21,11 +21,11 @@ jobs:
name: Build ${{ matrix.target_os }}_${{ matrix.target_arch }} binaries
runs-on: ${{ matrix.os }}
env:
GOVER: "1.19"
GOVER: "1.20"
GOOS: ${{ matrix.target_os }}
GOARCH: ${{ matrix.target_arch }}
GOPROXY: https://proxy.golang.org
GOLANGCI_LINT_VER: v1.50.1
GOLANGCI_LINT_VER: v1.51.2
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]

View File

@ -156,6 +156,8 @@ linters-settings:
nakedret:
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
max-func-lines: 30
nolintlint:
allow-unused: true
prealloc:
# XXX: we don't recommend using this linter before doing performance profiling.
# For most programs usage of prealloc will be a premature optimization.
@ -229,12 +231,17 @@ linters:
disable:
# TODO Enforce the below linters later
- dupl
- nonamedreturns
- errcheck
- funlen
- gochecknoglobals
- gochecknoinits
- gocyclo
- gocognit
- nosnakecase
- varcheck
- structcheck
- deadcode
- godox
- interfacer
- lll
@ -247,7 +254,6 @@ linters:
- goerr113
- nestif
- nlreturn
- exhaustive
- noctx
- gci
- golint
@ -256,7 +262,9 @@ linters:
- wrapcheck
- tagliatelle
- ireturn
- exhaustive
- exhaustivestruct
- exhaustruct
- errchkjson
- contextcheck
- gomoddirectives

130
crypto/aeskw/keywrap.go Normal file
View File

@ -0,0 +1,130 @@
/*
Copyright 2022 The Dapr 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 provides an AES-KW keywrap implementation as defined in RFC-3394.
package aeskw
/*!
Adapted from https://github.com/NickBall/go-aes-key-wrap/tree/1c3aa3e4dfc5b00bec9983bd1de6a71b3d52cd6d
Copyright (c) 2017 Nick Ball
License: MIT (https://github.com/NickBall/go-aes-key-wrap/blob/1c3aa3e4dfc5b00bec9983bd1de6a71b3d52cd6d/LICENSE)
*/
import (
"crypto/cipher"
"crypto/subtle"
"encoding/binary"
"errors"
)
// defaultIV as specified in RFC-3394
var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}
// Wrap encrypts the provided key data (cek) with the given AES cipher (and corresponding key), using the AES Key Wrap algorithm (RFC-3394)
func Wrap(block cipher.Block, cek []byte) ([]byte, error) {
if len(cek)%8 != 0 {
return nil, errors.New("cek must be in 8-byte blocks")
}
// Initialize variables
a := make([]byte, 8)
copy(a, defaultIV)
n := len(cek) / 8
// Calculate intermediate
r := make([][]byte, n)
for i := range r {
r[i] = make([]byte, 8)
copy(r[i], cek[i*8:])
}
for j := 0; j <= 5; j++ {
for i := 1; i <= n; i++ {
b := arrConcat(a, r[i-1])
block.Encrypt(b, b)
t := (n * j) + i
tBytes := make([]byte, 8)
binary.BigEndian.PutUint64(tBytes, uint64(t))
copy(a, arrXor(b[:len(b)/2], tBytes))
copy(r[i-1], b[len(b)/2:])
}
}
// Output
c := make([]byte, (n+1)*8)
copy(c, a)
for i := 1; i <= n; i++ {
for j := range r[i-1] {
c[(i*8)+j] = r[i-1][j]
}
}
return c, nil
}
// Unwrap decrypts the provided cipher text with the given AES cipher (and corresponding key), using the AES Key Wrap algorithm (RFC-3394).
// The decrypted cipher text is verified using the default IV and will return an error if validation fails.
func Unwrap(block cipher.Block, cipherText []byte) ([]byte, error) {
// Initialize variables
a := make([]byte, 8)
n := (len(cipherText) / 8) - 1
r := make([][]byte, n)
for i := range r {
r[i] = make([]byte, 8)
copy(r[i], cipherText[(i+1)*8:])
}
copy(a, cipherText[:8])
// Compute intermediate values
for j := 5; j >= 0; j-- {
for i := n; i >= 1; i-- {
t := (n * j) + i
tBytes := make([]byte, 8)
binary.BigEndian.PutUint64(tBytes, uint64(t))
b := arrConcat(arrXor(a, tBytes), r[i-1])
block.Decrypt(b, b)
copy(a, b[:len(b)/2])
copy(r[i-1], b[len(b)/2:])
}
}
if subtle.ConstantTimeCompare(a, defaultIV) != 1 {
return nil, errors.New("integrity check failed - unexpected IV")
}
// Output
c := arrConcat(r...)
return c, nil
}
func arrConcat(arrays ...[]byte) []byte {
out := make([]byte, len(arrays[0]))
copy(out, arrays[0])
for _, array := range arrays[1:] {
out = append(out, array...) //nolint:makezero
}
return out
}
func arrXor(arrL []byte, arrR []byte) []byte {
out := make([]byte, len(arrL))
for x := range arrL {
out[x] = arrL[x] ^ arrR[x]
}
return out
}

View File

@ -0,0 +1,116 @@
/*
Copyright 2022 The Dapr 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 aeskw
/*!
Adapted from https://github.com/NickBall/go-aes-key-wrap/tree/1c3aa3e4dfc5b00bec9983bd1de6a71b3d52cd6d
Copyright (c) 2017 Nick Ball
License: MIT (https://github.com/NickBall/go-aes-key-wrap/blob/1c3aa3e4dfc5b00bec9983bd1de6a71b3d52cd6d/LICENSE)
*/
import (
"crypto/aes"
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
)
type input struct {
Case string
Kek string
Data string
Expected string
}
func TestWrapRfc3394Vectors(t *testing.T) {
vectors := []input{
{
Case: "4.1 Wrap 128 bits of Key Data with a 128-bit KEK",
Kek: "000102030405060708090A0B0C0D0E0F",
Data: "00112233445566778899AABBCCDDEEFF",
Expected: "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5",
},
{
Case: "4.2 Wrap 128 bits of Key Data with a 192-bit KEK",
Kek: "000102030405060708090A0B0C0D0E0F1011121314151617",
Data: "00112233445566778899AABBCCDDEEFF",
Expected: "96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D",
},
{
Case: "4.3 Wrap 128 bits of Key Data with a 256-bit KEK",
Kek: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
Data: "00112233445566778899AABBCCDDEEFF",
Expected: "64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7",
},
{
Case: "4.4 Wrap 192 bits of Key Data with a 192-bit KEK",
Kek: "000102030405060708090A0B0C0D0E0F1011121314151617",
Data: "00112233445566778899AABBCCDDEEFF0001020304050607",
Expected: "031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2",
},
{
Case: "4.5 Wrap 192 bits of Key Data with a 256-bit KEK",
Kek: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
Data: "00112233445566778899AABBCCDDEEFF0001020304050607",
Expected: "A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1",
},
{
Case: "4.6 Wrap 256 bits of Key Data with a 256-bit KEK",
Kek: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
Data: "00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F",
Expected: "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21",
},
}
for _, v := range vectors {
t.Log("Testcase", "\t", v.Case)
t.Log("kek", "\t = ", v.Kek)
t.Log("data", "\t = ", v.Data)
t.Log("exp", "\t = ", v.Expected)
kek := mustHexDecode(v.Kek)
data := mustHexDecode(v.Data)
exp := mustHexDecode(v.Expected)
cipher, err := aes.NewCipher(kek)
if !assert.NoError(t, err, "NewCipher should not fail!") {
continue
}
actual, err := Wrap(cipher, data)
if !assert.NoError(t, err, "Wrap should not throw error with valid input") {
continue
}
if !assert.Equal(t, exp, actual, "Wrap Mismatch: Actual wrapped ciphertext should equal expected for test case '%s'", v.Case) {
continue
}
actualUnwrapped, err := Unwrap(cipher, actual)
if !assert.NoError(t, err, "Unwrap should not throw error with valid input") {
continue
}
if !assert.Equal(t, data, actualUnwrapped, "Unwrap Mismatch: Actual unwrapped ciphertext should equal the original data for test case '%s'", v.Case) {
continue
}
}
}
func mustHexDecode(s string) (b []byte) {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}

97
crypto/asymmetric_enc.go Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"github.com/lestrrat-go/jwx/v2/jwk"
)
// EncryptPublicKey encrypts a message using a public key and the specified algorithm.
// Note that "associatedData" is ignored if the cipher does not support labels/AAD.
func EncryptPublicKey(plaintext []byte, algorithm string, key jwk.Key, associatedData []byte) (ciphertext []byte, err error) {
// Ensure we are using a public key
key, err = key.PublicKey()
if err != nil {
return nil, ErrKeyTypeMismatch
}
switch algorithm {
case Algorithm_RSA1_5:
return encryptPublicKeyRSAPKCS1v15(plaintext, key)
case Algorithm_RSA_OAEP:
return encryptPublicKeyRSAOAEP(plaintext, key, crypto.SHA1, associatedData)
case Algorithm_RSA_OAEP_256, Algorithm_RSA_OAEP_384, Algorithm_RSA_OAEP_512:
return encryptPublicKeyRSAOAEP(plaintext, key, getSHAHash(algorithm), associatedData)
default:
return nil, ErrUnsupportedAlgorithm
}
}
func encryptPublicKeyRSAPKCS1v15(plaintext []byte, key jwk.Key) ([]byte, error) {
rsaKey := &rsa.PublicKey{}
if key.Raw(rsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
return rsa.EncryptPKCS1v15(rand.Reader, rsaKey, plaintext)
}
func encryptPublicKeyRSAOAEP(plaintext []byte, key jwk.Key, hash crypto.Hash, label []byte) ([]byte, error) {
rsaKey := &rsa.PublicKey{}
if key.Raw(rsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
return rsa.EncryptOAEP(hash.New(), rand.Reader, rsaKey, plaintext, label)
}
// DecryptPrivateKey decrypts a message using a private key and the specified algorithm.
// Note that "associatedData" is ignored if the cipher does not support labels/AAD.
func DecryptPrivateKey(ciphertext []byte, algorithm string, key jwk.Key, associatedData []byte) (plaintext []byte, err error) {
switch algorithm {
case Algorithm_RSA1_5:
return decryptPrivateKeyRSAPKCS1v15(ciphertext, key)
case Algorithm_RSA_OAEP:
return decryptPrivateKeyRSAOAEP(ciphertext, key, crypto.SHA1, associatedData)
case Algorithm_RSA_OAEP_256, Algorithm_RSA_OAEP_384, Algorithm_RSA_OAEP_512:
return decryptPrivateKeyRSAOAEP(ciphertext, key, getSHAHash(algorithm), associatedData)
default:
return nil, ErrUnsupportedAlgorithm
}
}
func decryptPrivateKeyRSAPKCS1v15(ciphertext []byte, key jwk.Key) ([]byte, error) {
rsaKey := &rsa.PrivateKey{}
if key.Raw(rsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
return rsa.DecryptPKCS1v15(rand.Reader, rsaKey, ciphertext)
}
func decryptPrivateKeyRSAOAEP(ciphertext []byte, key jwk.Key, hash crypto.Hash, label []byte) ([]byte, error) {
rsaKey := &rsa.PrivateKey{}
if key.Raw(rsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
return rsa.DecryptOAEP(hash.New(), rand.Reader, rsaKey, ciphertext, label)
}

View File

@ -0,0 +1,76 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEncryptionRSAPKCS1v15(t *testing.T) {
const message = "❤️ 45.5096759, 11.4108900"
key, err := ParseKey([]byte(privateKeyRSAPKCS8), "application/x-pem-file")
require.NoError(t, err)
require.NotNil(t, key)
var ciphertext []byte
t.Run("encrypt", func(t *testing.T) {
ciphertext, err = EncryptPublicKey([]byte(message), Algorithm_RSA1_5, key, nil)
require.NoError(t, err)
require.NotNil(t, ciphertext)
})
t.Run("decrypt", func(t *testing.T) {
plaintext, err := DecryptPrivateKey(ciphertext, Algorithm_RSA1_5, key, nil)
require.NoError(t, err)
require.NotNil(t, plaintext)
require.Equal(t, message, string(plaintext))
})
}
func TestEncryptionRSAOAEP(t *testing.T) {
const message = "❤️ 45.5096759, 11.4108900"
key, err := ParseKey([]byte(privateKeyRSAPKCS8), "application/x-pem-file")
require.NoError(t, err)
require.NotNil(t, key)
algs := []string{
Algorithm_RSA_OAEP,
Algorithm_RSA_OAEP_256,
Algorithm_RSA_OAEP_384,
Algorithm_RSA_OAEP_512,
}
for _, alg := range algs {
t.Run(alg, func(t *testing.T) {
var ciphertext []byte
t.Run("encrypt", func(t *testing.T) {
ciphertext, err = EncryptPublicKey([]byte(message), alg, key, nil)
require.NoError(t, err)
require.NotNil(t, ciphertext)
})
t.Run("decrypt", func(t *testing.T) {
plaintext, err := DecryptPrivateKey(ciphertext, alg, key, nil)
require.NoError(t, err)
require.NotNil(t, plaintext)
require.Equal(t, message, string(plaintext))
})
})
}
}

194
crypto/asymmetric_sig.go Normal file
View File

@ -0,0 +1,194 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"errors"
"math/big"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
)
// SignPrivateKey creates a signature from a digest using a private key and the specified algorithm.
// Note: when using EdDSA, the message gets hashed as part of the signing process, so users should normally pass the full message for the "digest" parameter.
func SignPrivateKey(digest []byte, algorithm string, key jwk.Key) (signature []byte, err error) {
switch algorithm {
case Algorithm_RS256, Algorithm_RS384, Algorithm_RS512:
return signPrivateKeyRSAPKCS1v15(digest, getSHAHash(algorithm), key)
case Algorithm_PS256, Algorithm_PS384, Algorithm_PS512:
return signPrivateKeyRSAPSS(digest, getSHAHash(algorithm), key)
case Algorithm_ES256, Algorithm_ES384, Algorithm_ES512:
return signPrivateKeyECDSA(digest, key)
case Algorithm_EdDSA:
return signPrivateKeyEdDSA(digest, key)
default:
return nil, ErrUnsupportedAlgorithm
}
}
func signPrivateKeyRSAPKCS1v15(digest []byte, hash crypto.Hash, key jwk.Key) ([]byte, error) {
rsaKey := &rsa.PrivateKey{}
if key.Raw(rsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
return rsa.SignPKCS1v15(rand.Reader, rsaKey, hash, digest)
}
func signPrivateKeyRSAPSS(digest []byte, hash crypto.Hash, key jwk.Key) ([]byte, error) {
rsaKey := &rsa.PrivateKey{}
if key.Raw(rsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
return rsa.SignPSS(rand.Reader, rsaKey, hash, digest, nil)
}
func signPrivateKeyECDSA(digest []byte, key jwk.Key) ([]byte, error) {
ecdsaKey := &ecdsa.PrivateKey{}
if key.Raw(ecdsaKey) != nil {
return nil, ErrKeyTypeMismatch
}
r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, digest)
if err != nil {
return nil, err
}
return append(r.Bytes(), s.Bytes()...), nil
}
func signPrivateKeyEdDSA(message []byte, key jwk.Key) ([]byte, error) {
if key.KeyType() != jwa.OKP {
return nil, ErrKeyTypeMismatch
}
okpKey, ok := key.(jwk.OKPPrivateKey)
if !ok {
return nil, ErrKeyTypeMismatch
}
switch okpKey.Crv() {
case jwa.Ed25519:
ed25519Key := &ed25519.PrivateKey{}
if okpKey.Raw(ed25519Key) != nil {
return nil, ErrKeyTypeMismatch
}
return ed25519.Sign(*ed25519Key, message), nil
default:
return nil, ErrKeyTypeMismatch
}
}
// VerifyPublicKey validates a signature using a public key and the specified algorithm.
// Note: when using EdDSA, the message gets hashed as part of the signing process, so users should normally pass the full message for the "digest" parameter.
func VerifyPublicKey(digest []byte, signature []byte, algorithm string, key jwk.Key) (valid bool, err error) {
// Ensure we are using a public key
key, err = key.PublicKey()
if err != nil {
return false, ErrKeyTypeMismatch
}
switch algorithm {
case Algorithm_RS256, Algorithm_RS384, Algorithm_RS512:
return verifyPublicKeyRSAPKCS1v15(digest, signature, getSHAHash(algorithm), key)
case Algorithm_PS256, Algorithm_PS384, Algorithm_PS512:
return verifyPublicKeyRSAPSS(digest, signature, getSHAHash(algorithm), key)
case Algorithm_ES256, Algorithm_ES384, Algorithm_ES512:
return verifyPublicKeyECDSA(digest, signature, key)
case Algorithm_EdDSA:
return verifyPublicKeyEdDSA(digest, signature, key)
default:
return false, ErrUnsupportedAlgorithm
}
}
func verifyPublicKeyRSAPKCS1v15(digest []byte, signature []byte, hash crypto.Hash, key jwk.Key) (bool, error) {
rsaKey := &rsa.PublicKey{}
if key.Raw(rsaKey) != nil {
return false, ErrKeyTypeMismatch
}
err := rsa.VerifyPKCS1v15(rsaKey, hash, digest, signature)
if err != nil {
if errors.Is(err, rsa.ErrVerification) {
err = nil
}
return false, err
}
return true, nil
}
func verifyPublicKeyRSAPSS(digest []byte, signature []byte, hash crypto.Hash, key jwk.Key) (bool, error) {
rsaKey := &rsa.PublicKey{}
if key.Raw(rsaKey) != nil {
return false, ErrKeyTypeMismatch
}
err := rsa.VerifyPSS(rsaKey, hash, digest, signature, nil)
if err != nil {
if errors.Is(err, rsa.ErrVerification) {
err = nil
}
return false, err
}
return true, nil
}
func verifyPublicKeyECDSA(digest []byte, signature []byte, key jwk.Key) (bool, error) {
ecdsaKey := &ecdsa.PublicKey{}
if key.Raw(ecdsaKey) != nil {
return false, ErrKeyTypeMismatch
}
n := len(signature) / 2
r := &big.Int{}
r.SetBytes(signature[:n])
s := &big.Int{}
s.SetBytes(signature[n:])
return ecdsa.Verify(ecdsaKey, digest, r, s), nil
}
func verifyPublicKeyEdDSA(mesage []byte, signature []byte, key jwk.Key) (bool, error) {
if key.KeyType() != jwa.OKP {
return false, ErrKeyTypeMismatch
}
okpKey, ok := key.(jwk.OKPPublicKey)
if !ok {
return false, ErrKeyTypeMismatch
}
switch okpKey.Crv() {
case jwa.Ed25519:
ed25519Key := ed25519.PublicKey{}
if okpKey.Raw(&ed25519Key) != nil {
return false, ErrKeyTypeMismatch
}
return ed25519.Verify(ed25519Key, mesage, signature), nil
default:
return false, ErrKeyTypeMismatch
}
}

View File

@ -0,0 +1,113 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"crypto/sha256"
"testing"
"github.com/stretchr/testify/require"
)
// Long message, which we will hash in the init method
const message = "Nel mezzo del cammin di nostra vita\nmi ritrovai per una selva oscura,\nché la diritta via era smarrita.\n\nAhi quanto a dir qual era è cosa dura\nesta selva selvaggia e aspra e forte\nche nel pensier rinova la paura!\n\nTant' è amara che poco è più morte;\nma per trattar del ben ch'i' vi trovai,\ndirò de l'altre cose ch'i' v'ho scorte.\n\nIo non so ben ridir com' i' v'intrai,\ntant' era pien di sonno a quel punto\nche la verace via abbandonai.\n\nMa poi ch'i' fui al piè d'un colle giunto,\nlà dove terminava quella valle\nche m'avea di paura il cor compunto,\n\nguardai in alto e vidi le sue spalle\nvestite già de' raggi del pianeta\nche mena dritto altrui per ogne calle."
var messageHash []byte
func init() {
h := sha256.Sum256([]byte(message))
messageHash = h[:]
}
func TestSigningRSAPKCS1v15(t *testing.T) {
key, err := ParseKey([]byte(privateKeyRSAPKCS8), "application/x-pem-file")
require.NoError(t, err)
require.NotNil(t, key)
var signature []byte
t.Run("sign", func(t *testing.T) {
signature, err = SignPrivateKey(messageHash, Algorithm_RS256, key)
require.NoError(t, err)
require.NotNil(t, signature)
})
t.Run("verify", func(t *testing.T) {
var valid bool
valid, err = VerifyPublicKey(messageHash, signature, Algorithm_RS256, key)
require.NoError(t, err)
require.True(t, valid)
})
}
func TestSigningRSAPSS(t *testing.T) {
key, err := ParseKey([]byte(privateKeyRSAPKCS8), "application/x-pem-file")
require.NoError(t, err)
require.NotNil(t, key)
var signature []byte
t.Run("sign", func(t *testing.T) {
signature, err = SignPrivateKey(messageHash, Algorithm_PS256, key)
require.NoError(t, err)
require.NotNil(t, signature)
})
t.Run("verify", func(t *testing.T) {
var valid bool
valid, err = VerifyPublicKey(messageHash, signature, Algorithm_PS256, key)
require.NoError(t, err)
require.True(t, valid)
})
}
func TestSigningECDSA(t *testing.T) {
key, err := ParseKey([]byte(privateKeyP256PKCS8), "application/x-pem-file")
require.NoError(t, err)
require.NotNil(t, key)
var signature []byte
t.Run("sign", func(t *testing.T) {
signature, err = SignPrivateKey(messageHash, Algorithm_ES256, key)
require.NoError(t, err)
require.NotNil(t, signature)
})
t.Run("verify", func(t *testing.T) {
var valid bool
valid, err = VerifyPublicKey(messageHash, signature, Algorithm_ES256, key)
require.NoError(t, err)
require.True(t, valid)
})
}
func TestSigningEdDSA(t *testing.T) {
// When using EdDSA, we pass the actual mesage and not the hash
key, err := ParseKey([]byte(privateKeyEd25519JSON), "application/json")
require.NoError(t, err)
require.NotNil(t, key)
var signature []byte
t.Run("sign", func(t *testing.T) {
signature, err = SignPrivateKey([]byte(message), Algorithm_EdDSA, key)
require.NoError(t, err)
require.NotNil(t, signature)
})
t.Run("verify", func(t *testing.T) {
var valid bool
valid, err = VerifyPublicKey([]byte(message), signature, Algorithm_EdDSA, key)
require.NoError(t, err)
require.True(t, valid)
})
}

83
crypto/consts.go Normal file
View File

@ -0,0 +1,83 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase,stylecheck,revive
package crypto
import (
"errors"
)
// Errors
var (
// ErrUnsupportedAlgorithm is returned when the algorithm is unsupported.
ErrUnsupportedAlgorithm = errors.New("unsupported algorithm")
// ErrKeyTypeMismatch is returned when the key type doesn't match the requested algorithm.
ErrKeyTypeMismatch = errors.New("key type mismatch")
// ErrInvalidNonce is returned when the nonce isn't valid for the requested operation.
ErrInvalidNonce = errors.New("invalid nonce")
// ErrInvalidTag is returned when the authentication tag is not of the required format (before the message is attempted to be opened).
ErrInvalidTag = errors.New("invalid tag")
// ErrInvalidPlaintextLength is returned when the plaintext's length is invalid.
ErrInvalidPlaintextLength = errors.New("invalid plaintext length")
// ErrInvalidCiphertextLength is returned when the ciphertext's length is invalid.
ErrInvalidCiphertextLength = errors.New("invalid ciphertext length")
)
// Algorithms
const (
Algorithm_A128CBC = "A128CBC" // Encryption: AES-CBC, 128-bit key, with PKCS#7 padding
Algorithm_A192CBC = "A192CBC" // Encryption: AES-CBC, 192-bit key, with PKCS#7 padding
Algorithm_A256CBC = "A256CBC" // Encryption: AES-CBC, 256-bit key, with PKCS#7 padding
Algorithm_A128CBC_NOPAD = "A128CBC-NOPAD" // Encryption: AES-CBC, 128-bit key, no padding
Algorithm_A192CBC_NOPAD = "A192CBC-NOPAD" // Encryption: AES-CBC, 192-bit key, no padding
Algorithm_A256CBC_NOPAD = "A256CBC-NOPAD" // Encryption: AES-CBC, 256-bit key, no padding
Algorithm_A128GCM = "A128GCM" // Encryption: AES-GCM, 128-bit key
Algorithm_A192GCM = "A192GCM" // Encryption: AES-GCM, 192-bit key
Algorithm_A256GCM = "A256GCM" // Encryption: AES-GCM, 256-bit key
Algorithm_A128CBC_HS256 = "A128CBC-HS256" // Encryption: AES-CBC + HMAC-SHA256, 128-bit key
Algorithm_A192CBC_HS384 = "A192CBC-HS384" // Encryption: AES-CBC + HMAC-SHA384, 192-bit key
Algorithm_A256CBC_HS512 = "A256CBC-HS512" // Encryption: AES-CBC + HMAC-SHA512, 256-bit key
Algorithm_A128KW = "A128KW" // Encryption: AES Key Wrap (RFC 3394), 128-bit key
Algorithm_A192KW = "A192KW" // Encryption: AES Key Wrap (RFC 3394), 192-bit key
Algorithm_A256KW = "A256KW" // Encryption: AES Key Wrap (RFC 3394), 256-bit key
Algorithm_A128GCMKW = "A128GCMKW" // Encryption: AES-GCM key wrap, 128-bit key
Algorithm_A192GCMKW = "A192GCMKW" // Encryption: AES-GCM key wrap, 192-bit key
Algorithm_A256GCMKW = "A256GCMKW" // Encryption: AES-GCM key wrap, 256-bit key
Algorithm_C20P = "C20P" // Encryption: ChaCha20-Poly1305, 96-bit IV
Algorithm_XC20P = "XC20P" // Encryption: XChaCha20-Poly1305, 192-bit IV
Algorithm_C20PKW = "C20PKW" // Encryption: ChaCha20-Poly1305 key wrap, 96-bit IV
Algorithm_XC20PKW = "XC20PKW" // Encryption: XChaCha20-Poly1305 key wrap, 192-bit IV
Algorithm_ECDH_ES = "ECDH-ES" // Encryption: ECDH-ES
Algorithm_ECDH_ES_A128KW = "ECDH-ES+A128KW" // Encryption: ECDH-ES + AES key wrap, 128-bit key
Algorithm_ECDH_ES_A192KW = "ECDH-ES+A192KW" // Encryption: ECDH-ES + AES key wrap, 192-bit key
Algorithm_ECDH_ES_A256KW = "ECDH-ES+A256KW" // Encryption: ECDH-ES + AES key wrap, 256-bit key
Algorithm_RSA1_5 = "RSA1_5" // Encryption: RSA-PKCS1v1.5
Algorithm_RSA_OAEP = "RSA-OAEP" // Encryption: RSA-OAEP with SHA1 hash
Algorithm_RSA_OAEP_256 = "RSA-OAEP-256" // Encryption: RSA-OAEP with SHA256 hash
Algorithm_RSA_OAEP_384 = "RSA-OAEP-384" // Encryption: RSA-OAEP with SHA384 hash
Algorithm_RSA_OAEP_512 = "RSA-OAEP-512" // Encryption: RSA-OAEP with SHA512 hash
Algorithm_ES256 = "ES256" // Signature: ECDSA using P-256 and SHA-256
Algorithm_ES384 = "ES384" // Signature: ECDSA using P-384 and SHA-384
Algorithm_ES512 = "ES512" // Signature: ECDSA using P-521 and SHA-512
Algorithm_EdDSA = "EdDSA" // Signature: EdDSA signature algorithms
Algorithm_HS256 = "HS256" // Signature: HMAC using SHA-256
Algorithm_HS384 = "HS384" // Signature: HMAC using SHA-384
Algorithm_HS512 = "HS512" // Signature: HMAC using SHA-512
Algorithm_PS256 = "PS256" // Signature: RSASSA-PSS using SHA256 and MGF1-SHA256
Algorithm_PS384 = "PS384" // Signature: RSASSA-PSS using SHA384 and MGF1-SHA384
Algorithm_PS512 = "PS512" // Signature: RSASSA-PSS using SHA512 and MGF1-SHA512
Algorithm_RS256 = "RS256" // Signature: RSASSA-PKCS-v1.5 using SHA-256
Algorithm_RS384 = "RS384" // Signature: RSASSA-PKCS-v1.5 using SHA-384
Algorithm_RS512 = "RS512" // Signature: RSASSA-PKCS-v1.5 using SHA-512
)

80
crypto/crypto.go Normal file
View File

@ -0,0 +1,80 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"crypto"
"github.com/lestrrat-go/jwx/v2/jwk"
)
// Encrypt a message using the given algorithm and key, supporting both symmetric and asymmetric ciphers.
func Encrypt(plaintext []byte, algorithm string, key jwk.Key, nonce []byte, associatedData []byte) (ciphertext []byte, tag []byte, err error) {
// Note that this includes all constants defined in consts.go, but some algorithms are not supported (yet)
switch algorithm {
case Algorithm_A128CBC, Algorithm_A192CBC, Algorithm_A256CBC,
Algorithm_A128GCM, Algorithm_A192GCM, Algorithm_A256GCM,
Algorithm_A128CBC_HS256, Algorithm_A192CBC_HS384, Algorithm_A256CBC_HS512,
Algorithm_A128KW, Algorithm_A192KW, Algorithm_A256KW,
Algorithm_A128GCMKW, Algorithm_A192GCMKW, Algorithm_A256GCMKW,
Algorithm_C20P, Algorithm_XC20P, Algorithm_C20PKW, Algorithm_XC20PKW:
return EncryptSymmetric(plaintext, algorithm, key, nonce, associatedData)
case Algorithm_ECDH_ES,
Algorithm_ECDH_ES_A128KW, Algorithm_ECDH_ES_A192KW, Algorithm_ECDH_ES_A256KW,
Algorithm_RSA1_5,
Algorithm_RSA_OAEP, Algorithm_RSA_OAEP_256, Algorithm_RSA_OAEP_384, Algorithm_RSA_OAEP_512:
ciphertext, err = EncryptPublicKey(plaintext, algorithm, key, associatedData)
return
default:
return nil, nil, ErrUnsupportedAlgorithm
}
}
// Decrypt a message using the given algorithm and key, supporting both symmetric and asymmetric ciphers.
func Decrypt(ciphertext []byte, algorithm string, key jwk.Key, nonce []byte, tag []byte, associatedData []byte) (plaintext []byte, err error) {
// Note that this includes all constants defined in consts.go, but some algorithms are not supported (yet)
switch algorithm {
case Algorithm_A128CBC, Algorithm_A192CBC, Algorithm_A256CBC,
Algorithm_A128GCM, Algorithm_A192GCM, Algorithm_A256GCM,
Algorithm_A128CBC_HS256, Algorithm_A192CBC_HS384, Algorithm_A256CBC_HS512,
Algorithm_A128KW, Algorithm_A192KW, Algorithm_A256KW,
Algorithm_A128GCMKW, Algorithm_A192GCMKW, Algorithm_A256GCMKW,
Algorithm_C20P, Algorithm_XC20P, Algorithm_C20PKW, Algorithm_XC20PKW:
return DecryptSymmetric(ciphertext, algorithm, key, nonce, tag, associatedData)
case Algorithm_ECDH_ES,
Algorithm_ECDH_ES_A128KW, Algorithm_ECDH_ES_A192KW, Algorithm_ECDH_ES_A256KW,
Algorithm_RSA1_5,
Algorithm_RSA_OAEP, Algorithm_RSA_OAEP_256, Algorithm_RSA_OAEP_384, Algorithm_RSA_OAEP_512:
return DecryptPrivateKey(ciphertext, algorithm, key, associatedData)
default:
return nil, ErrUnsupportedAlgorithm
}
}
func getSHAHash(alg string) crypto.Hash {
switch alg[len(alg)-3:] {
case "256":
return crypto.SHA256
case "384":
return crypto.SHA384
case "512":
return crypto.SHA512
}
return crypto.Hash(0)
}

93
crypto/keys.go Normal file
View File

@ -0,0 +1,93 @@
/*
Copyright 2022 The Dapr 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 crypto
import (
"bytes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"github.com/lestrrat-go/jwx/v2/jwk"
)
// SerializeKey serializes a jwk.Key in the appropriate format so they can be wrapped.
// Symmetric keys are returned as raw bytes, while asymmetric keys are marshalled as ASN.1 DER (X.509, not PEM-encoded).
func SerializeKey(key jwk.Key) ([]byte, error) {
var rawKey any
err := key.Raw(&rawKey)
if err != nil {
return nil, fmt.Errorf("failed to extract raw key: %w", err)
}
switch r := rawKey.(type) {
case []byte: // Symmetric keys
return r, nil
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: // Private keys: marshal as PKCS#8
return x509.MarshalPKCS8PrivateKey(r)
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: // Public keys: marshal as PKIX
return x509.MarshalPKIXPublicKey(r)
default:
return nil, errors.New("unsupported key type")
}
}
// ParseKey takes a byte slice and returns a key (public, private, symmetric) after determining its type.
// It supports keys represented as JWKs, PEM-encoded (PKCS#8, PKCS#1 or PKIX) or as raw bytes (optionally base64-encoded).
// The parameter contentType is optional and it can contain a mime type.
func ParseKey(raw []byte, contentType string) (jwk.Key, error) {
// Determine the type of key if the type parameter is set
switch contentType {
case "application/json": // JWK
return jwk.ParseKey(raw)
case "application/x-pem-file", "application/pkcs8": // Generic PEM: PKCS#1, PKCS#8, PKIX
return jwk.ParseKey(raw, jwk.WithPEM(true))
}
// Heuristically determine the type of key
l := len(raw)
switch {
case raw[0] == '{' && l != 16 && l != 24 && l != 32: // Assume it's a JWK unless the length is 16, 24, or 32 bytes
return jwk.ParseKey(raw)
case len(raw) > 10 && string(raw[0:5]) == ("-----"): // Assume it's something PEM-encoded
return jwk.ParseKey(raw, jwk.WithPEM(true))
default: // Assume a symmetric key
return parseSymmetricKey(raw)
}
}
func parseSymmetricKey(raw []byte) (jwk.Key, error) {
// Try parsing as base64; first: remove any padding if present
trimmedRaw := bytes.TrimRight(raw, "=")
// Try parsing as base64-standard
dst := make([]byte, base64.RawStdEncoding.DecodedLen(len(raw)))
n, err := base64.RawStdEncoding.Decode(dst, trimmedRaw)
if err == nil {
return jwk.FromRaw(dst[:n])
}
// Try parsing as base64-url
n, err = base64.RawURLEncoding.Decode(dst, trimmedRaw)
if err == nil {
return jwk.FromRaw(dst[:n])
}
// Treat the byte slice as the raw, symmetric key
return jwk.FromRaw(raw)
}

295
crypto/keys_test.go Normal file
View File

@ -0,0 +1,295 @@
/*
Copyright 2022 The Dapr 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 crypto
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"encoding/pem"
"io"
"testing"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/stretchr/testify/require"
)
func TestSymmetricKeys(t *testing.T) {
rawKey := make([]byte, 16)
_, rawErr := io.ReadFull(rand.Reader, rawKey)
require.NoError(t, rawErr)
rawKeyJSON, _ := json.Marshal(map[string]any{
"kty": "oct",
"k": rawKey,
})
testSymmetricKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.OctetSeq, key.KeyType())
err = key.Raw(&exported)
require.NoError(t, err)
require.Equal(t, rawKey, exported)
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.Equal(t, rawKey, exported)
})
}
}
t.Run("raw bytes", testSymmetricKey(rawKey, ""))
t.Run("base64 standard with padding", testSymmetricKey([]byte(base64.StdEncoding.EncodeToString(rawKey)), ""))
t.Run("base64 standard without padding", testSymmetricKey([]byte(base64.RawStdEncoding.EncodeToString(rawKey)), ""))
t.Run("base64 url with padding", testSymmetricKey([]byte(base64.URLEncoding.EncodeToString(rawKey)), ""))
t.Run("base64 url without padding", testSymmetricKey([]byte(base64.RawURLEncoding.EncodeToString(rawKey)), ""))
t.Run("JSON with content-type", testSymmetricKey(rawKeyJSON, "application/json"))
t.Run("JSON without content-type", testSymmetricKey(rawKeyJSON, ""))
}
func TestPrivateKeysRSA(t *testing.T) {
testPrivateKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.RSA, key.KeyType())
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.NotEmpty(t, exported)
exportedPEM := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: exported,
})
require.Equal(t, privateKeyRSAPKCS8, string(exportedPEM))
})
}
}
t.Run("PEM PKCS#8 with content-type", testPrivateKey([]byte(privateKeyRSAPKCS8), "application/x-pem-file"))
t.Run("PEM PKCS#8 with content-type 2", testPrivateKey([]byte(privateKeyRSAPKCS8), "application/pkcs8"))
t.Run("PEM PKCS#8 without content-type", testPrivateKey([]byte(privateKeyRSAPKCS8), ""))
t.Run("PEM PKCS#1 with content-type", testPrivateKey([]byte(privateKeyRSAPKCS1), "application/x-pem-file"))
t.Run("PEM PKCS#1 without content-type", testPrivateKey([]byte(privateKeyRSAPKCS1), ""))
t.Run("JSON with content-type", testPrivateKey([]byte(privateKeyRSAJSON), "application/json"))
t.Run("JSON without content-type", testPrivateKey([]byte(privateKeyRSAJSON), ""))
}
func TestPublicKeysRSA(t *testing.T) {
testPublicKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.RSA, key.KeyType())
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.NotEmpty(t, exported)
exportedPEM := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: exported,
})
require.Equal(t, publicKeyRSAPKIX, string(exportedPEM))
})
}
}
t.Run("PEM PKIX with content-type", testPublicKey([]byte(publicKeyRSAPKIX), "application/x-pem-file"))
t.Run("PEM PKIX without content-type", testPublicKey([]byte(publicKeyRSAPKIX), ""))
t.Run("PEM PKCS#1 with content-type", testPublicKey([]byte(publicKeyRSAPKCS1), "application/x-pem-file"))
t.Run("PEM PKCS#1 without content-type", testPublicKey([]byte(publicKeyRSAPKCS1), ""))
t.Run("JSON with content-type", testPublicKey([]byte(publicKeyRSAJSON), "application/json"))
t.Run("JSON without content-type", testPublicKey([]byte(publicKeyRSAJSON), ""))
}
func TestPrivateKeysEd25519(t *testing.T) {
testPrivateKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.OKP, key.KeyType())
crv, ok := key.Get("crv")
require.True(t, ok)
require.Equal(t, jwa.Ed25519, crv)
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.NotEmpty(t, exported)
exportedPEM := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: exported,
})
require.Equal(t, privateKeyEd25519PKCS8, string(exportedPEM))
})
}
}
t.Run("PEM PKCS#8 with content-type", testPrivateKey([]byte(privateKeyEd25519PKCS8), "application/x-pem-file"))
t.Run("PEM PKCS#8 with content-type 2", testPrivateKey([]byte(privateKeyEd25519PKCS8), "application/pkcs8"))
t.Run("PEM PKCS#8 without content-type", testPrivateKey([]byte(privateKeyEd25519PKCS8), ""))
t.Run("JSON with content-type", testPrivateKey([]byte(privateKeyEd25519JSON), "application/json"))
t.Run("JSON without content-type", testPrivateKey([]byte(privateKeyEd25519JSON), ""))
}
func TestPublicKeysEd25519(t *testing.T) {
testPublicKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.OKP, key.KeyType())
crv, ok := key.Get("crv")
require.True(t, ok)
require.Equal(t, jwa.Ed25519, crv)
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.NotEmpty(t, exported)
exportedPEM := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: exported,
})
require.Equal(t, publicKeyEd25519PKIX, string(exportedPEM))
})
}
}
t.Run("PEM PKIX with content-type", testPublicKey([]byte(publicKeyEd25519PKIX), "application/x-pem-file"))
t.Run("PEM PKIX without content-type", testPublicKey([]byte(publicKeyEd25519PKIX), ""))
t.Run("JSON with content-type", testPublicKey([]byte(publicKeyEd25519JSON), "application/json"))
t.Run("JSON without content-type", testPublicKey([]byte(publicKeyEd25519JSON), ""))
}
func TestPrivateKeysP256(t *testing.T) {
testPrivateKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.EC, key.KeyType())
crv, ok := key.Get("crv")
require.True(t, ok)
require.Equal(t, jwa.P256, crv)
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.NotEmpty(t, exported)
exportedPEM := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: exported,
})
require.Equal(t, privateKeyP256PKCS8, string(exportedPEM))
})
}
}
t.Run("PEM EC with content-type", testPrivateKey([]byte(privateKeyP256EC), "application/x-pem-file"))
t.Run("PEM EC without content-type", testPrivateKey([]byte(privateKeyP256EC), ""))
t.Run("PEM PKCS8 with content-type", testPrivateKey([]byte(privateKeyP256PKCS8), "application/x-pem-file"))
t.Run("PEM PKCS8 without content-type", testPrivateKey([]byte(privateKeyP256PKCS8), ""))
t.Run("JSON with content-type", testPrivateKey([]byte(privateKeyP256JSON), "application/json"))
t.Run("JSON without content-type", testPrivateKey([]byte(privateKeyP256JSON), ""))
}
func TestPublicKeysP256(t *testing.T) {
testPublicKey := func(parse []byte, contentType string) func(t *testing.T) {
return func(t *testing.T) {
var (
key jwk.Key
exported []byte
err error
)
t.Run("parse", func(t *testing.T) {
key, err = ParseKey(parse, contentType)
require.NoError(t, err)
require.Equal(t, jwa.EC, key.KeyType())
crv, ok := key.Get("crv")
require.True(t, ok)
require.Equal(t, jwa.P256, crv)
})
t.Run("serialize", func(t *testing.T) {
require.NotNil(t, key)
exported, err = SerializeKey(key)
require.NoError(t, err)
require.NotEmpty(t, exported)
exportedPEM := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: exported,
})
require.Equal(t, publicKeyP256PKIX, string(exportedPEM))
})
}
}
t.Run("PEM PKIX with content-type", testPublicKey([]byte(publicKeyP256PKIX), "application/x-pem-file"))
t.Run("PEM PKIX without content-type", testPublicKey([]byte(publicKeyP256PKIX), ""))
t.Run("JSON with content-type", testPublicKey([]byte(publicKeyP256JSON), "application/json"))
t.Run("JSON without content-type", testPublicKey([]byte(publicKeyP256JSON), ""))
}

67
crypto/pkcs7_padding.go Normal file
View File

@ -0,0 +1,67 @@
/*
Copyright 2022 The Dapr 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 crypto
/*!
This code is adapted from https://github.com/mergermarket/go-pkcs7/tree/153b18ea13c9b94f698070cadb23701e51a55b3e
Copyright (c) 2017 Richard Zadorozny
License: MIT https://github.com/mergermarket/go-pkcs7/blob/153b18ea13c9b94f698070cadb23701e51a55b3e/LICENSE
*/
import (
"bytes"
"errors"
)
var (
ErrInvalidPKCS7BlockSize = errors.New("pkcs7: invalid block size")
ErrInvalidPKCS7Padding = errors.New("pkcs7: incorrect padding")
)
// PadPKCS7 adds PKCS#7 padding to a message.
func PadPKCS7(buf []byte, size int) ([]byte, error) {
if size <= 1 || size >= 256 {
return nil, ErrInvalidPKCS7BlockSize
}
bufLen := len(buf)
padLen := size - bufLen%size
padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
return append(buf, padding...), nil
}
// UnpadPKCS7 removes PKCS#7 from a message.
func UnpadPKCS7(buf []byte, size int) ([]byte, error) {
if size <= 1 || size >= 256 {
return nil, ErrInvalidPKCS7BlockSize
}
l := len(buf)
if l == 0 {
return []byte{}, nil
}
if l%size != 0 {
return nil, ErrInvalidPKCS7Padding
}
padLen := int(buf[l-1])
if padLen <= 0 || padLen > size {
return nil, ErrInvalidPKCS7Padding
}
padLenB := byte(padLen)
for i := l - padLen; i < l; i++ {
if buf[i] != padLenB {
return nil, ErrInvalidPKCS7Padding
}
}
return buf[:l-padLen], nil
}

View File

@ -0,0 +1,137 @@
/*
Copyright 2022 The Dapr 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 crypto
/*!
This code is adapted from https://github.com/mergermarket/go-pkcs7/tree/153b18ea13c9b94f698070cadb23701e51a55b3e
Copyright (c) 2017 Richard Zadorozny
License: MIT https://github.com/mergermarket/go-pkcs7/blob/153b18ea13c9b94f698070cadb23701e51a55b3e/LICENSE
*/
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPkcs7(t *testing.T) {
const blockSize = 16
t.Run("Pads", func(t *testing.T) {
expected := []byte("1234567890\x06\x06\x06\x06\x06\x06")
result, err := PadPKCS7([]byte("1234567890"), blockSize)
require.NoError(t, err)
assert.Equal(t, expected, result)
})
t.Run("Unpads", func(t *testing.T) {
result, _ := UnpadPKCS7([]byte("1234567890\x06\x06\x06\x06\x06\x06"), blockSize)
expected := []byte("1234567890")
assert.Equal(t, expected, result)
})
t.Run("Handles long", func(t *testing.T) {
longStr := []byte("123456789012345678901234567890123456789012345678901234567890")
expected := []byte("123456789012345678901234567890123456789012345678901234567890\x04\x04\x04\x04")
padded, err := PadPKCS7(longStr, blockSize)
require.NoError(t, err)
assert.Equal(t, expected, padded)
if bytes.Equal(padded, expected) == false {
panic(fmt.Sprintf(`Padding wrong - expected "%x" but got "%x"`, expected, padded))
}
unpadded, err := UnpadPKCS7(padded, blockSize)
require.NoError(t, err)
assert.Equal(t, longStr, unpadded)
})
t.Run("Handles short", func(t *testing.T) {
shortStr := []byte("1")
expected := []byte("1\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f")
padded, err := PadPKCS7(shortStr, blockSize)
require.NoError(t, err)
assert.Equal(t, expected, padded)
unpadded, _ := UnpadPKCS7(padded, blockSize)
assert.Equal(t, shortStr, unpadded)
})
t.Run("Handles empty", func(t *testing.T) {
emptyStr := []byte("")
expected := []byte("\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10")
padded, err := PadPKCS7(emptyStr, blockSize)
require.NoError(t, err)
assert.Equal(t, expected, padded)
unpadded, err := UnpadPKCS7(padded, blockSize)
require.NoError(t, err)
assert.Equal(t, emptyStr, unpadded)
})
t.Run("Handles block size", func(t *testing.T) {
val := []byte("1234567890ABCDEF")
expected := []byte("1234567890ABCDEF\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10")
padded, err := PadPKCS7(val, blockSize)
require.NoError(t, err)
assert.Equal(t, expected, padded)
unpadded, err := UnpadPKCS7(padded, blockSize)
require.NoError(t, err)
assert.Equal(t, val, unpadded)
})
t.Run("Invalid length while unpadding", func(t *testing.T) {
unpadded, err := UnpadPKCS7([]byte("1234567890\x06\x06\x06\x06"), blockSize)
require.Error(t, err)
assert.ErrorIs(t, err, ErrInvalidPKCS7Padding)
assert.Nil(t, unpadded)
})
t.Run("Invalid padding bytes", func(t *testing.T) {
tests := [][]byte{
[]byte("1234567890\x06\x06\x06\x06\x06\x07"),
[]byte("1234567890\x06\x06\x07\x07\x06\x06"),
[]byte("1234567890\x01\x06\x06\x06\x06\x06"),
[]byte("1234567890\x0A\x0A\x0A\x0A\x0A\x0A"),
[]byte("1234567890\xEE\xEE\xEE\xEE\xEE\xEE"),
}
for _, tt := range tests {
unpadded, err := UnpadPKCS7(tt, blockSize)
require.Error(t, err)
assert.ErrorIs(t, err, ErrInvalidPKCS7Padding)
assert.Nil(t, unpadded)
}
})
t.Run("Invalid block size", func(t *testing.T) {
res, err := PadPKCS7([]byte("1234567890ABCDEF"), 260)
require.Error(t, err)
assert.ErrorIs(t, err, ErrInvalidPKCS7BlockSize)
assert.Nil(t, res)
res, err = UnpadPKCS7([]byte("1234567890ABCDEF"), 260)
require.Error(t, err)
assert.ErrorIs(t, err, ErrInvalidPKCS7BlockSize)
assert.Nil(t, res)
})
t.Run("Unpad empty string", func(t *testing.T) {
res, err := UnpadPKCS7([]byte{}, blockSize)
require.NoError(t, err)
assert.Empty(t, res)
})
}

File diff suppressed because one or more lines are too long

301
crypto/symmetric.go Normal file
View File

@ -0,0 +1,301 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"crypto/aes"
"crypto/cipher"
"errors"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"golang.org/x/crypto/chacha20poly1305"
"github.com/dapr/kit/crypto/aeskw"
)
// EncryptSymmetric encrypts a message using a symmetric key and the specified algorithm.
// Note that "associatedData" is ignored if the cipher does not support labels/AAD.
func EncryptSymmetric(plaintext []byte, algorithm string, key jwk.Key, nonce []byte, associatedData []byte) (ciphertext []byte, tag []byte, err error) {
var keyBytes []byte
if key.KeyType() != jwa.OctetSeq || key.Raw(&keyBytes) != nil {
return nil, nil, ErrKeyTypeMismatch
}
switch algorithm {
case Algorithm_A128CBC, Algorithm_A192CBC, Algorithm_A256CBC,
Algorithm_A128CBC_NOPAD, Algorithm_A192CBC_NOPAD, Algorithm_A256CBC_NOPAD:
ciphertext, err = encryptSymmetricAESCBC(plaintext, algorithm, keyBytes, nonce)
return ciphertext, tag, err
case Algorithm_A128GCM, Algorithm_A192GCM, Algorithm_A256GCM:
return encryptSymmetricAESGCM(plaintext, algorithm, keyBytes, nonce, associatedData)
case Algorithm_A128KW, Algorithm_A192KW, Algorithm_A256KW:
ciphertext, err = encryptSymmetricAESKW(plaintext, algorithm, keyBytes)
return ciphertext, tag, err
case Algorithm_C20P, Algorithm_C20PKW, Algorithm_XC20P, Algorithm_XC20PKW:
return encryptSymmetricChaCha20Poly1305(plaintext, algorithm, keyBytes, nonce, associatedData)
default:
return nil, nil, ErrUnsupportedAlgorithm
}
}
// DecryptSymmetric decrypts an encrypted message using a symmetric key and the specified algorithm.
// Note that "associatedData" is ignored if the cipher does not support labels/AAD.
func DecryptSymmetric(ciphertext []byte, algorithm string, key jwk.Key, nonce []byte, tag []byte, associatedData []byte) (plaintext []byte, err error) {
var keyBytes []byte
if key.KeyType() != jwa.OctetSeq || key.Raw(&keyBytes) != nil {
return nil, ErrKeyTypeMismatch
}
switch algorithm {
case Algorithm_A128CBC, Algorithm_A192CBC, Algorithm_A256CBC,
Algorithm_A128CBC_NOPAD, Algorithm_A192CBC_NOPAD, Algorithm_A256CBC_NOPAD:
return decryptSymmetricAESCBC(ciphertext, algorithm, keyBytes, nonce)
case Algorithm_A128GCM, Algorithm_A192GCM, Algorithm_A256GCM:
return decryptSymmetricAESGCM(ciphertext, algorithm, keyBytes, nonce, tag, associatedData)
case Algorithm_A128KW, Algorithm_A192KW, Algorithm_A256KW:
return decryptSymmetricAESKW(ciphertext, algorithm, keyBytes)
case Algorithm_C20P, Algorithm_C20PKW, Algorithm_XC20P, Algorithm_XC20PKW:
return decryptSymmetricChaCha20Poly1305(ciphertext, algorithm, keyBytes, nonce, tag, associatedData)
default:
return nil, ErrUnsupportedAlgorithm
}
}
func encryptSymmetricAESCBC(plaintext []byte, algorithm string, key []byte, iv []byte) (ciphertext []byte, err error) {
if len(key) != expectedKeySize(algorithm) {
return nil, ErrKeyTypeMismatch
}
if len(iv) != aes.BlockSize {
return nil, ErrInvalidNonce
}
switch algorithm {
case Algorithm_A128CBC_NOPAD, Algorithm_A192CBC_NOPAD, Algorithm_A256CBC_NOPAD:
if (len(plaintext) % aes.BlockSize) != 0 {
return nil, ErrInvalidPlaintextLength
}
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, ErrKeyTypeMismatch
}
switch algorithm {
case Algorithm_A128CBC_NOPAD, Algorithm_A192CBC_NOPAD, Algorithm_A256CBC_NOPAD:
// nop
default:
plaintext, err = PadPKCS7(plaintext, aes.BlockSize)
if err != nil {
return nil, err
}
}
ciphertext = make([]byte, len(plaintext))
cipher.NewCBCEncrypter(block, iv).
CryptBlocks(ciphertext, plaintext)
return ciphertext, err
}
// Note that when using PKCS#7 padding, this returns a specific error if padding mismatches.
// Callers are responsible for handling these errors in a way that doesn't introduce the possibility of padding oracle attacks.
// See: https://research.nccgroup.com/2021/02/17/cryptopals-exploiting-cbc-padding-oracles/
func decryptSymmetricAESCBC(ciphertext []byte, algorithm string, key []byte, iv []byte) (plaintext []byte, err error) {
if len(key) != expectedKeySize(algorithm) {
return nil, ErrKeyTypeMismatch
}
if len(iv) != aes.BlockSize {
return nil, ErrInvalidNonce
}
if (len(ciphertext) % aes.BlockSize) != 0 {
return nil, ErrInvalidCiphertextLength
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, ErrKeyTypeMismatch
}
plaintext = make([]byte, len(ciphertext))
cipher.NewCBCDecrypter(block, iv).
CryptBlocks(plaintext, ciphertext)
switch algorithm {
case Algorithm_A128CBC_NOPAD, Algorithm_A192CBC_NOPAD, Algorithm_A256CBC_NOPAD:
// nop
default:
plaintext, err = UnpadPKCS7(plaintext, aes.BlockSize)
if err != nil {
return nil, err
}
}
return plaintext, err
}
func encryptSymmetricAESGCM(plaintext []byte, algorithm string, key []byte, nonce []byte, associatedData []byte) (ciphertext []byte, tag []byte, err error) {
if len(key) != expectedKeySize(algorithm) {
return nil, nil, ErrKeyTypeMismatch
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, ErrKeyTypeMismatch
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, ErrKeyTypeMismatch
}
if len(nonce) != aead.NonceSize() {
return nil, nil, ErrInvalidNonce
}
out := aead.Seal(nil, nonce, plaintext, associatedData)
// Tag is added at the end
tagSize := aead.Overhead()
return out[0 : len(out)-tagSize], out[len(out)-tagSize:], nil
}
func decryptSymmetricAESGCM(ciphertext []byte, algorithm string, key []byte, nonce []byte, tag []byte, associatedData []byte) (plaintext []byte, err error) {
if len(key) != expectedKeySize(algorithm) {
return nil, ErrKeyTypeMismatch
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, ErrKeyTypeMismatch
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, ErrKeyTypeMismatch
}
if len(nonce) != aead.NonceSize() {
return nil, ErrInvalidNonce
}
if len(tag) != aead.Overhead() {
return nil, ErrInvalidTag
}
// Add the tag at the end of the ciphertext
ciphertext = append(ciphertext, tag...)
return aead.Open(nil, nonce, ciphertext, associatedData)
}
func encryptSymmetricAESKW(plaintext []byte, algorithm string, key []byte) (ciphertext []byte, err error) {
if len(key) != expectedKeySize(algorithm) {
return nil, ErrKeyTypeMismatch
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, ErrKeyTypeMismatch
}
return aeskw.Wrap(block, plaintext)
}
func decryptSymmetricAESKW(ciphertext []byte, algorithm string, key []byte) (plaintext []byte, err error) {
if len(key) != expectedKeySize(algorithm) {
return nil, ErrKeyTypeMismatch
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, ErrKeyTypeMismatch
}
return aeskw.Unwrap(block, ciphertext)
}
func encryptSymmetricChaCha20Poly1305(plaintext []byte, algorithm string, key []byte, nonce []byte, associatedData []byte) (ciphertext []byte, tag []byte, err error) {
if len(key) != chacha20poly1305.KeySize {
return nil, nil, ErrKeyTypeMismatch
}
aead, err := getChaCha20Poly1305Cipher(algorithm, key, nonce)
if err != nil {
return nil, nil, err
}
// Tag is added at the end
out := aead.Seal(nil, nonce, plaintext, associatedData)
return out[0 : len(out)-chacha20poly1305.Overhead], out[len(out)-chacha20poly1305.Overhead:], nil
}
func decryptSymmetricChaCha20Poly1305(ciphertext []byte, algorithm string, key []byte, nonce []byte, tag []byte, associatedData []byte) (plaintext []byte, err error) {
if len(key) != chacha20poly1305.KeySize {
return nil, ErrKeyTypeMismatch
}
aead, err := getChaCha20Poly1305Cipher(algorithm, key, nonce)
if err != nil {
return nil, err
}
if len(tag) != aead.Overhead() {
return nil, ErrInvalidTag
}
// Add the tag at the end of the ciphertext
ciphertext = append(ciphertext, tag...)
return aead.Open(nil, nonce, ciphertext, associatedData)
}
func getChaCha20Poly1305Cipher(algorithm string, key []byte, nonce []byte) (aead cipher.AEAD, err error) {
switch algorithm {
case Algorithm_C20P, Algorithm_C20PKW:
aead, err = chacha20poly1305.New(key)
if err == nil && len(nonce) != chacha20poly1305.NonceSize {
err = ErrInvalidNonce
}
return
case Algorithm_XC20P, Algorithm_XC20PKW:
aead, err = chacha20poly1305.NewX(key)
if err == nil && len(nonce) != chacha20poly1305.NonceSizeX {
err = ErrInvalidNonce
}
return
}
return nil, errors.New("invalid algorithm")
}
func expectedKeySize(alg string) int {
switch alg[1:4] {
case "128":
return 16
case "192":
return 24
case "256":
return 32
}
return 0
}

684
crypto/symmetric_test.go Normal file
View File

@ -0,0 +1,684 @@
/*
Copyright 2022 The Dapr 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.
*/
//nolint:nosnakecase
package crypto
import (
"encoding/hex"
"encoding/json"
"errors"
"os"
"reflect"
"testing"
)
func TestEncryptSymmetricAESCBC(t *testing.T) {
type args struct {
plaintext []byte
algorithm string
key []byte
iv []byte
}
type test struct {
name string
args args
wantCiphertext []byte
wantErr error
}
tests := []test{
{
name: "key size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: []byte{0x00, 0x01},
iv: mustDecodeHexString("000102030405060708090a0b0c0d0e0f"),
plaintext: mustDecodeHexString("6bc1bee22e409f96e93d7e117393172a"),
},
wantErr: ErrKeyTypeMismatch,
},
{
name: "iv size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: []byte{0x00, 0x01},
plaintext: mustDecodeHexString("6bc1bee22e409f96e93d7e117393172a"),
},
wantErr: ErrInvalidNonce,
},
}
// Test vectors from NIST publication SP800-38A with added padding
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-cbc") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
iv: mustDecodeHexString(v.Nonce),
plaintext: mustDecodeHexString(v.Plaintext),
},
wantCiphertext: mustDecodeHexString(v.Ciphertext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCiphertext, err := encryptSymmetricAESCBC(tt.args.plaintext, tt.args.algorithm, tt.args.key, tt.args.iv)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("encryptSymmetricAESCBC() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotCiphertext, tt.wantCiphertext) {
t.Errorf("encryptSymmetricAESCBC() = %v, want %v", gotCiphertext, tt.wantCiphertext)
}
})
}
}
func TestEncryptSymmetricAESCBCNOPAD(t *testing.T) {
type args struct {
plaintext []byte
algorithm string
key []byte
iv []byte
}
type test struct {
name string
args args
wantCiphertext []byte
wantErr error
}
tests := []test{
{
name: "key size mismatch",
args: args{
algorithm: Algorithm_A128CBC_NOPAD,
key: []byte{0x00, 0x01},
iv: mustDecodeHexString("000102030405060708090a0b0c0d0e0f"),
plaintext: mustDecodeHexString("6bc1bee22e409f96e93d7e117393172a"),
},
wantErr: ErrKeyTypeMismatch,
},
{
name: "iv size mismatch",
args: args{
algorithm: Algorithm_A128CBC_NOPAD,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: []byte{0x00, 0x01},
plaintext: mustDecodeHexString("6bc1bee22e409f96e93d7e117393172a"),
},
wantErr: ErrInvalidNonce,
},
{
name: "invalid plaintext length",
args: args{
algorithm: Algorithm_A128CBC_NOPAD,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: mustDecodeHexString("000102030405060708090a0b0c0d0e0f"),
plaintext: mustDecodeHexString("0011"),
},
wantErr: ErrInvalidPlaintextLength,
},
}
// Test vectors from NIST publication SP800-38A
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-cbc-nopad") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
iv: mustDecodeHexString(v.Nonce),
plaintext: mustDecodeHexString(v.Plaintext),
},
wantCiphertext: mustDecodeHexString(v.Ciphertext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCiphertext, err := encryptSymmetricAESCBC(tt.args.plaintext, tt.args.algorithm, tt.args.key, tt.args.iv)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("encryptSymmetricAESCBC() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotCiphertext, tt.wantCiphertext) {
t.Errorf("encryptSymmetricAESCBC() = %v, want %v", gotCiphertext, tt.wantCiphertext)
}
})
}
}
func TestDecryptSymmetricAESCBC(t *testing.T) {
type args struct {
ciphertext []byte
algorithm string
key []byte
iv []byte
}
type test struct {
name string
args args
wantPlaintext []byte
wantErr error
}
tests := []test{
{
name: "key size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: []byte{0x00, 0x01},
iv: mustDecodeHexString("000102030405060708090a0b0c0d0e0f"),
ciphertext: mustDecodeHexString("00000000000000000000000000000000"),
},
wantErr: ErrKeyTypeMismatch,
},
{
name: "iv size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: []byte{0x00, 0x01},
ciphertext: mustDecodeHexString("00000000000000000000000000000000"),
},
wantErr: ErrInvalidNonce,
},
{
name: "invalid padding",
args: args{
algorithm: Algorithm_A128CBC,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: mustDecodeHexString("5086cb9b507219ee95db113a917678b2"),
ciphertext: mustDecodeHexString("73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adcea"),
},
wantErr: ErrInvalidPKCS7Padding,
},
}
// Test vectors from NIST publication SP800-38A
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-cbc") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
iv: mustDecodeHexString(v.Nonce),
ciphertext: mustDecodeHexString(v.Ciphertext),
},
wantPlaintext: mustDecodeHexString(v.Plaintext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPlaintext, err := decryptSymmetricAESCBC(tt.args.ciphertext, tt.args.algorithm, tt.args.key, tt.args.iv)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("decryptSymmetricAESCBC() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotPlaintext, tt.wantPlaintext) {
t.Errorf("decryptSymmetricAESCBC() = %v, want %v", gotPlaintext, tt.wantPlaintext)
}
})
}
}
func TestDecryptSymmetricAESCBCNOPAD(t *testing.T) {
type args struct {
ciphertext []byte
algorithm string
key []byte
iv []byte
}
type test struct {
name string
args args
wantPlaintext []byte
wantErr error
}
tests := []test{
{
name: "key size mismatch",
args: args{
algorithm: Algorithm_A128CBC_NOPAD,
key: []byte{0x00, 0x01},
iv: mustDecodeHexString("000102030405060708090a0b0c0d0e0f"),
ciphertext: mustDecodeHexString("00000000000000000000000000000000"),
},
wantErr: ErrKeyTypeMismatch,
},
{
name: "iv size mismatch",
args: args{
algorithm: Algorithm_A128CBC_NOPAD,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: []byte{0x00, 0x01},
ciphertext: mustDecodeHexString("00000000000000000000000000000000"),
},
wantErr: ErrInvalidNonce,
},
{
name: "invalid ciphertext length",
args: args{
algorithm: Algorithm_A128CBC_NOPAD,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
iv: mustDecodeHexString("000102030405060708090a0b0c0d0e0f"),
ciphertext: mustDecodeHexString("0011"),
},
wantErr: ErrInvalidCiphertextLength,
},
}
// Test vectors from NIST publication SP800-38A
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-cbc-nopad") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
iv: mustDecodeHexString(v.Nonce),
ciphertext: mustDecodeHexString(v.Ciphertext),
},
wantPlaintext: mustDecodeHexString(v.Plaintext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPlaintext, err := decryptSymmetricAESCBC(tt.args.ciphertext, tt.args.algorithm, tt.args.key, tt.args.iv)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("decryptSymmetricAESCBC() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotPlaintext, tt.wantPlaintext) {
t.Errorf("decryptSymmetricAESCBC() = %v, want %v", gotPlaintext, tt.wantPlaintext)
}
})
}
}
func TestEncryptSymmetricAESGCM(t *testing.T) {
type args struct {
plaintext []byte
algorithm string
key []byte
nonce []byte
associatedData []byte
}
type test struct {
name string
args args
wantCiphertext []byte
wantTag []byte
wantErr error
}
tests := []test{
{
name: "key size mismatch",
args: args{
algorithm: Algorithm_A128GCM,
key: []byte{0x00, 0x01},
nonce: mustDecodeHexString("cafebabefacedbaddecaf888"),
plaintext: mustDecodeHexString("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255"),
},
wantErr: ErrKeyTypeMismatch,
},
{
name: "nonce size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
nonce: []byte{0x00, 0x01},
plaintext: mustDecodeHexString("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255"),
},
wantErr: ErrInvalidNonce,
},
}
// Test vectors from NIST publication SP800-38d
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-gcm") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
nonce: mustDecodeHexString(v.Nonce),
plaintext: mustDecodeHexString(v.Plaintext),
associatedData: mustDecodeHexString(v.AssociatedData),
},
wantCiphertext: mustDecodeHexString(v.Ciphertext),
wantTag: mustDecodeHexString(v.Tag),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCiphertext, gotTag, err := encryptSymmetricAESGCM(tt.args.plaintext, tt.args.algorithm, tt.args.key, tt.args.nonce, tt.args.associatedData)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("encryptSymmetricAESGCM() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotCiphertext, tt.wantCiphertext) {
t.Errorf("encryptSymmetricAESGCM() gotCiphertext = %v, want %v", gotCiphertext, tt.wantCiphertext)
}
if !reflect.DeepEqual(gotTag, tt.wantTag) {
t.Errorf("encryptSymmetricAESGCM() gotTag = %v, want %v", gotTag, tt.wantTag)
}
})
}
}
func TestDecryptSymmetricAESGCM(t *testing.T) {
type args struct {
ciphertext []byte
algorithm string
key []byte
nonce []byte
tag []byte
associatedData []byte
}
type test struct {
name string
args args
wantPlaintext []byte
wantErr error
}
tests := []test{
{
name: "key size mismatch",
args: args{
algorithm: Algorithm_A128GCM,
key: []byte{0x00, 0x01},
nonce: mustDecodeHexString("cafebabefacedbaddecaf888"),
ciphertext: mustDecodeHexString("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985"),
tag: mustDecodeHexString("4d5c2af327cd64a62cf35abd2ba6fab4"),
},
wantErr: ErrKeyTypeMismatch,
},
{
name: "nonce size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
nonce: []byte{0x00, 0x01},
ciphertext: mustDecodeHexString("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985"),
tag: mustDecodeHexString("4d5c2af327cd64a62cf35abd2ba6fab4"),
},
wantErr: ErrInvalidNonce,
},
{
name: "tag size mismatch",
args: args{
algorithm: Algorithm_A128CBC,
key: mustDecodeHexString("2b7e151628aed2a6abf7158809cf4f3c"),
nonce: mustDecodeHexString("cafebabefacedbaddecaf888"),
ciphertext: mustDecodeHexString("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985"),
tag: []byte{0x00, 0x01},
},
wantErr: ErrInvalidTag,
},
}
// Test vectors from NIST publication SP800-38d
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-gcm") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
nonce: mustDecodeHexString(v.Nonce),
ciphertext: mustDecodeHexString(v.Ciphertext),
tag: mustDecodeHexString(v.Tag),
associatedData: mustDecodeHexString(v.AssociatedData),
},
wantPlaintext: mustDecodeHexString(v.Plaintext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPlaintext, err := decryptSymmetricAESGCM(tt.args.ciphertext, tt.args.algorithm, tt.args.key, tt.args.nonce, tt.args.tag, tt.args.associatedData)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("decryptSymmetricAESGCM() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotPlaintext, tt.wantPlaintext) && len(gotPlaintext) != 0 && len(tt.wantPlaintext) != 0 {
t.Errorf("decryptSymmetricAESGCM() = %v, want %v", gotPlaintext, tt.wantPlaintext)
}
})
}
}
func TestEncryptSymmetricAESKW(t *testing.T) {
type args struct {
plaintext []byte
algorithm string
key []byte
}
type test struct {
name string
args args
wantCiphertext []byte
wantErr error
}
tests := []test{}
// Test cases from RFC3394
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-kw") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
plaintext: mustDecodeHexString(v.Plaintext),
},
wantCiphertext: mustDecodeHexString(v.Ciphertext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCiphertext, err := encryptSymmetricAESKW(tt.args.plaintext, tt.args.algorithm, tt.args.key)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("encryptSymmetricAESKW() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotCiphertext, tt.wantCiphertext) {
t.Errorf("encryptSymmetricAESKW() = %v, want %v", gotCiphertext, tt.wantCiphertext)
}
})
}
}
func TestDecryptSymmetricAESKW(t *testing.T) {
type args struct {
ciphertext []byte
algorithm string
key []byte
}
type test struct {
name string
args args
wantPlaintext []byte
wantErr error
}
tests := []test{}
// Test cases from RFC3394
for _, v := range readTestVectors("symmetric-test-vectors.json", "aes-kw") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
ciphertext: mustDecodeHexString(v.Ciphertext),
},
wantPlaintext: mustDecodeHexString(v.Plaintext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPlaintext, err := decryptSymmetricAESKW(tt.args.ciphertext, tt.args.algorithm, tt.args.key)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("decryptSymmetricAESKW() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotPlaintext, tt.wantPlaintext) {
t.Errorf("decryptSymmetricAESKW() = %v, want %v", gotPlaintext, tt.wantPlaintext)
}
})
}
}
func TestEncryptSymmetricChaCha20Poly1305(t *testing.T) {
type args struct {
plaintext []byte
algorithm string
key []byte
nonce []byte
associatedData []byte
}
type test struct {
name string
args args
wantCiphertext []byte
wantTag []byte
wantErr error
}
tests := []test{}
for _, v := range readTestVectors("symmetric-test-vectors.json", "chacha20-poly1305") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
nonce: mustDecodeHexString(v.Nonce),
plaintext: mustDecodeHexString(v.Plaintext),
associatedData: mustDecodeHexString(v.AssociatedData),
},
wantCiphertext: mustDecodeHexString(v.Ciphertext),
wantTag: mustDecodeHexString(v.Tag),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCiphertext, gotTag, err := encryptSymmetricChaCha20Poly1305(tt.args.plaintext, tt.args.algorithm, tt.args.key, tt.args.nonce, tt.args.associatedData)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("encryptSymmetricChaCha20Poly1305() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotCiphertext, tt.wantCiphertext) {
t.Errorf("encryptSymmetricChaCha20Poly1305() gotCiphertext = %v, want %v", gotCiphertext, tt.wantCiphertext)
}
if !reflect.DeepEqual(gotTag, tt.wantTag) {
t.Errorf("encryptSymmetricChaCha20Poly1305() gotTag = %v, want %v", gotTag, tt.wantTag)
}
})
}
}
func TestDecryptSymmetricChaCha20Poly1305(t *testing.T) {
type args struct {
ciphertext []byte
algorithm string
key []byte
nonce []byte
tag []byte
associatedData []byte
}
type test struct {
name string
args args
wantPlaintext []byte
wantErr error
}
tests := []test{}
for _, v := range readTestVectors("symmetric-test-vectors.json", "chacha20-poly1305") {
tests = append(tests, test{
name: v.Name,
args: args{
algorithm: v.Algorithm,
key: mustDecodeHexString(v.Key),
nonce: mustDecodeHexString(v.Nonce),
ciphertext: mustDecodeHexString(v.Ciphertext),
tag: mustDecodeHexString(v.Tag),
associatedData: mustDecodeHexString(v.AssociatedData),
},
wantPlaintext: mustDecodeHexString(v.Plaintext),
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPlaintext, err := decryptSymmetricChaCha20Poly1305(tt.args.ciphertext, tt.args.algorithm, tt.args.key, tt.args.nonce, tt.args.tag, tt.args.associatedData)
if ((err != nil) != (tt.wantErr != nil)) ||
(err != nil && !errors.Is(err, tt.wantErr)) {
t.Errorf("decryptSymmetricChaCha20Poly1305() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotPlaintext, tt.wantPlaintext) && len(gotPlaintext) != 0 && len(tt.wantPlaintext) != 0 {
t.Errorf("decryptSymmetricChaCha20Poly1305() = %v, want %v", gotPlaintext, tt.wantPlaintext)
}
})
}
}
type testVector struct {
Name string `json:"name"`
Algorithm string `json:"algorithm"`
Key string `json:"key"`
Nonce string `json:"nonce"`
AssociatedData string `json:"associatedData"`
Plaintext string `json:"plaintext"`
Ciphertext string `json:"ciphertext"`
Tag string `json:"tag"`
}
func readTestVectors(fileName string, vectorsName string) []testVector {
f, err := os.Open(fileName)
if err != nil {
panic(err)
}
defer f.Close()
read := map[string][]testVector{}
err = json.NewDecoder(f).Decode(&read)
if err != nil {
panic(err)
}
return read[vectorsName]
}
func mustDecodeHexString(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}

139
crypto/testdata_test.go Normal file
View File

@ -0,0 +1,139 @@
/*
Copyright 2022 The Dapr 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 crypto
const (
privateKeyRSAPKCS1 = `-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA3I2mdIK4mRRu+ywMrYjUZzBxt0NlAVLrMhGlaJsby7PWTMiL
pZVip4SBD9GwnCU0TGFD7k2+7tfs0y9U6WV7MwgCjc9m/DUUGbE+kKjEU7JYkLzY
lndys+6xuhD4Jf1hu9AZVdfXftpWSy/NNg6fVwTH4nckOAbOSL1hXToOYWQcDDW9
5Rhw3U4z04PqssEpRKn5KGBuTahNNNiZcWns99pChpLTxgdm93LjMBI1KCGBpOaz
7fcQJ9V3c6rSwMKyY3IPm1LwS6PIs7xb2ZJ0Eb8A6MtCkGhgNsodpkxhqKbqtxI+
KqTuZy9g4jb8WKjJq9lB9q+HPHoQqIEDom6P8wIDAQABAoIBAQCwSSxWDjI8cR+v
9WqAvlB/1jHeeKjgIQXK9F3QucHC1RCiON2d0USUfw2ltkZC2F3v+wKQFGZbhA1Q
U5G8KBgMFwfrj25Sgp55VrmaaLrb5z5xGfDIe9sbxNsbzPp7awbWVqQZgDdHtn6p
BCEzZwanSDP4aKBzg8UYUP7kxEXAQwc/Z/L7U8SnZ0oGJPeyXbKjgiMw9FYxaHV7
/bGe8VdhIYLHX7WrIP9WkIKkvv6S5NYJkU7HwKr8SlmJfui8NUERvQmWtPeytcAC
mQQTjZhgWeXdN6OWBk7LTSC/up77pYHXuUEz/b94V3mWJ0nNVLiuInFloE0l5bSy
D9K6xgARAoGBAPSzprPH3dBVcIdgsknMQVs+T4O4FbZQpuqg9mSg2S/OBQw/wVcs
uS0JvZVeJE3BN08lh+nR3hpieIN1habmOMbrLlPK3VVDr/naXC6FuICZ0OLGqinY
t7lOU5s6gqr8zV0qBAr8uQD1yvi4dNXOX4K5nkc2FBCTdoYMcWi+hyQZAoGBAOa8
kSiac7GQ9gdcW521HkS04wUajiKhbFbhdJTIu0jGduCLCiH2vQngQnxiBZSMBnvo
yG6My9payLMN0N42R3MCFavatbPb6GXluGe3MpQgbPS6udffW+fuvkLngyuBZLIO
bkLA2NiZXmppudy+AjCI5gUWlRFDFu40X8TUCXXrAoGBAL2/bR3tdugsyJSBe97T
3z4kpD3sihGUmspHxKglPiTeqXVlHsQ9bt4otnTTymzszbm2zZyXW5W0E+VERT41
OYOuLH9XSKfsU9H37BE7KeW24I5pJNxMK7MqtJQjG94BKDH8+aOXctH2xsx1JEwC
undA/iYUHhz4bCjoDyz8yQhJAoGBAKZE1+2Ebz5TkZb4TOS2cVtdZgJ1glseK63n
xVS52Yu1yFBCoUicPtjxC7gKAq9qv/lmY5iWSQfOR7YPjKW0Q4Ryyh8O+cTkAPH8
ugezEfV/8EPikq0mvVnCxsP7hKBDG8bbc0UTJq/exRS1n0OcgINtAyRDg6PuzoVU
pHZ3vWGFAoGAc5Um3YYkhh2QScQBy5+kumH40LhFFy2ETznWEp0tS2NwmTfTm/Nl
Sg+Ct2nOw93cIhwDjWyoilkIapuuX2obY+sUc3kj2ugU+hONfuBStsF020IPP1sk
A9okIZVbz8ycqcjaBiNc4+TeiXED1K7bV9Kg+A9lxDxfGRybJ1/ECWA=
-----END RSA PRIVATE KEY-----
`
privateKeyRSAPKCS8 = `-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcjaZ0griZFG77
LAytiNRnMHG3Q2UBUusyEaVomxvLs9ZMyIullWKnhIEP0bCcJTRMYUPuTb7u1+zT
L1TpZXszCAKNz2b8NRQZsT6QqMRTsliQvNiWd3Kz7rG6EPgl/WG70BlV19d+2lZL
L802Dp9XBMfidyQ4Bs5IvWFdOg5hZBwMNb3lGHDdTjPTg+qywSlEqfkoYG5NqE00
2Jlxaez32kKGktPGB2b3cuMwEjUoIYGk5rPt9xAn1XdzqtLAwrJjcg+bUvBLo8iz
vFvZknQRvwDoy0KQaGA2yh2mTGGopuq3Ej4qpO5nL2DiNvxYqMmr2UH2r4c8ehCo
gQOibo/zAgMBAAECggEBALBJLFYOMjxxH6/1aoC+UH/WMd54qOAhBcr0XdC5wcLV
EKI43Z3RRJR/DaW2RkLYXe/7ApAUZluEDVBTkbwoGAwXB+uPblKCnnlWuZpoutvn
PnEZ8Mh72xvE2xvM+ntrBtZWpBmAN0e2fqkEITNnBqdIM/hooHODxRhQ/uTERcBD
Bz9n8vtTxKdnSgYk97JdsqOCIzD0VjFodXv9sZ7xV2Ehgsdftasg/1aQgqS+/pLk
1gmRTsfAqvxKWYl+6Lw1QRG9CZa097K1wAKZBBONmGBZ5d03o5YGTstNIL+6nvul
gde5QTP9v3hXeZYnSc1UuK4icWWgTSXltLIP0rrGABECgYEA9LOms8fd0FVwh2Cy
ScxBWz5Pg7gVtlCm6qD2ZKDZL84FDD/BVyy5LQm9lV4kTcE3TyWH6dHeGmJ4g3WF
puY4xusuU8rdVUOv+dpcLoW4gJnQ4saqKdi3uU5TmzqCqvzNXSoECvy5APXK+Lh0
1c5fgrmeRzYUEJN2hgxxaL6HJBkCgYEA5ryRKJpzsZD2B1xbnbUeRLTjBRqOIqFs
VuF0lMi7SMZ24IsKIfa9CeBCfGIFlIwGe+jIbozL2lrIsw3Q3jZHcwIVq9q1s9vo
ZeW4Z7cylCBs9Lq5199b5+6+QueDK4Fksg5uQsDY2Jleamm53L4CMIjmBRaVEUMW
7jRfxNQJdesCgYEAvb9tHe126CzIlIF73tPfPiSkPeyKEZSaykfEqCU+JN6pdWUe
xD1u3ii2dNPKbOzNubbNnJdblbQT5URFPjU5g64sf1dIp+xT0ffsETsp5bbgjmkk
3Ewrsyq0lCMb3gEoMfz5o5dy0fbGzHUkTAK6d0D+JhQeHPhsKOgPLPzJCEkCgYEA
pkTX7YRvPlORlvhM5LZxW11mAnWCWx4rrefFVLnZi7XIUEKhSJw+2PELuAoCr2q/
+WZjmJZJB85Htg+MpbRDhHLKHw75xOQA8fy6B7MR9X/wQ+KSrSa9WcLGw/uEoEMb
xttzRRMmr97FFLWfQ5yAg20DJEODo+7OhVSkdne9YYUCgYBzlSbdhiSGHZBJxAHL
n6S6YfjQuEUXLYRPOdYSnS1LY3CZN9Ob82VKD4K3ac7D3dwiHAONbKiKWQhqm65f
ahtj6xRzeSPa6BT6E41+4FK2wXTbQg8/WyQD2iQhlVvPzJypyNoGI1zj5N6JcQPU
rttX0qD4D2XEPF8ZHJsnX8QJYA==
-----END PRIVATE KEY-----
`
privateKeyRSAJSON = `{"kty":"RSA","n":"3I2mdIK4mRRu-ywMrYjUZzBxt0NlAVLrMhGlaJsby7PWTMiLpZVip4SBD9GwnCU0TGFD7k2-7tfs0y9U6WV7MwgCjc9m_DUUGbE-kKjEU7JYkLzYlndys-6xuhD4Jf1hu9AZVdfXftpWSy_NNg6fVwTH4nckOAbOSL1hXToOYWQcDDW95Rhw3U4z04PqssEpRKn5KGBuTahNNNiZcWns99pChpLTxgdm93LjMBI1KCGBpOaz7fcQJ9V3c6rSwMKyY3IPm1LwS6PIs7xb2ZJ0Eb8A6MtCkGhgNsodpkxhqKbqtxI-KqTuZy9g4jb8WKjJq9lB9q-HPHoQqIEDom6P8w","e":"AQAB","d":"sEksVg4yPHEfr_VqgL5Qf9Yx3nio4CEFyvRd0LnBwtUQojjdndFElH8NpbZGQthd7_sCkBRmW4QNUFORvCgYDBcH649uUoKeeVa5mmi62-c-cRnwyHvbG8TbG8z6e2sG1lakGYA3R7Z-qQQhM2cGp0gz-Gigc4PFGFD-5MRFwEMHP2fy-1PEp2dKBiT3sl2yo4IjMPRWMWh1e_2xnvFXYSGCx1-1qyD_VpCCpL7-kuTWCZFOx8Cq_EpZiX7ovDVBEb0JlrT3srXAApkEE42YYFnl3TejlgZOy00gv7qe-6WB17lBM_2_eFd5lidJzVS4riJxZaBNJeW0sg_SusYAEQ","p":"9LOms8fd0FVwh2CyScxBWz5Pg7gVtlCm6qD2ZKDZL84FDD_BVyy5LQm9lV4kTcE3TyWH6dHeGmJ4g3WFpuY4xusuU8rdVUOv-dpcLoW4gJnQ4saqKdi3uU5TmzqCqvzNXSoECvy5APXK-Lh01c5fgrmeRzYUEJN2hgxxaL6HJBk","q":"5ryRKJpzsZD2B1xbnbUeRLTjBRqOIqFsVuF0lMi7SMZ24IsKIfa9CeBCfGIFlIwGe-jIbozL2lrIsw3Q3jZHcwIVq9q1s9voZeW4Z7cylCBs9Lq5199b5-6-QueDK4Fksg5uQsDY2Jleamm53L4CMIjmBRaVEUMW7jRfxNQJdes"}`
)
const (
publicKeyRSAPKCS1 = `-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA3I2mdIK4mRRu+ywMrYjUZzBxt0NlAVLrMhGlaJsby7PWTMiLpZVi
p4SBD9GwnCU0TGFD7k2+7tfs0y9U6WV7MwgCjc9m/DUUGbE+kKjEU7JYkLzYlndy
s+6xuhD4Jf1hu9AZVdfXftpWSy/NNg6fVwTH4nckOAbOSL1hXToOYWQcDDW95Rhw
3U4z04PqssEpRKn5KGBuTahNNNiZcWns99pChpLTxgdm93LjMBI1KCGBpOaz7fcQ
J9V3c6rSwMKyY3IPm1LwS6PIs7xb2ZJ0Eb8A6MtCkGhgNsodpkxhqKbqtxI+KqTu
Zy9g4jb8WKjJq9lB9q+HPHoQqIEDom6P8wIDAQAB
-----END RSA PUBLIC KEY-----
`
publicKeyRSAPKIX = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3I2mdIK4mRRu+ywMrYjU
ZzBxt0NlAVLrMhGlaJsby7PWTMiLpZVip4SBD9GwnCU0TGFD7k2+7tfs0y9U6WV7
MwgCjc9m/DUUGbE+kKjEU7JYkLzYlndys+6xuhD4Jf1hu9AZVdfXftpWSy/NNg6f
VwTH4nckOAbOSL1hXToOYWQcDDW95Rhw3U4z04PqssEpRKn5KGBuTahNNNiZcWns
99pChpLTxgdm93LjMBI1KCGBpOaz7fcQJ9V3c6rSwMKyY3IPm1LwS6PIs7xb2ZJ0
Eb8A6MtCkGhgNsodpkxhqKbqtxI+KqTuZy9g4jb8WKjJq9lB9q+HPHoQqIEDom6P
8wIDAQAB
-----END PUBLIC KEY-----
`
publicKeyRSAJSON = `{"kty":"RSA","n":"3I2mdIK4mRRu-ywMrYjUZzBxt0NlAVLrMhGlaJsby7PWTMiLpZVip4SBD9GwnCU0TGFD7k2-7tfs0y9U6WV7MwgCjc9m_DUUGbE-kKjEU7JYkLzYlndys-6xuhD4Jf1hu9AZVdfXftpWSy_NNg6fVwTH4nckOAbOSL1hXToOYWQcDDW95Rhw3U4z04PqssEpRKn5KGBuTahNNNiZcWns99pChpLTxgdm93LjMBI1KCGBpOaz7fcQJ9V3c6rSwMKyY3IPm1LwS6PIs7xb2ZJ0Eb8A6MtCkGhgNsodpkxhqKbqtxI-KqTuZy9g4jb8WKjJq9lB9q-HPHoQqIEDom6P8w","e":"AQAB"}`
)
const (
publicKeyEd25519PKIX = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAzSjVm3Fj1chMFGcRoE/e6ft9xvAs3x1CVn6PVS+x1e8=
-----END PUBLIC KEY-----
`
publicKeyEd25519JSON = `{"kid":"ed25519-public","kty":"OKP","crv":"Ed25519","alg":"EdDSA","x":"zSjVm3Fj1chMFGcRoE_e6ft9xvAs3x1CVn6PVS-x1e8"}`
)
const (
privateKeyEd25519PKCS8 = `-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIFnPHZMGmIQ4W8kx53JzZEDPVmTQwdI7uM5hxDzWIIbU
-----END PRIVATE KEY-----
`
privateKeyEd25519JSON = `{"kid":"ed25519-private","kty":"OKP","crv":"Ed25519","alg":"EdDSA","x":"zSjVm3Fj1chMFGcRoE_e6ft9xvAs3x1CVn6PVS-x1e8","d":"Wc8dkwaYhDhbyTHncnNkQM9WZNDB0ju4zmHEPNYghtQ"}`
)
const (
publicKeyP256PKIX = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUMn1c2ioMNi2DqvC8hdBVUERFZ97
eVFsNVcQIgR0Hsq5PVrQ/dQ4uI5u97b6k4wXHYFXMvPmsW1T6qZAE9bB3Q==
-----END PUBLIC KEY-----
`
publicKeyP256JSON = `{"kty":"EC","crv":"P-256","x":"UMn1c2ioMNi2DqvC8hdBVUERFZ97eVFsNVcQIgR0Hso","y":"uT1a0P3UOLiObve2-pOMFx2BVzLz5rFtU-qmQBPWwd0"},`
)
const (
privateKeyP256EC = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOcFe4Q6ardS97ml2tV4+194nmlfQPh8o9ir/qsacEozoAoGCCqGSM49
AwEHoUQDQgAEUMn1c2ioMNi2DqvC8hdBVUERFZ97eVFsNVcQIgR0Hsq5PVrQ/dQ4
uI5u97b6k4wXHYFXMvPmsW1T6qZAE9bB3Q==
-----END EC PRIVATE KEY-----
`
privateKeyP256PKCS8 = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5wV7hDpqt1L3uaXa
1Xj7X3ieaV9A+Hyj2Kv+qxpwSjOhRANCAARQyfVzaKgw2LYOq8LyF0FVQREVn3t5
UWw1VxAiBHQeyrk9WtD91Di4jm73tvqTjBcdgVcy8+axbVPqpkAT1sHd
-----END PRIVATE KEY-----
`
privateKeyP256JSON = `{"kty":"EC","crv":"P-256","d":"5wV7hDpqt1L3uaXa1Xj7X3ieaV9A-Hyj2Kv-qxpwSjM","x":"UMn1c2ioMNi2DqvC8hdBVUERFZ97eVFsNVcQIgR0Hso","y":"uT1a0P3UOLiObve2-pOMFx2BVzLz5rFtU-qmQBPWwd0"},`
)

17
go.mod
View File

@ -1,20 +1,29 @@
module github.com/dapr/kit
go 1.19
go 1.20
require (
github.com/benbjohnson/clock v1.3.0
github.com/cenkalti/backoff/v4 v4.2.0
github.com/fsnotify/fsnotify v1.6.0
github.com/lestrrat-go/jwx/v2 v2.0.8
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.1
golang.org/x/exp v0.0.0-20230105000112-eab7a2c85304
github.com/stretchr/testify v1.8.2
golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

43
go.sum
View File

@ -5,8 +5,25 @@ github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.0.8 h1:jCFT8oc0hEDVjgUgsBy1F9cbjsjAVZSXNi7JaU9HR/Q=
github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY=
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -16,21 +33,29 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ2XTedFPGmbEVh8cdoc=
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230105000112-eab7a2c85304 h1:YUqj+XKtfrn3kXjFIiZ8jwKROD7ioAOOHUuo3ZZ2opc=
golang.org/x/exp v0.0.0-20230105000112-eab7a2c85304/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=