8000 Add fixes for processing InTune SCEP messages within our PKCS7 library by stevendpclark · Pull Request #30595 · hashicorp/vault · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add fixes for processing InTune SCEP messages within our PKCS7 library #30595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions helper/pkcs7/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
Expand All @@ -33,15 +34,70 @@
switch pkey := pkey.(type) {
case *rsa.PrivateKey:
var contentKey []byte
contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, pkey, recipient.EncryptedKey)
if err != nil {
return nil, err
switch {
case recipient.KeyEncryptionAlgorithm.Algorithm.Equal(OIDEncryptionAlgorithmRSAOAEP):
oaepHash, err := parseRSAOaepParam(recipient.KeyEncryptionAlgorithm.Parameters)
if err != nil {
return nil, fmt.Errorf("failed to parse RSA OAEP parameter: %w", err)
}
contentKey, err = rsa.DecryptOAEP(oaepHash.New(), rand.Reader, pkey, recipient.EncryptedKey, []byte{})
if err != nil {
return nil, fmt.Errorf("failed OAEP decryption of content key: %w", err)
}
default:
var err error
contentKey, err = rsa.DecryptPKCS1v15(rand.Reader, pkey, recipient.EncryptedKey)
if err != nil {
return nil, fmt.Errorf("failed PKCS1v15 decryption of content key: %w", err)
}
}
return data.EncryptedContentInfo.decrypt(contentKey)
}
return nil, ErrUnsupportedAlgorithm
}

// parseRSAOaepParam parses the RSA-OAEP parameters as defined in RFC 3447,
// at the moment we don't support the PSourceAlgorithm (label attribute)
func parseRSAOaepParam(parameters asn1.RawValue) (crypto.Hash, error) {
if len(parameters.Bytes) == 0 {
// Based on RFC 3447 Section A.2.1, if the parameter is absent, we assume SHA-1.
return crypto.SHA1, nil
}

type rsaOAEPParams struct {
HashAlgorithm pkix.AlgorithmIdentifier `asn1:"tag:0,optional,explicit"`
MaskGenAlgorithm pkix.AlgorithmIdentifier `asn1:"tag:1,optional,explicit"`
PSourceAlgorithm pkix.AlgorithmIdentifier `asn1:"tag:2,optional,explicit"`
}

var params rsaOAEPParams
_, err := asn1.Unmarshal(parameters.FullBytes, &params)
if err != nil {
return 0, errors.New("failed to parse RSA-OAEP parameters")
}

// Parse the hash algorithm
hashOID := params.HashAlgorithm.Algorithm
var hash crypto.Hash
switch {
case hashOID == nil:
// If the hash algorithm is not specified, we assume SHA-1.
hash = crypto.SHA1
case hashOID.Equal(OIDDigestAlgorithmSHA1):
hash = crypto.SHA1
case hashOID.Equal(OIDDigestAlgori 8000 thmSHA256):
hash = crypto.SHA256
case hashOID.Equal(OIDDigestAlgorithmSHA384):
hash = crypto.SHA384
case hashOID.Equal(OIDDigestAlgorithmSHA512):
hash = crypto.SHA512
default:
return 0, fmt.Errorf("unsupported hash algorithm in RSA-OAEP parameters: %s", hashOID)
}

return hash, nil
}

// DecryptUsingPSK decrypts encrypted data using caller provided
// pre-shared secret
func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) {
Expand Down
51 changes: 51 additions & 0 deletions helper/pkcs7/decrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package pkcs7

import (
"bytes"
"crypto"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"testing"

"github.com/stretchr/testify/require"
)

func TestDecrypt(t *testing.T) {
Expand Down Expand Up @@ -59,3 +65,48 @@ o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b
dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy
KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA==
-----END PRIVATE KEY-----`

// TestRsaOAEPKeyIdentifier validates that we can parse the RSA-OAEP algo
// identifier's parameters correctly. See RFC 3447 for the definition
func TestRsaOAEPKeyIdentifier(t *testing.T) {
t.Parallel()

t.Run("no params", func(t *testing.T) {
hash, err := parseRSAOaepParam(asn1.NullRawValue)
require.NoError(t, err)
require.Equal(t, hash, crypto.SHA1)
})

t.Run("only pSource parameter set", func(t *testing.T) {
// This is the base64 encoded key identifier that only has pSource parameter set.
// Example pulled from https://github.com/pnwamk/mbedtls/blob/rsaes-oaep/example_tpm_cert.der linked
// from https://github.com/Mbed-TLS/mbedtls/issues/1015
base64KeyId := "MCIGCSqGSIb3DQEBBzAVohMwEQYJKoZIhvcNAQEJBARUQ1BB"
rawKeyId, err := base64.StdEncoding.DecodeString(base64KeyId)
require.NoError(t, err)

var keyId pkix.AlgorithmIdentifier
_, err = asn1.Unmarshal(rawKeyId, &keyId)
require.NoError(t, err)

hash, err := parseRSAOaepParam(keyId.Parameters)
require.NoError(t, err)
require.Equal(t, hash, crypto.SHA1)
})

t.Run("full parameters", func(t *testing.T) {
// This is the base64 encoded key identifier that has all parameters set, example pulled from
// https://stackoverflow.com/questions/22194359/rsaes-oaep-certificate-public-key-0-bits
base64KeyId := "MFIGCSqGSIb3DQEBBzBFoA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAIBBQCiFDASBgkqhkiG9w0BAQkEBVRDUEEA"
rawKeyId, err := base64.StdEncoding.DecodeString(base64KeyId)
require.NoError(t, err)

var keyId pkix.AlgorithmIdentifier
_, err = asn1.Unmarshal(rawKeyId, &keyId)
require.NoError(t, err)

hash, err := parseRSAOaepParam(keyId.Parameters)
require.NoError(t, err)
require.Equal(t, hash, crypto.SHA256)
})
}
8000
15 changes: 14 additions & 1 deletion helper/pkcs7/pkcs7.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"errors"
"fmt"
"sort"
"strings"
)

// PKCS7 Represents a PKCS7 structure
Expand Down Expand Up @@ -63,6 +64,7 @@ var (

// Signature Algorithms
OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
OIDEncryptionAlgorithmRSAOAEP = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 7}
OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
Expand Down Expand Up @@ -214,7 +216,18 @@ func (raw rawCertificates) Parse() ([]*x509.Certificate, error) {
return nil, err
}

return x509.ParseCertificates(val.Bytes)
stdlibCerts, err := x509.ParseCertificates(val.Bytes)
if err == nil {
// If the parsing was successful, return the stdlib certs and don't worry about
// our workarounds
return stdlibCerts, nil
}

if !strings.Contains(err.Error(), "authority key identifier incorrectly marked critical") {
return nil, err
}

return CustomParseX509Certificates(val.Bytes)
}

func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool {
Expand Down
1 change: 1 addition & 0 deletions helper/pkcs7/testdata/intune-pkcs7-sample.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
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 59D8 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
Loading
Loading
0