mirror of https://github.com/dapr/kit.git
Add Dapr crypto library to dapr/kit (#38)
Signed-off-by: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com>
This commit is contained in:
parent
fcb09958bf
commit
39814af4c7
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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), ""))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
17
go.mod
|
@ -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
43
go.sum
|
@ -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=
|
||||
|
|
Loading…
Reference in New Issue