Security context
Critical· 9.1GHSA-pfmw-vj74-ph8g CVE-2021-43998CWE-732Published Dec 2, 2021

HashiCorp Vault Incorrect Permission Assignment for Critical Resource

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

0.11.0 → fixed in 1.7.61.8.0 → fixed in 1.8.5

Details

HashiCorp Vault and Vault Enterprise 0.11.0 up to 1.7.5 and 1.8.4 templated ACL policies would always match the first-created entity alias if multiple entity aliases exist for a specified entity and mount combination, potentially resulting in incorrect policy enforcement. Fixed in Vault and Vault Enterprise 1.7.6, 1.8.5, and 1.9.0.

The fix

Release delta 1.8.0 → 1.8.5 (contains the fix)

· Jul 30, 2021, 12:18 AM+4769958compare
CHANGELOG.md+69 28
@@ -1,5 +1,24 @@
-## 1.8.0-rc2
-### July 15th, 2021
+## 1.8.1
+### August 5th, 2021
+
+CHANGES:
+
+* go: Update go version to 1.16.6 [[GH-12245](https://github.com/hashicorp/vault/pull/12245)]
+
+IMPROVEMENTS:
+
+* serviceregistration: add `external-source: "vault"` metadata value for Consul registration. [[GH-12163](https://github.com/hashicorp/vault/pull/12163)]
+
+BUG FIXES:
+
+* auth/aws: Remove warning stating AWS Token TTL will be capped by the Default Lease TTL. [[GH-12026](https://github.com/hashicorp/vault/pull/12026)]
+* auth/jwt: Fixes OIDC auth from the Vault UI when using `form_post` as the `oidc_response_mode`. [[GH-12258](https://github.com/hashicorp/vault/pull/12258)]
+* core (enterprise): Disallow autogenerated licenses to be used in diagnose even when config is specified
+* core: fix byte printing for diagnose disk checks [[GH-12229](https://github.com/hashicorp/vault/pull/12229)]
+* identity: do not allow a role's token_ttl to be longer than the signing key's verification_ttl [[GH-12151](https://github.com/hashicorp/vault/pull/12151)]
+
+## 1.8.0
+### July 28th, 2021
CHANGES:
@@ -12,23 +31,17 @@ be also set to `true`. Otherwise, the template engine will log an error but then
restart its internal runner. [[GH-11775](https://github.com/hashicorp/vault/pull/11775)]
* agent: Update to use IAM Service Account Credentials endpoint for signing JWTs
when using GCP Auto-Auth method [[GH-11473](https://github.com/hashicorp/vault/pull/11473)]
-* go: Update to Go 1.16.5 [[GH-11802](https://github.com/hashicorp/vault/pull/11802)]
+* core (enterprise): License/EULA changes that ensure the presence of a valid HashiCorp license to
+start Vault. More information is available in the [Vault License FAQ](https://www.vaultproject.io/docs/enterprise/license/faqs)
FEATURES:
+* **GCP Secrets Engine Static Accounts**: Adds ability to use existing service accounts for generation
+ of service account keys and access tokens. [[GH-12023](https://github.com/hashicorp/vault/pull/12023)]
+* **Key Management Secrets Engine (Enterprise)**: Adds general availability for distributing and managing keys in AWS KMS. [[GH-11958](https://github.com/hashicorp/vault/pull/11958)]
+* **License Autoloading (Enterprise)**: Licenses may now be automatically loaded from the environment or disk.
* **MySQL Database UI**: The UI now supports adding and editing MySQL connections in the database secret engine [[GH-11532](https://github.com/hashicorp/vault/pull/11532)]
-* cli/api: Add lease lookup command [[GH-11129](https://github.com/hashicorp/vault/pull/11129)]
-* core: Add controlled capabilities to control group policy stanza
-* secret/rabbitmq: Add ability to customize dynamic usernames [[GH-11899](https://github.com/hashicorp/vault/pull/11899)]
-* secrets/database/elasticsearch: Add ability to customize dynamic usernames [[GH-11957](https://github.com/hashicorp/vault/pull/11957)]
-* secrets/database/influxdb: Add ability to customize dynamic usernames [[GH-11796](https://github.com/hashicorp/vault/pull/11796)]
-* secrets/database/mongodbatlas: Add ability to customize dynamic usernames [[GH-11956](https://github.com/hashicorp/vault/pull/11956)]
-* secrets/database/redshift: Add ability to customize dynamic usernames [[GH-12016](https://github.com/hashicorp/vault/pull/12016)]
-* secrets/database/snowflake: Add ability to customize dynamic usernames [[GH-11997](https://github.com/hashicorp/vault/pull/11997)]
-* secrets/gcp: Adds ability to use existing service accounts for generation of service account keys and access tokens. [[GH-12023](https://github.com/hashicorp/vault/pull/12023)]
-* secrets/keymgmt (enterprise): Adds general availability for distributing and managing keys in AWS KMS. [[GH-11958](https://github.com/hashicorp/vault/pull/11958)]
-* ssh: add support for templated values in SSH CA DefaultExtensions [[GH-11495](https://github.com/hashicorp/vault/pull/11495)]
-* ui: Add database secret engine support for MSSQL [[GH-11231](https://github.com/hashicorp/vault/pull/11231)]
+* **Vault Diagnose**: A new `vault operator` command to detect common issues with vault server setups.
IMPROVEMENTS:
@@ -36,24 +49,38 @@ IMPROVEMENTS:
* agent: Allow Agent auto auth to read symlinked JWT files [[GH-11502](https://github.com/hashicorp/vault/pull/11502)]
* api: Allow a leveled logger to be provided to `api.Client` through `SetLogger`. [[GH-11696](https://github.com/hashicorp/vault/pull/11696)]
* auth/aws: Underlying error included in validation failure message. [[GH-11638](https://github.com/hashicorp/vault/pull/11638)]
+* cli/api: Add lease lookup command [[GH-11129](https://github.com/hashicorp/vault/pull/11129)]
* core: Add `prefix_filter` to telemetry config [[GH-12025](https://github.com/hashicorp/vault/pull/12025)]
+* core: Add a darwin/arm64 binary release supporting the Apple M1 CPU [[GH-12071](https://github.com/hashicorp/vault/pull/12071)]
* core: Add a small (<1s) exponential backoff to failed TCP listener Accept failures. [[GH-11588](https://github.com/hashicorp/vault/pull/11588)]
+* core (enterprise): Add controlled capabilities to control group policy stanza
* core: Add metrics for standby node forwarding. [[GH-11366](https://github.com/hashicorp/vault/pull/11366)]
+* core: Add metrics to report if a node is a perf standby, if a node is a dr secondary or primary, and if a node is a perf secondary or primary. [[GH-11472](https://github.com/hashicorp/vault/pull/11472)]
* core: Send notifications to systemd on start, stop, and configuration reload. [[GH-11517](https://github.com/hashicorp/vault/pull/11517)]
* core: add irrevocable lease list and count apis [[GH-11607](https://github.com/hashicorp/vault/pull/11607)]
* core: allow arbitrary length stack traces upon receiving SIGUSR2 (was 32MB) [[GH-11364](https://github.com/hashicorp/vault/pull/11364)]
* db/cassandra: Added tls_server_name to specify server name for TLS validation [[GH-11820](https://github.com/hashicorp/vault/pull/11820)]
-* plugins/ad: Added rotate-role endpoint for manual service account password rotations [[GH-11942](https://github.com/hashicorp/vault/pull/11942)]
-* raft: Improve raft batch size selection [[GH-11907](https://github.com/hashicorp/vault/pull/11907)]
-* raft: change freelist type to map and set nofreelistsync to true [[GH-11895](https://github.com/hashicorp/vault/pull/11895)]
-* replication (enterprise): The log shipper is now memory
-as well as length bound, and length and size can be
-separately configured.
+* go: Update to Go 1.16.5 [[GH-11802](https://github.com/hashicorp/vault/pull/11802)]
+* replication: Delay evaluation of X-Vault-Index headers until merkle sync completes.
+* secrets/rabbitmq: Add ability to customize dynamic usernames [[GH-11899](https://github.com/hashicorp/vault/pull/11899)]
+* secrets/ad: Add `rotate-role` endpoint to allow rotations of service accounts. [[GH-11942](https://github.com/hashicorp/vault/pull/11942)]
+* secrets/aws: add IAM tagging support for iam_user roles [[GH-10953](https://github.com/hashicorp/vault/pull/10953)]
* secrets/aws: add ability to provide a role session name when generating STS credentials [[GH-11345](https://github.com/hashicorp/vault/pull/11345)]
+* secrets/database/elasticsearch: Add ability to customize dynamic usernames [[GH-11957](https://github.com/hashicorp/vault/pull/11957)]
+* secrets/database/influxdb: Add ability to customize dynamic usernames [[GH-11796](https://github.com/hashicorp/vault/pull/11796)]
* secrets/database/mongodb: Add ability to customize `SocketTimeout`, `ConnectTimeout`, and `ServerSelectionTimeout` [[GH-11600](https://github.com/hashicorp/vault/pull/11600)]
* secrets/database/mongodb: Increased throughput by allowing for multiple request threads to simultaneously update users in MongoDB [[GH-11600](https://github.com/hashicorp/vault/pull/11600)]
+* secrets/database/mongodbatlas: Adds the ability to customize username generation for dynamic users in MongoDB Atlas. [[GH-11956](https://github.com/hashicorp/vault/pull/11956)]
+* secrets/database/redshift: Add ability to customize dynamic usernames [[GH-12016](https://github.com/hashicorp/vault/pull/12016)]
+* secrets/database/snowflake: Add ability to customize dynamic usernames [[GH-11997](https://github.com/hashicorp/vault/pull/11997)]
+* ssh: add support for templated values in SSH CA DefaultExtensions [[GH-11495](https://github.com/hashicorp/vault/pull/11495)]
+* storage/raft: Improve raft batch size selection [[GH-11907](https://github.com/hashicorp/vault/pull/11907)]
+* storage/raft: change freelist type to map and set nofreelistsync to true [[GH-11895](https://github.com/hashicorp/vault/pull/11895)]
+* storage/raft: Switch to shared raft-boltdb library and add boltdb metrics [[GH-11269](https://github.com/hashicorp/vault/pull/11269)]
* storage/raft: Support autopilot for HA only raft storage. [[GH-11260](https://github.com/hashicorp/vault/pull/11260)]
+* storage/raft (enterprise): Enable Autopilot on DR secondary clusters
* ui: Add Validation to KV secret engine [[GH-11785](https://github.com/hashicorp/vault/pull/11785)]
+* ui: Add database secret engine support for MSSQL [[GH-11231](https://github.com/hashicorp/vault/pull/11231)]
* ui: Add push notification message when selecting okta auth. [[GH-11442](https://github.com/hashicorp/vault/pull/11442)]
* ui: Add regex validation to Transform Template pattern input [[GH-11586](https://github.com/hashicorp/vault/pull/11586)]
* ui: Add specific error message if unseal fails due to license [[GH-11705](https://github.com/hashicorp/vault/pull/11705)]
@@ -67,6 +94,7 @@ separately configured.
* ui: Update ember to latest LTS and upgrade UI dependencies [[GH-11447](https://github.com/hashicorp/vault/pull/11447)]
* ui: Update partials to components [[GH-11680](https://github.com/hashicorp/vault/pull/11680)]
* ui: Updated ivy code mirror component for consistency [[GH-11500](https://github.com/hashicorp/vault/pull/11500)]
+* ui: Updated node to v14, latest stable build [[GH-12049](https://github.com/hashicorp/vault/pull/12049)]
* ui: Updated search select component styling [[GH-11360](https://github.com/hashicorp/vault/pull/11360)]
* ui: add transform secrets engine to features list [[GH-12003](https://github.com/hashicorp/vault/pull/12003)]
* ui: add validations for duplicate path kv engine [[GH-11878](https://github.com/hashicorp/vault/pull/11878)]
@@ -75,25 +103,27 @@ separately configured.
DEPRECATIONS:
-* secrets/gcp: Deprecated the `/gcp/token/:roleset` and `/gcp/key/:roleset` paths for generating secrets for rolesets.
-Use `/gcp/roleset/:roleset/token` and `/gcp/roleset/:roleset/key` instead. [[GH-12023](https://github.com/hashicorp/vault/pull/12023)]
+* secrets/gcp: Deprecated the `/gcp/token/:roleset` and `/gcp/key/:roleset` paths for generating
+ secrets for rolesets. Use `/gcp/roleset/:roleset/token` and `/gcp/roleset/:roleset/key` instead. [[GH-12023](https://github.com/hashicorp/vault/pull/12023)]
BUG FIXES:
* activity: Omit wrapping tokens and control groups from client counts [[GH-11826](https://github.com/hashicorp/vault/pull/11826)]
* agent/cert: Fix issue where the API client on agent was not honoring certificate
-information from the auto-auth config map on renewals or retries. [[GH-11576](https://github.com/hashicorp/vault/pull/11576)]
+ information from the auto-auth config map on renewals or retries. [[GH-11576](https://github.com/hashicorp/vault/pull/11576)]
* agent/template: fix command shell quoting issue [[GH-11838](https://github.com/hashicorp/vault/pull/11838)]
* agent: Fixed agent templating to use configured tls servername values [[GH-11288](https://github.com/hashicorp/vault/pull/11288)]
* agent: fix timestamp format in log messages from the templating engine [[GH-11838](https://github.com/hashicorp/vault/pull/11838)]
* auth/approle: fixing dereference of nil pointer [[GH-11864](https://github.com/hashicorp/vault/pull/11864)]
* auth/jwt: Updates the [hashicorp/cap](https://github.com/hashicorp/cap) library to `v0.1.0` to
-bring in a verification key caching fix. [[GH-11784](https://github.com/hashicorp/vault/pull/11784)]
+ bring in a verification key caching fix. [[GH-11784](https://github.com/hashicorp/vault/pull/11784)]
+* auth/kubernetes: Fix AliasLookahead to correctly extract ServiceAccount UID when using ephemeral JWTs [[GH-12073](https://github.com/hashicorp/vault/pull/12073)]
* auth/ldap: Fix a bug where the LDAP auth method does not return the request_timeout configuration parameter on config read. [[GH-11975](https://github.com/hashicorp/vault/pull/11975)]
* cli: Add support for response wrapping in `vault list` and `vault kv list` with output format other than `table`. [[GH-12031](https://github.com/hashicorp/vault/pull/12031)]
* cli: vault delete and vault kv delete should support the same output options (e.g. -format) as vault write. [[GH-11992](https://github.com/hashicorp/vault/pull/11992)]
* core (enterprise): Fix orphan return value from auth methods executed on performance standby nodes.
* core (enterprise): Fix plugins mounted in namespaces being unable to use password policies [[GH-11596](https://github.com/hashicorp/vault/pull/11596)]
+* core (enterprise): serialize access to HSM entropy generation to avoid errors in concurrent key generation.
* core/metrics: Add generic KV mount support for vault.kv.secret.count telemetry metric [[GH-12020](https://github.com/hashicorp/vault/pull/12020)]
* core: Fix cleanup of storage entries from cubbyholes within namespaces. [[GH-11408](https://github.com/hashicorp/vault/pull/11408)]
* core: Fix edge cases in the configuration endpoint for barrier key autorotation. [[GH-11541](https://github.com/hashicorp/vault/pull/11541)]
@@ -105,6 +135,10 @@ bring in a verification key caching fix. [[GH-11784](https://github.com/hashicor
* identity: Use correct mount accessor when refreshing external group memberships. [[GH-11506](https://github.com/hashicorp/vault/pull/11506)]
* mongo-db: default username template now strips invalid '.' characters [[GH-11872](https://github.com/hashicorp/vault/pull/11872)]
* pki: Only remove revoked entry for certificates during tidy if they are past their NotAfter value [[GH-11367](https://github.com/hashicorp/vault/pull/11367)]
+* replication: Fix panic trying to update walState during identity group invalidation.
+* replication: Fix: mounts created within a namespace that was part of an Allow
+ filtering rule would not appear on performance secondary if created after rule
+ was defined.
* secret/pki: use case insensitive domain name comparison as per RFC1035 section 2.3.3
* secret: fix the bug where transit encrypt batch doesn't work with key_version [[GH-11628](https://github.com/hashicorp/vault/pull/11628)]
* secrets/ad: Forward all creds requests to active node [[GH-76](https://github.com/hashicorp/vault-plugin-secrets-ad/pull/76)] [[GH-11836](https://github.com/hashicorp/vault/pull/11836)]
@@ -112,20 +146,29 @@ bring in a verification key caching fix. [[GH-11784](https://github.com/hashicor
* secrets/database/cassandra: Fixed issue where the PEM parsing logic of `pem_bundle` and `pem_json` didn't work for CA-only configurations [[GH-11861](https://github.com/hashicorp/vault/pull/11861)]
* secrets/database/cassandra: Updated default statement for password rotation to allow for special characters. This applies to root and static credentials. [[GH-11262](https://github.com/hashicorp/vault/pull/11262)]
* secrets/database: Fix marshalling to allow providing numeric arguments to external database plugins. [[GH-11451](https://github.com/hashicorp/vault/pull/11451)]
+* secrets/database: Fixed an issue that prevented external database plugin processes from restarting after a shutdown. [[GH-12087](https://github.com/hashicorp/vault/pull/12087)]
* secrets/database: Fixed minor race condition when rotate-root is called [[GH-11600](https://github.com/hashicorp/vault/pull/11600)]
* secrets/database: Fixes issue for V4 database interface where `SetCredentials` wasn't falling back to using `RotateRootCredentials` if `SetCredentials` is `Unimplemented` [[GH-11585](https://github.com/hashicorp/vault/pull/11585)]
+* secrets/openldap: Fix bug where schema was not compatible with rotate-root [#24](https://github.com/hashicorp/vault-plugin-secrets-openldap/pull/24) [[GH-12019](https://github.com/hashicorp/vault/pull/12019)]
* storage/dynamodb: Handle throttled batch write requests by retrying, without which writes could be lost. [[GH-10181](https://github.com/hashicorp/vault/pull/10181)]
* storage/raft: Support cluster address change for nodes in a cluster managed by autopilot [[GH-11247](https://github.com/hashicorp/vault/pull/11247)]
* storage/raft: Tweak creation of vault.db file [[GH-12034](https://github.com/hashicorp/vault/pull/12034)]
* storage/raft: leader_tls_servername wasn't used unless leader_ca_cert_file and/or mTLS were configured. [[GH-11252](https://github.com/hashicorp/vault/pull/11252)]
* tokenutil: Perform the num uses check before token type. [[GH-11647](https://github.com/hashicorp/vault/pull/11647)]
* transform (enterprise): Fix an issue with malformed transform configuration
-storage when upgrading from 1.5 to 1.6. See Upgrade Notes for 1.6.x.
+ storage when upgrading from 1.5 to 1.6. See Upgrade Notes for 1.6.x.
+* ui: Add role from database connection automatically populates the database for new role [[GH-11119](https://github.com/hashicorp/vault/pull/11119)]
* ui: Add root rotation statements support to appropriate database secret engine plugins [[GH-11404](https://github.com/hashicorp/vault/pull/11404)]
+* ui: Automatically refresh the page when user logs out [[GH-12035](https://github.com/hashicorp/vault/pull/12035)]
+* ui: Fix Version History queryParams on LinkedBlock [[GH-12079](https://github.com/hashicorp/vault/pull/12079)]
+* ui: Fix bug where database secret engines with custom names cannot delete connections [[GH-11127](https://github.com/hashicorp/vault/pull/11127)]
* ui: Fix bug where the UI does not recognize version 2 KV until refresh, and fix [object Object] error message [[GH-11258](https://github.com/hashicorp/vault/pull/11258)]
+* ui: Fix database role CG access [[GH-12111](https://github.com/hashicorp/vault/pull/12111)]
+* ui: Fix date display on expired token notice [[GH-11142](https://github.com/hashicorp/vault/pull/11142)]
* ui: Fix entity group membership and metadata not showing [[GH-11641](https://github.com/hashicorp/vault/pull/11641)]
* ui: Fix error message caused by control group [[GH-11143](https://github.com/hashicorp/vault/pull/11143)]
* ui: Fix footer URL linking to the correct version changelog. [[GH-11283](https://github.com/hashicorp/vault/pull/11283)]
+* ui: Fix issue where logging in without namespace input causes error [[GH-11094](https://github.com/hashicorp/vault/pull/11094)]
* ui: Fix namespace-bug on login [[GH-11182](https://github.com/hashicorp/vault/pull/11182)]
* ui: Fix status menu no showing on login [[GH-11213](https://github.com/hashicorp/vault/pull/11213)]
* ui: Fix text link URL on database roles list [[GH-11597](https://github.com/hashicorp/vault/pull/11597)]
@@ -134,8 +177,6 @@ storage when upgrading from 1.5 to 1.6. See Upgrade Notes for 1.6.x.
* ui: fix issue where select-one option was not showing in secrets database role creation [[GH-11294](https://github.com/hashicorp/vault/pull/11294)]
* ui: fix oidc login with Safari [[GH-11884](https://github.com/hashicorp/vault/pull/11884)]
-## 1.7.3
-
## 1.7.3
### June 16th, 2021
to 3.11 in test (#12311) (#12313)
plugins/database/cassandra/connection_producer_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
builtin/credential/aws/pkcs7/verify.go+399 0
@@ -0,0 +1,399 @@
+package pkcs7
+
+import (
+ "crypto"
+ "crypto/dsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ "errors"
+ "fmt"
+ "time"
+)
+
+// Verify is a wrapper around VerifyWithChain() that initializes an empty
+// trust store, effectively disabling certificate verification when validating
+// a signature.
+func (p7 *PKCS7) Verify() (err error) {
+ return p7.VerifyWithChain(nil)
+}
+
+// VerifyWithChain checks the signatures of a PKCS7 object.
+//
+// If truststore is not nil, it also verifies the chain of trust of
+// the end-entity signer cert to one of the roots in the
+// truststore. When the PKCS7 object includes the signing time
+// authenticated attr verifies the chain at that time and UTC now
+// otherwise.
+func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error) {
+ if len(p7.Signers) == 0 {
+ return errors.New("pkcs7: Message has no signers")
+ }
+ for _, signer := range p7.Signers {
+ if err := verifySignature(p7, signer, truststore); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// VerifyWithChainAtTime checks the signatures of a PKCS7 object.
+//
+// If truststore is not nil, it also verifies the chain of trust of
+// the end-entity signer cert to a root in the truststore at
+// currentTime. It does not use the signing time authenticated
+// attribute.
+func (p7 *PKCS7) VerifyWithChainAtTime(truststore *x509.CertPool, currentTime time.Time) (err error) {
+ if len(p7.Signers) == 0 {
+ return errors.New("pkcs7: Message has no signers")
+ }
+ for _, signer := range p7.Signers {
+ if err := verifySignatureAtTime(p7, signer, truststore, currentTime); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func verifySignatureAtTime(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool, currentTime time.Time) (err error) {
+ signedData := p7.Content
+ ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
+ if ee == nil {
+ return errors.New("pkcs7: No certificate for signer")
+ }
+ if len(signer.AuthenticatedAttributes) > 0 {
+ // TODO(fullsailor): First check the content type match
+ var (
+ digest []byte
+ signingTime time.Time
+ )
+ err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest)
+ if err != nil {
+ return err
+ }
+ hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
+ if err != nil {
+ return err
+ }
+ h := hash.New()
+ h.Write(p7.Content)
+ computed := h.Sum(nil)
+ if subtle.ConstantTimeCompare(digest, computed) != 1 {
+ return &MessageDigestMismatchError{
+ ExpectedDigest: digest,
+ ActualDigest: computed,
+ }
+ }
+ signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
+ if err != nil {
+ return err
+ }
+ err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime)
+ if err == nil {
+ // signing time found, performing validity check
+ if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) {
+ return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q",
+ signingTime.Format(time.RFC3339),
+ ee.NotBefore.Format(time.RFC3339),
+ ee.NotAfter.Format(time.RFC3339))
+ }
+ }
+ }
+ if truststore != nil {
+ _, err = verifyCertChain(ee, p7.Certificates, truststore, currentTime)
+ if err != nil {
+ return err
+ }
+ }
+ sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
+ if err != nil {
+ return err
+ }
+ switch sigalg {
+ case x509.DSAWithSHA1, x509.DSAWithSHA256:
+ return dsaCheckSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey)
+ default:
+ return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
+ }
+}
+
+// dsaSignature verifies the DSA signature on a PKCS7 document. DSA support was
+// removed from Go's crypto/x509 support prior to Go 1.16. This allows
+// verifying legacy signatures until affected applications can be migrated off
+// of DSA.
+func dsaCheckSignature(algo x509.SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) error {
+ dsaKey, ok := publicKey.(*dsa.PublicKey)
+ if !ok {
+ return ErrUnsupportedAlgorithm
+ }
+
+ var hashType crypto.Hash
+ switch algo {
+ case x509.DSAWithSHA1:
+ hashType = crypto.SHA1
+ case x509.DSAWithSHA256:
+ hashType = crypto.SHA256
+ default:
+ return ErrUnsupportedAlgorithm
+ }
+ h := hashType.New()
+ h.Write(signed)
+ signed = h.Sum(nil)
+
+ dsaSig := new(dsaSignature)
+ if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil {
+ return err
+ } else if len(rest) != 0 {
+ return errors.New("x509: trailing data after DSA signature")
+ }
+ if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
+ return errors.New("x509: DSA signature contained zero or negative values")
+ }
+ // According to FIPS 186-3, section 4.6, the hash must be truncated if it is longer
+ // than the key length, but crypto/dsa doesn't do it automatically.
+ if maxHashLen := dsaKey.Q.BitLen() / 8; maxHashLen < len(signed) {
+ signed = signed[:maxHashLen]
+ }
+ if !dsa.Verify(dsaKey, signed, dsaSig.R, dsaSig.S) {
+ return errors.New("x509: DSA verification failure")
+ }
+ return nil
+}
+
+func verifySignature(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool) (err error) {
+ signedData := p7.Content
+ ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
+ if ee == nil {
+ return errors.New("pkcs7: No certificate for signer")
+ }
+ signingTime := time.Now().UTC()
+ if len(signer.AuthenticatedAttributes) > 0 {
+ // TODO(fullsailor): First check the content type match
+ var digest []byte
+ err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest)
+ if err != nil {
+ return err
+ }
+ hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
+ if err != nil {
+ return err
+ }
+ h := hash.New()
+ h.Write(p7.Content)
+ computed := h.Sum(nil)
+ if subtle.ConstantTimeCompare(digest, computed) != 1 {
+ return &MessageDigestMismatchError{
+ ExpectedDigest: digest,
+ ActualDigest: computed,
+ }
+ }
+ signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
+ if err != nil {
+ return err
+ }
+ err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime)
+ if err == nil {
+ // signing time found, performing validity check
+ if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) {
+ return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q",
+ signingTime.Format(time.RFC3339),
+ ee.NotBefore.Format(time.RFC3339),
+ ee.NotAfter.Format(time.RFC3339))
+ }
+ }
+ }
+ if truststore != nil {
+ _, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime)
+ if err != nil {
+ return err
+ }
+ }
+ sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
+ if err != nil {
+ return err
+ }
+
+ switch sigalg {
+ case x509.DSAWithSHA1, x509.DSAWithSHA256:
+ return dsaCheckSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey)
+ default:
+ return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
+ }
+}
+
+// GetOnlySigner returns an x509.Certificate for the first signer of the signed
+// data payload. If there are more or less than one signer, nil is returned
+func (p7 *PKCS7) GetOnlySigner() *x509.Certificate {
+ if len(p7.Signers) != 1 {
+ return nil
+ }
+ signer := p7.Signers[0]
+ return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
+}
+
+// UnmarshalSignedAttribute decodes a single attribute from the signer info
+func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
+ sd, ok := p7.raw.(signedData)
+ if !ok {
+ return errors.New("pkcs7: payload is not signedData content")
+ }
+ if len(sd.SignerInfos) < 1 {
+ return errors.New("pkcs7: payload has no signers")
+ }
+ attributes := sd.SignerInfos[0].AuthenticatedAttributes
+ return unmarshalAttribute(attributes, attributeType, out)
+}
+
+func parseSignedData(data []byte) (*PKCS7, error) {
+ var sd signedData
+ asn1.Unmarshal(data, &sd)
+ certs, err := sd.Certificates.Parse()
+ if err != nil {
+ return nil, err
+ }
+ // fmt.Printf("--> Signed Data Version %d\n", sd.Version)
+
+ var compound asn1.RawValue
+ var content unsignedData
+
+ // The Content.Bytes maybe empty on PKI responses.
+ if len(sd.ContentInfo.Content.Bytes) > 0 {
+ if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil {
+ return nil, err
+ }
+ }
+ // Compound octet string
+ if compound.IsCompound {
+ if compound.Tag == 4 {
+ if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
+ return nil, err
+ }
+ } else {
+ content = compound.Bytes
+ }
+ } else {
+ // assuming this is tag 04
+ content = compound.Bytes
+ }
+ return &PKCS7{
+ Content: content,
+ Certificates: certs,
+ CRLs: sd.CRLs,
+ Signers: sd.SignerInfos,
+ raw: sd}, nil
+}
+
+// verifyCertChain takes an end-entity certs, a list of potential intermediates and a
+// truststore, and built all potential chains between the EE and a trusted root.
+//
+// When verifying chains that may have expired, currentTime can be set to a past date
+// to allow the verification to pass. If unset, currentTime is set to the current UTC time.
+func verifyCertChain(ee *x509.Certificate, certs []*x509.Certificate, truststore *x509.CertPool, currentTime time.Time) (chains [][]*x509.Certificate, err error) {
+ intermediates := x509.NewCertPool()
+ for _, intermediate := range certs {
+ intermediates.AddCert(intermediate)
+ }
+ verifyOptions := x509.VerifyOptions{
+ Roots: truststore,
+ Intermediates: intermediates,
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
+ CurrentTime: currentTime,
+ }
+ chains, err = ee.Verify(verifyOptions)
+ if err != nil {
+ return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err)
+ }
+ return
+}
+
+// MessageDigestMismatchError is returned when the signer data digest does not
+// match the computed digest for the contained content
+type MessageDigestMismatchError struct {
+ ExpectedDigest []byte
+ ActualDigest []byte
+}
+
+func (err *MessageDigestMismatchError) Error() string {
+ return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest)
+}
+
+func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) {
+ switch {
+ case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1):
+ return x509.ECDSAWithSHA1, nil
+ case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256):
+ return x509.ECDSAWithSHA256, nil
+ case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384):
+ return x509.ECDSAWithSHA384, nil
+ case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512):
+ return x509.ECDSAWithSHA512, nil
+ case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA),
+ digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1),
+ digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256),
+ digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384),
+ digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512):
+ switch {
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
+ return x509.SHA1WithRSA, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
+ return x509.SHA256WithRSA, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384):
+ return x509.SHA384WithRSA, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512):
+ return x509.SHA512WithRSA, nil
+ default:
+ return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
+ digest.Algorithm.String(), digestEncryption.Algorithm.String())
+ }
+ case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA),
+ digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1):
+ switch {
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
+ return x509.DSAWithSHA1, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
+ return x509.DSAWithSHA256, nil
+ default:
+ return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
+ digest.Algorithm.String(), digestEncryption.Algorithm.String())
+ }
+ case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256),
+ digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384),
+ digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521):
+ switch {
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
+ return x509.ECDSAWithSHA1, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
+ return x509.ECDSAWithSHA256, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384):
+ return x509.ECDSAWithSHA384, nil
+ case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512):
+ return x509.ECDSAWithSHA512, nil
+ default:
+ return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
+ digest.Algorithm.String(), digestEncryption.Algorithm.String())
+ }
+ default:
+ return -1, fmt.Errorf("pkcs7: unsupported algorithm %q",
+ digestEncryption.Algorithm.String())
+ }
+}
+
+func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate {
+ for _, cert := range certs {
+ if isCertMatchForIssuerAndSerial(cert, ias) {
+ return cert
+ }
+ }
+ return nil
+}
+
+func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error {
+ for _, attr := range attrs {
+ if attr.Type.Equal(attributeType) {
+ _, err := asn1.Unmarshal(attr.Value.Bytes, out)
+ return err
+ }
+ }
+ return errors.New("pkcs7: attribute type not in attributes")
+}
builtin/credential/aws/pkcs7/sign.go+399 0
@@ -0,0 +1,429 @@
+package pkcs7
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/dsa"
+ "crypto/rand"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ "errors"
+ "fmt"
+ "math/big"
+ "time"
+)
+
+// SignedData is an opaque data structure for creating signed data payloads
+type SignedData struct {
+ sd signedData
+ certs []*x509.Certificate
+ data, messageDigest []byte
+ digestOid asn1.ObjectIdentifier
+ encryptionOid asn1.ObjectIdentifier
+}
+
+// NewSignedData takes data and initializes a PKCS7 SignedData struct that is
+// ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default
+// and can be changed by calling SetDigestAlgorithm.
+func NewSignedData(data []byte) (*SignedData, error) {
+ content, err := asn1.Marshal(data)
+ if err != nil {
+ return nil, err
+ }
+ ci := contentInfo{
+ ContentType: OIDData,
+ Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
+ }
+ sd := signedData{
+ ContentInfo: ci,
+ Version: 1,
+ }
+ return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1}, nil
+}
+
+// SignerInfoConfig are optional values to include when adding a signer
+type SignerInfoConfig struct {
+ ExtraSignedAttributes []Attribute
+ ExtraUnsignedAttributes []Attribute
+}
+
+type signedData struct {
+ Version int `asn1:"default:1"`
+ DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
+ ContentInfo contentInfo
+ Certificates rawCertificates `asn1:"optional,tag:0"`
+ CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
+ SignerInfos []signerInfo `asn1:"set"`
+}
+
+type signerInfo struct {
+ Version int `asn1:"default:1"`
+ IssuerAndSerialNumber issuerAndSerial
+ DigestAlgorithm pkix.AlgorithmIdentifier
+ AuthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:0"`
+ DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
+ EncryptedDigest []byte
+ UnauthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:1"`
+}
+
+type attribute struct {
+ Type asn1.ObjectIdentifier
+ Value asn1.RawValue `asn1:"set"`
+}
+
+func marshalAttributes(attrs []attribute) ([]byte, error) {
+ encodedAttributes, err := asn1.Marshal(struct {
+ A []attribute `asn1:"set"`
+ }{A: attrs})
+ if err != nil {
+ return nil, err
+ }
+
+ // Remove the leading sequence octets
+ var raw asn1.RawValue
+ asn1.Unmarshal(encodedAttributes, &raw)
+ return raw.Bytes, nil
+}
+
+type rawCertificates struct {
+ Raw asn1.RawContent
+}
+
+type issuerAndSerial struct {
+ IssuerName asn1.RawValue
+ SerialNumber *big.Int
+}
+
+// SetDigestAlgorithm sets the digest algorithm to be used in the signing process.
+//
+// This should be called before adding signers
+func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) {
+ sd.digestOid = d
+}
+
+// SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process.
+//
+// This should be called before adding signers
+func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) {
+ sd.encryptionOid = d
+}
+
+// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.
+func (sd *SignedData) AddSigner(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
+ var parents []*x509.Certificate
+ return sd.AddSignerChain(ee, pkey, parents, config)
+}
+
+// AddSignerChain signs attributes about the content and adds certificates
+// and signers infos to the Signed Data. The certificate and private key
+// of the end-entity signer are used to issue the signature, and any
+// parent of that end-entity that need to be added to the list of
+// certifications can be specified in the parents slice.
+//
+// The signature algorithm used to hash the data is the one of the end-entity
+// certificate.
+func (sd *SignedData) AddSignerChain(ee *x509.Certificate, pkey crypto.PrivateKey, parents []*x509.Certificate, config SignerInfoConfig) error {
+ // Following RFC 2315, 9.2 SignerInfo type, the distinguished name of
+ // the issuer of the end-entity signer is stored in the issuerAndSerialNumber
+ // section of the SignedData.SignerInfo, alongside the serial number of
+ // the end-entity.
+ var ias issuerAndSerial
+ ias.SerialNumber = ee.SerialNumber
+ if len(parents) == 0 {
+ // no parent, the issuer is the end-entity cert itself
+ ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer}
+ } else {
+ err := verifyPartialChain(ee, parents)
+ if err != nil {
+ return err
+ }
+ // the first parent is the issuer
+ ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject}
+ }
+ sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers,
+ pkix.AlgorithmIdentifier{Algorithm: sd.digestOid},
+ )
+ hash, err := getHashForOID(sd.digestOid)
+ if err != nil {
+ return err
+ }
+ h := hash.New()
+ h.Write(sd.data)
+ sd.messageDigest = h.Sum(nil)
+ encryptionOid, err := getOIDForEncryptionAlgorithm(pkey, sd.digestOid)
+ if err != nil {
+ return err
+ }
+ attrs := &attributes{}
+ attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType)
+ attrs.Add(OIDAttributeMessageDigest, sd.messageDigest)
+ attrs.Add(OIDAttributeSigningTime, time.Now().UTC())
+ for _, attr := range config.ExtraSignedAttributes {
+ attrs.Add(attr.Type, attr.Value)
+ }
+ finalAttrs, err := attrs.ForMarshalling()
+ if err != nil {
+ return err
+ }
+ unsignedAttrs := &attributes{}
+ for _, attr := range config.ExtraUnsignedAttributes {
+ unsignedAttrs.Add(attr.Type, attr.Value)
+ }
+ finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling()
+ if err != nil {
+ return err
+ }
+ // create signature of signed attributes
+ signature, err := signAttributes(finalAttrs, pkey, hash)
+ if err != nil {
+ return err
+ }
+ signer := signerInfo{
+ AuthenticatedAttributes: finalAttrs,
+ UnauthenticatedAttributes: finalUnsignedAttrs,
+ DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid},
+ DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: encryptionOid},
+ IssuerAndSerialNumber: ias,
+ EncryptedDigest: signature,
+ Version: 1,
+ }
+ sd.certs = append(sd.certs, ee)
+ if len(parents) > 0 {
+ sd.certs = append(sd.certs, parents...)
+ }
+ sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
+ return nil
+}
+
+// SignWithoutAttr issues a signature on the content of the pkcs7 SignedData.
+// Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone
+// and does not include any signed attributes like timestamp and so on.
+//
+// This function is needed to sign old Android APKs, something you probably
+// shouldn't do unless you're maintaining backward compatibility for old
+// applications.
+func (sd *SignedData) SignWithoutAttr(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
+ var signature []byte
+ sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, pkix.AlgorithmIdentifier{Algorithm: sd.digestOid})
+ hash, err := getHashForOID(sd.digestOid)
+ if err != nil {
+ return err
+ }
+ h := hash.New()
+ h.Write(sd.data)
+ sd.messageDigest = h.Sum(nil)
+ switch pkey := pkey.(type) {
+ case *dsa.PrivateKey:
+ // dsa doesn't implement crypto.Signer so we make a special case
+ // https://github.com/golang/go/issues/27889
+ r, s, err := dsa.Sign(rand.Reader, pkey, sd.messageDigest)
+ if err != nil {
+ return err
+ }
+ signature, err = asn1.Marshal(dsaSignature{r, s})
+ if err != nil {
+ return err
+ }
+ default:
+ key, ok := pkey.(crypto.Signer)
+ if !ok {
+ return errors.New("pkcs7: private key does not implement crypto.Signer")
+ }
+ signature, err = key.Sign(rand.Reader, sd.messageDigest, hash)
+ if err != nil {
+ return err
+ }
+ }
+ var ias issuerAndSerial
+ ias.SerialNumber = ee.SerialNumber
+ // no parent, the issue is the end-entity cert itself
+ ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer}
+ if sd.encryptionOid == nil {
+ // if the encryption algorithm wasn't set by SetEncryptionAlgorithm,
+ // infer it from the digest algorithm
+ sd.encryptionOid, err = getOIDForEncryptionAlgorithm(pkey, sd.digestOid)
+ }
+ if err != nil {
+ return err
+ }
+ signer := signerInfo{
+ DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid},
+ DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.encryptionOid},
+ IssuerAndSerialNumber: ias,
+ EncryptedDigest: signature,
+ Version: 1,
+ }
+ // create signature of signed attributes
+ sd.certs = append(sd.certs, ee)
+ sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
+ return nil
+}
+
+func (si *signerInfo) SetUnauthenticatedAttributes(extraUnsignedAttrs []Attribute) error {
+ unsignedAttrs := &attributes{}
+ for _, attr := range extraUnsignedAttrs {
+ unsignedAttrs.Add(attr.Type, attr.Value)
+ }
+ finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling()
+ if err != nil {
+ return err
+ }
+
+ si.UnauthenticatedAttributes = finalUnsignedAttrs
+
+ return nil
+}
+
+// AddCertificate adds the certificate to the payload. Useful for parent certificates
+func (sd *SignedData) AddCertificate(cert *x509.Certificate) {
+ sd.certs = append(sd.certs, cert)
+}
+
+// Detach removes content from the signed data struct to make it a detached signature.
+// This must be called right before Finish()
+func (sd *SignedData) Detach() {
+ sd.sd.ContentInfo = contentInfo{ContentType: OIDData}
+}
+
+// GetSignedData returns the private Signed Data
+func (sd *SignedData) GetSignedData() *signedData {
+ return &sd.sd
+}
+
+// Finish marshals the content and its signers
+func (sd *SignedData) Finish() ([]byte, error) {
+ sd.sd.Certificates = marshalCertificates(sd.certs)
+ inner, err := asn1.Marshal(sd.sd)
+ if err != nil {
+ return nil, err
+ }
+ outer := contentInfo{
+ ContentType: OIDSignedData,
+ Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
+ }
+ return asn1.Marshal(outer)
+}
+
+// RemoveAuthenticatedAttributes removes authenticated attributes from signedData
+// similar to OpenSSL's PKCS7_NOATTR or -noattr flags
+func (sd *SignedData) RemoveAuthenticatedAttributes() {
+ for i := range sd.sd.SignerInfos {
+ sd.sd.SignerInfos[i].AuthenticatedAttributes = nil
+ }
+}
+
+// RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData
+func (sd *SignedData) RemoveUnauthenticatedAttributes() {
+ for i := range sd.sd.SignerInfos {
+ sd.sd.SignerInfos[i].UnauthenticatedAttributes = nil
+ }
+}
+
+// verifyPartialChain checks that a given cert is issued by the first parent in the list,
+// then continue down the path. It doesn't require the last parent to be a root CA,
+// or to be trusted in any truststore. It simply verifies that the chain provided, albeit
+// partial, makes sense.
+func verifyPartialChain(cert *x509.Certificate, parents []*x509.Certificate) error {
+ if len(parents) == 0 {
+ return fmt.Errorf("pkcs7: zero parents provided to verify the signature of certificate %q", cert.Subject.CommonName)
+ }
+ err := cert.CheckSignatureFrom(parents[0])
+ if err != nil {
+ return fmt.Errorf("pkcs7: certificate signature from parent is invalid: %v", err)
+ }
+ if len(parents) == 1 {
+ // there is no more parent to check, return
+ return nil
+ }
+ return verifyPartialChain(parents[0], parents[1:])
+}
+
+func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) {
+ var ias issuerAndSerial
+ // The issuer RDNSequence has to match exactly the sequence in the certificate
+ // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence
+ ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer}
+ ias.SerialNumber = cert.SerialNumber
+
+ return ias, nil
+}
+
+// signs the DER encoded form of the attributes with the private key
+func signAttributes(attrs []attribute, pkey crypto.PrivateKey, digestAlg crypto.Hash) ([]byte, error) {
+ attrBytes, err := marshalAttributes(attrs)
+ if err != nil {
+ return nil, err
+ }
+ h := digestAlg.New()
+ h.Write(attrBytes)
+ hash := h.Sum(nil)
+
+ // dsa doesn't implement crypto.Signer so we make a special case
+ // https://github.com/golang/go/issues/27889
+ switch pkey := pkey.(type) {
+ case *dsa.PrivateKey:
+ r, s, err := dsa.Sign(rand.Reader, pkey, hash)
+ if err != nil {
+ return nil, err
+ }
+ return asn1.Marshal(dsaSignature{r, s})
+ }
+
+ key, ok := pkey.(crypto.Signer)
+ if !ok {
+ return nil, errors.New("pkcs7: private key does not implement crypto.Signer")
+ }
+ return key.Sign(rand.Reader, hash, digestAlg)
+}
+
+type dsaSignature struct {
+ R, S *big.Int
+}
+
+// concats and wraps the certificates in the RawValue structure
+func marshalCertificates(certs []*x509.Certificate) rawCertificates {
+ var buf bytes.Buffer
+ for _, cert := range certs {
+ buf.Write(cert.Raw)
+ }
+ rawCerts, _ := marshalCertificateBytes(buf.Bytes())
+ return rawCerts
+}
+
+// Even though, the tag & length are stripped out during marshalling the
+// RawContent, we have to encode it into the RawContent. If its missing,
+// then `asn1.Marshal()` will strip out the certificate wrapper instead.
+func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
+ var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
+ b, err := asn1.Marshal(val)
… diff truncated
vault/identity_store_oidc_test.go+86 0
@@ -113,6 +113,48 @@ func TestOIDC_Path_OIDCRoleRole(t *testing.T) {
}
}
+// TestOIDC_Path_OIDCRole_InvalidTokenTTL tests the TokenTTL validation
+func TestOIDC_Path_OIDCRole_InvalidTokenTTL(t *testing.T) {
+ c, _, _ := TestCoreUnsealed(t)
+ ctx := namespace.RootContext(nil)
+ storage := &logical.InmemStorage{}
+
+ // Create a test key "test-key"
+ c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/key/test-key",
+ Operation: logical.CreateOperation,
+ Data: map[string]interface{}{
+ "verification_ttl": int64(60),
+ },
+ Storage: storage,
+ })
+
+ // Create a test role "test-role1" with a ttl longer than the
+ // verification_ttl -- should fail with error
+ resp, err := c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/role/test-role1",
+ Operation: logical.CreateOperation,
+ Data: map[string]interface{}{
+ "key": "test-key",
+ "ttl": int64(3600),
+ },
+ Storage: storage,
+ })
+ expectError(t, resp, err)
+
+ // Read "test-role1"
+ respReadTestRole1, err3 := c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/role/test-role1",
+ Operation: logical.ReadOperation,
+ Storage: storage,
+ })
+ // Ensure that "test-role1" was not created
+ expectSuccess(t, respReadTestRole1, err3)
+ if respReadTestRole1 != nil {
+ t.Fatalf("Expected a nil response but instead got:\n%#v", respReadTestRole1)
+ }
+}
+
// TestOIDC_Path_OIDCRole tests the List operation for roles
func TestOIDC_Path_OIDCRole(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
@@ -284,6 +326,49 @@ func TestOIDC_Path_OIDCKeyKey(t *testing.T) {
expectSuccess(t, resp, err)
}
+// TestOIDC_Path_OIDCKey_InvalidTokenTTL tests the TokenTTL validation
+func TestOIDC_Path_OIDCKey_InvalidTokenTTL(t *testing.T) {
+ c, _, _ := TestCoreUnsealed(t)
+ ctx := namespace.RootContext(nil)
+ storage := &logical.InmemStorage{}
+
+ // Create a test key "test-key" -- should succeed
+ resp, err := c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/key/test-key",
+ Operation: logical.CreateOperation,
+ Data: map[string]interface{}{
+ "verification_ttl": "4m",
+ },
+ Storage: storage,
+ })
+ expectSuccess(t, resp, err)
+
+ // Create a role that depends on test key
+ resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/role/allowed-test-role",
+ Operation: logical.UpdateOperation,
+ Data: map[string]interface{}{
+ "key": "test-key",
+ "ttl": "4m",
+ },
+ Storage: storage,
+ })
+ expectSuccess(t, resp, err)
+
+ // Update "test-key" -- should fail since allowed-test-role ttl is less than 2m
+ resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/key/test-key",
+ Operation: logical.UpdateOperation,
+ Data: map[string]interface{}{
+ "rotation_period": "10m",
+ "verification_ttl": "2m",
+ "allowed_client_ids": "allowed-test-role",
+ },
+ Storage: storage,
+ })
+ expectError(t, resp, err)
+}
+
// TestOIDC_Path_OIDCKey tests the List operation for keys
func TestOIDC_Path_OIDCKey(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
@@ -1100,6 +1185,7 @@ func expectSuccess(t *testing.T, resp *logical.Response, err error) {
}
func expectError(t *testing.T, resp *logical.Response, err error) {
+ t.Helper()
if err == nil {
if resp == nil || !resp.IsError() {
t.Fatalf("expected error but got success; error:\n%v\nresp: %#v", err, resp)
changelog/12229.txt | 3 +++
go.mod | 1 +
vault/diagnose/os_common.go | 7 ++++---
3 files changed, 8 insertions(+), 3 deletions(-)
create mode 100644 changelog/12229.txt
vault/identity_store_oidc.go+99 32
@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"net/url"
+ "sort"
"strings"
"time"
@@ -472,6 +473,25 @@ func (i *IdentityStore) pathOIDCCreateUpdateKey(ctx context.Context, req *logica
return logical.ErrorResponse("verification_ttl cannot be longer than 10x rotation_period"), nil
}
+ if req.Operation == logical.UpdateOperation {
+ // ensure any roles referencing this key do not already have a token_ttl
+ // greater than the key's verification_ttl
+ roles, err := i.rolesReferencingTargetKeyName(ctx, req, name)
+ if err != nil {
+ return nil, err
+ }
+ for _, role := range roles {
+ if role.TokenTTL > key.VerificationTTL {
+ errorMessage := fmt.Sprintf(
+ "unable to update key %q because it is currently referenced by one or more roles with a token ttl greater than %d seconds",
+ name,
+ key.VerificationTTL/time.Second,
+ )
+ return logical.ErrorResponse(errorMessage), nil
+ }
+ }
+ }
+
if allowedClientIDsRaw, ok := d.GetOk("allowed_client_ids"); ok {
key.AllowedClientIDs = allowedClientIDsRaw.([]string)
} else if req.Operation == logical.CreateOperation {
@@ -556,46 +576,70 @@ func (i *IdentityStore) pathOIDCReadKey(ctx context.Context, req *logical.Reques
}, nil
}
-// handleOIDCDeleteKey is used to delete a key
-func (i *IdentityStore) pathOIDCDeleteKey(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
- ns, err := namespace.FromContext(ctx)
- if err != nil {
- return nil, err
- }
-
- targetKeyName := d.Get("name").(string)
-
- i.oidcLock.Lock()
-
- // it is an error to delete a key that is actively referenced by a role
+// rolesReferencingTargetKeyName returns a map of role names to roles referenced by targetKeyName.
+// Note: this is not threadsafe. It is to be called with Lock already held.
+func (i *IdentityStore) rolesReferencingTargetKeyName(ctx context.Context, req *logical.Request, targetKeyName string) (map[string]role, error) {
roleNames, err := req.Storage.List(ctx, roleConfigPath)
if err != nil {
- i.oidcLock.Unlock()
return nil, err
}
- var role *role
- rolesReferencingTargetKeyName := make([]string, 0)
+ var tempRole role
+ roles := make(map[string]role)
for _, roleName := range roleNames {
entry, err := req.Storage.Get(ctx, roleConfigPath+roleName)
if err != nil {
- i.oidcLock.Unlock()
return nil, err
}
if entry != nil {
- if err := entry.DecodeJSON(&role); err != nil {
- i.oidcLock.Unlock()
+ if err := entry.DecodeJSON(&tempRole); err != nil {
return nil, err
}
- if role.Key == targetKeyName {
- rolesReferencingTargetKeyName = append(rolesReferencingTargetKeyName, roleName)
+ if tempRole.Key == targetKeyName {
+ roles[roleName] = tempRole
}
}
}
- if len(rolesReferencingTargetKeyName) > 0 {
+ return roles, nil
+}
+
+// roleNamesReferencingTargetKeyName returns a slice of strings of role
+// names referenced by targetKeyName.
+// Note: this is not threadsafe. It is to be called with Lock already held.
+func (i *IdentityStore) roleNamesReferencingTargetKeyName(ctx context.Context, req *logical.Request, targetKeyName string) ([]string, error) {
+ roles, err := i.rolesReferencingTargetKeyName(ctx, req, targetKeyName)
+ if err != nil {
+ return nil, err
+ }
+
+ var names []string
+ for key, _ := range roles {
+ names = append(names, key)
+ }
+ sort.Strings(names)
+ return names, nil
+}
+
+// handleOIDCDeleteKey is used to delete a key
+func (i *IdentityStore) pathOIDCDeleteKey(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
+ ns, err := namespace.FromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ targetKeyName := d.Get("name").(string)
+
+ i.oidcLock.Lock()
+
+ roleNames, err := i.roleNamesReferencingTargetKeyName(ctx, req, targetKeyName)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(roleNames) > 0 {
errorMessage := fmt.Sprintf("unable to delete key %q because it is currently referenced by these roles: %s",
- targetKeyName, strings.Join(rolesReferencingTargetKeyName, ", "))
+ targetKeyName, strings.Join(roleNames, ", "))
i.oidcLock.Unlock()
return logical.ErrorResponse(errorMessage), logical.ErrInvalidRequest
}
@@ -747,13 +791,21 @@ func (i *IdentityStore) pathOIDCGenerateToken(ctx context.Context, req *logical.
return nil, err
}
+ retResp := &logical.Response{}
+ expiry := role.TokenTTL
+ if expiry > key.VerificationTTL {
+ expiry = key.VerificationTTL
+ retResp.AddWarning(fmt.Sprintf("a role's token ttl cannot be longer "+
+ "than the verification_ttl of the key it references, setting token ttl to %d", expiry))
+ }
+
now := time.Now()
idToken := idToken{
Issuer: config.effectiveIssuer,
Namespace: ns.ID,
Subject: req.EntityID,
Audience: role.ClientID,
- Expiry: now.Add(role.TokenTTL).Unix(),
+ Expiry: now.Add(expiry).Unix(),
IssuedAt: now.Unix(),
}
@@ -782,13 +834,12 @@ func (i *IdentityStore) pathOIDCGenerateToken(ctx context.Context, req *logical.
return nil, fmt.Errorf("error signing OIDC token: %w", err)
}
- return &logical.Response{
- Data: map[string]interface{}{
- "token": signedIdToken,
- "client_id": role.ClientID,
- "ttl": int64(role.TokenTTL.Seconds()),
- },
- }, nil
+ retResp.Data = map[string]interface{}{
+ "token": signedIdToken,
+ "client_id": role.ClientID,
+ "ttl": int64(role.TokenTTL.Seconds()),
+ }
+ return retResp, nil
}
func (tok *idToken) generatePayload(logger hclog.Logger, template string, entity *identity.Entity, groups []*identity.Group) ([]byte, error) {
@@ -867,7 +918,7 @@ func (i *IdentityStore) pathOIDCRoleExistenceCheck(ctx context.Context, req *log
return role != nil, nil
}
-// handleOIDCCreateRole is used to create a new role or update an existing one
+// pathOIDCCreateUpdateRole is used to create a new role or update an existing one
func (i *IdentityStore) pathOIDCCreateUpdateRole(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
ns, err := namespace.FromContext(ctx)
if err != nil {
@@ -938,6 +989,22 @@ func (i *IdentityStore) pathOIDCCreateUpdateRole(ctx context.Context, req *logic
role.TokenTTL = time.Duration(d.Get("ttl").(int)) * time.Second
}
+ // get the key referenced by this role
+ var key namedKey
+ entry, err := req.Storage.Get(ctx, namedKeyConfigPath+role.Key)
+ if err != nil {
+ return nil, err
+ }
+ if entry != nil {
+ if err := entry.DecodeJSON(&key); err != nil {
+ return nil, err
+ }
+ }
+
+ if role.TokenTTL > key.VerificationTTL {
+ return logical.ErrorResponse("a role's token ttl cannot be longer than the verification_ttl of the key it references"), nil
+ }
+
if clientID, ok := d.GetOk("client_id"); ok {
role.ClientID = clientID.(string)
}
@@ -952,7 +1019,7 @@ func (i *IdentityStore) pathOIDCCreateUpdateRole(ctx context.Context, req *logic
}
// store role (which was either just created or updated)
- entry, err := logical.StorageEntryJSON(roleConfigPath+name, role)
+ entry, err = logical.StorageEntryJSON(roleConfigPath+name, role)
if err != nil {
return nil, err
}
vault/identity_store_oidc_test.go+35 0
@@ -113,6 +113,41 @@ func TestOIDC_Path_OIDCRoleRole(t *testing.T) {
}
}
+// TestOIDC_Path_OIDCRole_NoKey tests that a role can be created with a non-existent key
+func TestOIDC_Path_OIDCRole_NoKey(t *testing.T) {
+ c, _, _ := TestCoreUnsealed(t)
+ ctx := namespace.RootContext(nil)
+ storage := &logical.InmemStorage{}
+
+ // Create a test role "test-role1" with a non-existent key -- should succeed
+ resp, err := c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/role/test-role1",
+ Operation: logical.CreateOperation,
+ Data: map[string]interface{}{
+ "key": "test-key",
+ },
+ Storage: storage,
+ })
+ expectSuccess(t, resp, err)
+
+ // Read "test-role1" and validate
+ resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{
+ Path: "oidc/role/test-role1",
+ Operation: logical.ReadOperation,
+ Storage: storage,
+ })
+ expectSuccess(t, resp, err)
+ expected := map[string]interface{}{
+ "key": "test-key",
+ "ttl": int64(86400),
+ "template": "",
+ "client_id": resp.Data["client_id"],
+ }
+ if diff := deep.Equal(expected, resp.Data); diff != nil {
+ t.Fatal(diff)
+ }
+}
+
// TestOIDC_Path_OIDCRole_InvalidTokenTTL tests the TokenTTL validation
func TestOIDC_Path_OIDCRole_InvalidTokenTTL(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
changelog/12258.txt | 3 +++
go.mod | 2 +-
go.sum | 4 ++--
3 files changed, 6 insertions(+), 3 deletions(-)
create mode 100644 changelog/12258.txt
builtin/credential/aws/backend_test.go+17 0
@@ -1129,6 +1129,22 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing
}
}
+ // Configure additional metadata to be returned for ec2 logins.
+ identity := map[string]interface{}{
+ "ec2_metadata": []string{"instance_id", "region", "ami_id"},
+ }
+
+ // store the identity
+ _, err = b.HandleRequest(context.Background(), &logical.Request{
+ Operation: logical.UpdateOperation,
+ Storage: storage,
+ Path: "config/identity",
+ Data: identity,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
loginInput := map[string]interface{}{
"pkcs7": pkcs7,
"nonce": "vault-client-nonce",
@@ -1241,6 +1257,7 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing
delete(loginInput, "pkcs7")
loginInput["identity"] = identityDoc
loginInput["signature"] = identityDocSig
+
resp, err = b.HandleRequest(context.Background(), loginRequest)
if err != nil {
t.Fatal(err)
CHANGELOG.md+21 0
@@ -1,3 +1,24 @@
+## 1.8.2
+### 26 August 2021
+
+CHANGES:
+
+* go: Update go version to 1.16.7 [[GH-12408](https://github.com/hashicorp/vault/pull/12408)]
+
+BUG FIXES:
+
+* auth/aws: Fixes ec2 login no longer supporting DSA signature verification [[GH-12340](https://github.com/hashicorp/vault/pull/12340)]
+* cli: vault debug now puts newlines after every captured log line. [[GH-12175](https://github.com/hashicorp/vault/pull/12175)]
+* database/couchbase: change default template to truncate username at 128 characters [[GH-12300](https://github.com/hashicorp/vault/pull/12300)]
+* identity: Fix a panic on arm64 platform when doing identity I/O. [[GH-12371](https://github.com/hashicorp/vault/pull/12371)]
+* physical/raft: Fix safeio.Rename error when restoring snapshots on windows [[GH-12377](https://github.com/hashicorp/vault/pull/12377)]
+* plugin/snowflake: Fixed bug where plugin would crash on 32 bit systems [[GH-12378](https://github.com/hashicorp/vault/pull/12378)]
+* sdk/database: Fix a DeleteUser error message on the gRPC client. [[GH-12351](https://github.com/hashicorp/vault/pull/12351)]
+* secrets/gcp: Fixes a potential panic in the service account policy rollback for rolesets. [[GH-12379](https://github.com/hashicorp/vault/pull/12379)]
+* ui: Fixed api explorer routing bug [[GH-12354](https://github.com/hashicorp/vault/pull/12354)]
+* ui: Fixes metrics page when read on counter config not allowed [[GH-12348](https://github.com/hashicorp/vault/pull/12348)]
+* ui: fix issue where on MaskedInput on auth methods if tab it would clear the value. [[GH-12409](https://github.com/hashicorp/vault/pull/12409)]
+
## 1.8.1
### August 5th, 2021
tokens (#12468)
changelog/12339.txt | 3 +++
vault/auth.go | 10 ++++++----
vault/quotas/quotas.go | 5 +++--
3 files changed, 12 insertions(+), 6 deletions(-)
create mode 100644 changelog/12339.txt
ui/tests/acceptance/secrets/backend/kv/secret-test.js+56 0
@@ -409,6 +409,62 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.dom('[data-test-secret-undelete]').exists('undelete button shows');
});
+ test('version 2 with path forward slash will show delete button', async function(assert) {
+ let backend = 'kv-v2';
+ const V2_POLICY = `
+ path "kv-v2/delete/forward/slash" {
+ capabilities = ["update"]
+ }
+ path "kv-v2/metadata/*" {
+ capabilities = ["list","read","create","update"]
+ }
+ path "kv-v2/data/forward/slash" {
+ capabilities = ["create", "read"]
+ }
+ `;
+ await consoleComponent.runCommands([
+ `write sys/mounts/${backend} type=kv options=version=2`,
+ `write sys/policies/acl/kv-v2-degrade policy=${btoa(V2_POLICY)}`,
+ // delete any kv previously written here so that tests can be re-run
+ 'delete kv-v2/metadata/forward/slash',
+ 'write -field=client_token auth/token/create policies=kv-v2-degrade',
+ ]);
+
+ let userToken = consoleComponent.lastLogOutput;
+ await logout.visit();
+ await authPage.login(userToken);
+ await writeSecret(backend, 'forward/slash', 'foo', 'bar');
+ assert.dom('[data-test-secret-v2-delete="true"]').exists('drop down delete shows');
+ });
+
+ test('version 2 with engine with forward slash will show delete button', async function(assert) {
+ let backend = 'forward/slash';
+ const V2_POLICY = `
+ path "forward/slash/delete/secret" {
+ capabilities = ["update"]
+ }
+ path "forward/slash/metadata/*" {
+ capabilities = ["list","read","create","update"]
+ }
+ path "forward/slash/data/*" {
+ capabilities = ["create", "read"]
+ }
+ `;
+ await consoleComponent.runCommands([
+ `write sys/mounts/${backend} type=kv options=version=2`,
+ `write sys/policies/acl/kv-v2-degrade policy=${btoa(V2_POLICY)}`,
+ // delete any kv previously written here so that tests can be re-run
+ 'delete forward/slash/metadata/secret',
+ 'write -field=client_token auth/token/create policies=kv-v2-degrade',
+ ]);
+
+ let userToken = consoleComponent.lastLogOutput;
+ await logout.visit();
+ await authPage.login(userToken);
+ await writeSecret(backend, 'secret', 'foo', 'bar');
+ assert.dom('[data-test-secret-v2-delete="true"]').exists('drop down delete shows');
+ });
+
test('paths are properly encoded', async function(assert) {
let backend = 'kv';
let paths = [
key algorithm. (#12514) (#12548)
builtin/logical/pki/backend_test.go | 197 +++++++++++++++++++++++++---
builtin/logical/pki/path_root.go | 57 ++++++++
changelog/12514.txt | 3 +
3 files changed, 240 insertions(+), 17 deletions(-)
create mode 100644 changelog/12514.txt
builtin/credential/aws/pkcs7/encrypt_test.go+102 0
@@ -0,0 +1,102 @@
+package pkcs7
+
+import (
+ "bytes"
+ "crypto/x509"
+ "testing"
+)
+
+func TestEncrypt(t *testing.T) {
+ modes := []int{
+ EncryptionAlgorithmDESCBC,
+ EncryptionAlgorithmAES128CBC,
+ EncryptionAlgorithmAES256CBC,
+ EncryptionAlgorithmAES128GCM,
+ EncryptionAlgorithmAES256GCM,
+ }
+ sigalgs := []x509.SignatureAlgorithm{
+ x509.SHA1WithRSA,
+ x509.SHA256WithRSA,
+ x509.SHA512WithRSA,
+ }
+ for _, mode := range modes {
+ for _, sigalg := range sigalgs {
+ ContentEncryptionAlgorithm = mode
+
+ plaintext := []byte("Hello Secret World!")
+ cert, err := createTestCertificate(sigalg)
+ if err != nil {
+ t.Fatal(err)
+ }
+ encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate})
+ if err != nil {
+ t.Fatal(err)
+ }
+ p7, err := Parse(encrypted)
+ if err != nil {
+ t.Fatalf("cannot Parse encrypted result: %s", err)
+ }
+ result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey)
+ if err != nil {
+ t.Fatalf("cannot Decrypt encrypted result: %s", err)
+ }
+ if !bytes.Equal(plaintext, result) {
+ t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
+ }
+ }
+ }
+}
+
+func TestEncryptUsingPSK(t *testing.T) {
+ modes := []int{
+ EncryptionAlgorithmDESCBC,
+ EncryptionAlgorithmAES128GCM,
+ }
+
+ for _, mode := range modes {
+ ContentEncryptionAlgorithm = mode
+ plaintext := []byte("Hello Secret World!")
+ var key []byte
+
+ switch mode {
+ case EncryptionAlgorithmDESCBC:
+ key = []byte("64BitKey")
+ case EncryptionAlgorithmAES128GCM:
+ key = []byte("128BitKey4AESGCM")
+ }
+ ciphertext, err := EncryptUsingPSK(plaintext, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p7, _ := Parse(ciphertext)
+ result, err := p7.DecryptUsingPSK(key)
+ if err != nil {
+ t.Fatalf("cannot Decrypt encrypted result: %s", err)
+ }
+ if !bytes.Equal(plaintext, result) {
+ t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
+ }
+ }
+}
+
+func TestPad(t *testing.T) {
+ tests := []struct {
+ Original []byte
+ Expected []byte
+ BlockSize int
+ }{
+ {[]byte{0x1, 0x2, 0x3, 0x10}, []byte{0x1, 0x2, 0x3, 0x10, 0x4, 0x4, 0x4, 0x4}, 8},
+ {[]byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0}, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}, 8},
+ }
+ for _, test := range tests {
+ padded, err := pad(test.Original, test.BlockSize)
+ if err != nil {
+ t.Errorf("pad encountered error: %s", err)
+ continue
+ }
+ if !bytes.Equal(test.Expected, padded) {
+ t.Errorf("pad results mismatch:\n\tExpected: %X\n\tActual: %X", test.Expected, padded)
+ }
+ }
+}
builtin/credential/aws/pkcs7/sign_test.go+266 0
@@ -0,0 +1,266 @@
+package pkcs7
+
+import (
+ "bytes"
+ "crypto/dsa"
+ "crypto/x509"
+ "encoding/asn1"
+ "encoding/pem"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "math/big"
+ "os"
+ "os/exec"
+ "testing"
+)
+
+func TestSign(t *testing.T) {
+ content := []byte("Hello World")
+ sigalgs := []x509.SignatureAlgorithm{
+ x509.SHA1WithRSA,
+ x509.SHA256WithRSA,
+ x509.SHA512WithRSA,
+ x509.ECDSAWithSHA1,
+ x509.ECDSAWithSHA256,
+ x509.ECDSAWithSHA384,
+ x509.ECDSAWithSHA512,
+ }
+ for _, sigalgroot := range sigalgs {
+ rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
+ if err != nil {
+ t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
+ }
+ truststore := x509.NewCertPool()
+ truststore.AddCert(rootCert.Certificate)
+ for _, sigalginter := range sigalgs {
+ interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true)
+ if err != nil {
+ t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err)
+ }
+ var parents []*x509.Certificate
+ parents = append(parents, interCert.Certificate)
+ for _, sigalgsigner := range sigalgs {
+ signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false)
+ if err != nil {
+ t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
+ }
+ for _, testDetach := range []bool{false, true} {
+ log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach)
+ toBeSigned, err := NewSignedData(content)
+ if err != nil {
+ t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
+ }
+
+ // Set the digest to match the end entity cert
+ signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm)
+ toBeSigned.SetDigestAlgorithm(signerDigest)
+
+ if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
+ t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err)
+ }
+ if testDetach {
+ toBeSigned.Detach()
+ }
+ signed, err := toBeSigned.Finish()
+ if err != nil {
+ t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err)
+ }
+ pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
+ p7, err := Parse(signed)
+ if err != nil {
+ t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
+ }
+ if testDetach {
+ p7.Content = content
+ }
+ if !bytes.Equal(content, p7.Content) {
+ t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
+ }
+ if err := p7.VerifyWithChain(truststore); err != nil {
+ t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
+ }
+ if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
+ t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q",
+ sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
+ }
+ }
+ }
+ }
+ }
+}
+
+func TestDSASignAndVerifyWithOpenSSL(t *testing.T) {
+ content := []byte("Hello World")
+ // write the content to a temp file
+ tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
+
+ block, _ := pem.Decode([]byte(dsaPublicCert))
+ if block == nil {
+ t.Fatal("failed to parse certificate PEM")
+ }
+ signerCert, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ t.Fatal("failed to parse certificate: " + err.Error())
+ }
+
+ // write the signer cert to a temp file
+ tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755)
+
+ priv := dsa.PrivateKey{
+ PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"),
+ Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"),
+ G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"),
+ },
+ },
+ X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"),
+ }
+ toBeSigned, err := NewSignedData(content)
+ if err != nil {
+ t.Fatalf("test case: cannot initialize signed data: %s", err)
+ }
+ if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil {
+ t.Fatalf("Cannot add signer: %s", err)
+ }
+ toBeSigned.Detach()
+ signed, err := toBeSigned.Finish()
+ if err != nil {
+ t.Fatalf("test case: cannot finish signing data: %s", err)
+ }
+
+ // write the signature to a temp file
+ tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755)
+
+ // call openssl to verify the signature on the content using the root
+ opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify",
+ "-in", tmpSignatureFile.Name(), "-inform", "PEM",
+ "-content", tmpContentFile.Name())
+ out, err := opensslCMD.CombinedOutput()
+ if err != nil {
+ t.Fatalf("test case: openssl command failed with %s: %s", err, out)
+ }
+ os.Remove(tmpSignatureFile.Name()) // clean up
+ os.Remove(tmpContentFile.Name()) // clean up
+ os.Remove(tmpSignerCertFile.Name()) // clean up
+}
+
+func ExampleSignedData() {
+ // generate a signing cert or load a key pair
+ cert, err := createTestCertificate(x509.SHA256WithRSA)
+ if err != nil {
+ fmt.Printf("Cannot create test certificates: %s", err)
+ }
+
+ // Initialize a SignedData struct with content to be signed
+ signedData, err := NewSignedData([]byte("Example data to be signed"))
+ if err != nil {
+ fmt.Printf("Cannot initialize signed data: %s", err)
+ }
+
+ // Add the signing cert and private key
+ if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
+ fmt.Printf("Cannot add signer: %s", err)
+ }
+
+ // Call Detach() is you want to remove content from the signature
+ // and generate an S/MIME detached signature
+ signedData.Detach()
+
+ // Finish() to obtain the signature bytes
+ detachedSignature, err := signedData.Finish()
+ if err != nil {
+ fmt.Printf("Cannot finish signing data: %s", err)
+ }
+ pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature})
+}
+
+func TestUnmarshalSignedAttribute(t *testing.T) {
+ cert, err := createTestCertificate(x509.SHA512WithRSA)
+ if err != nil {
+ t.Fatal(err)
+ }
+ content := []byte("Hello World")
+ toBeSigned, err := NewSignedData(content)
+ if err != nil {
+ t.Fatalf("Cannot initialize signed data: %s", err)
+ }
+ oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7}
+ testValue := "TestValue"
+ if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{
+ ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}},
+ }); err != nil {
+ t.Fatalf("Cannot add signer: %s", err)
+ }
+ signed, err := toBeSigned.Finish()
+ if err != nil {
+ t.Fatalf("Cannot finish signing data: %s", err)
+ }
+ p7, err := Parse(signed)
+ if err != nil {
+ t.Fatalf("Cannot parse signed data: %v", err)
+ }
+ var actual string
+ err = p7.UnmarshalSignedAttribute(oidTest, &actual)
+ if err != nil {
+ t.Fatalf("Cannot unmarshal test value: %s", err)
+ }
+ if testValue != actual {
+ t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual)
+ }
+}
+
+func TestDegenerateCertificate(t *testing.T) {
+ cert, err := createTestCertificate(x509.SHA1WithRSA)
+ if err != nil {
+ t.Fatal(err)
+ }
+ deg, err := DegenerateCertificate(cert.Certificate.Raw)
+ if err != nil {
+ t.Fatal(err)
+ }
+ testOpenSSLParse(t, deg)
+ pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg})
+}
+
+// writes the cert to a temporary file and tests that openssl can read it.
+func testOpenSSLParse(t *testing.T, certBytes []byte) {
+ tmpCertFile, err := ioutil.TempFile("", "testCertificate")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(tmpCertFile.Name()) // clean up
+
+ if _, err := tmpCertFile.Write(certBytes); err != nil {
+ t.Fatal(err)
+ }
+
+ opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name())
+ _, err = opensslCMD.Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err := tmpCertFile.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+}
+func fromHex(s string) *big.Int {
+ result, ok := new(big.Int).SetString(s, 16)
+ if !ok {
+ panic(s)
+ }
+ return result
+}
changelog/12371.txt+3 0
@@ -0,0 +1,3 @@
+```release-note: bug
+identity: Fix a panic on arm64 platform when doing identity I/O.
+```
builtin/credential/aws/pkcs7/verify_test_dsa.go+182 0
@@ -0,0 +1,182 @@
+// +build go1.11 go1.12 go1.13 go1.14 go1.15
+
+package pkcs7
+
+import (
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "testing"
+)
+
+func TestVerifyEC2(t *testing.T) {
+ fixture := UnmarshalDSATestFixture(EC2IdentityDocumentFixture)
+ p7, err := Parse(fixture.Input)
+ if err != nil {
+ t.Errorf("Parse encountered unexpected error: %v", err)
+ }
+ p7.Certificates = []*x509.Certificate{fixture.Certificate}
+ if err := p7.Verify(); err != nil {
+ t.Errorf("Verify failed with error: %v", err)
+ }
+}
+
+var EC2IdentityDocumentFixture = `
+-----BEGIN PKCS7-----
+MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA
+JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh
+eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1
+cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh
+bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs
+bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg
+OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK
+ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj
+aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy
+YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA
+AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n
+dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi
+IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B
+CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG
+CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w
+LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA
+AAAAAA==
+-----END PKCS7-----
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw
+FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD
+VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z
+ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u
+IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl
+cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e
+ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3
+VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P
+hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j
+k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U
+hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF
+lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf
+MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW
+MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw
+vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw
+7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K
+-----END CERTIFICATE-----`
+
+func TestDSASignWithOpenSSLAndVerify(t *testing.T) {
+ content := []byte(`
+A ship in port is safe,
+but that's not what ships are built for.
+-- Grace Hopper`)
+ // write the content to a temp file
+ tmpContentFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_content")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
+
+ // write the signer cert to a temp file
+ tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signer")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755)
+
+ // write the signer key to a temp file
+ tmpSignerKeyFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_key")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ioutil.WriteFile(tmpSignerKeyFile.Name(), dsaPrivateKey, 0755)
+
+ tmpSignedFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signature")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // call openssl to sign the content
+ opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", "-md", "sha1",
+ "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(),
+ "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(),
+ "-certfile", tmpSignerCertFile.Name(), "-outform", "PEM")
+ out, err := opensslCMD.CombinedOutput()
+ if err != nil {
+ t.Fatalf("openssl command failed with %s: %s", err, out)
+ }
+
+ // verify the signed content
+ pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ fmt.Printf("%s\n", pemSignature)
+ derBlock, _ := pem.Decode(pemSignature)
+ if derBlock == nil {
+ t.Fatalf("failed to read DER block from signature PEM %s", tmpSignedFile.Name())
+ }
+ p7, err := Parse(derBlock.Bytes)
+ if err != nil {
+ t.Fatalf("Parse encountered unexpected error: %v", err)
+ }
+ if err := p7.Verify(); err != nil {
+ t.Fatalf("Verify failed with error: %v", err)
+ }
+ os.Remove(tmpSignerCertFile.Name()) // clean up
+ os.Remove(tmpSignerKeyFile.Name()) // clean up
+ os.Remove(tmpContentFile.Name()) // clean up
+}
+
+var dsaPrivateKey = []byte(`-----BEGIN PRIVATE KEY-----
+MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS
+PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl
+pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith
+1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L
+vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3
+zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo
+g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUfW4aPdQBn9gJZp2KuNpzgHzvfsE=
+-----END PRIVATE KEY-----`)
+
+var dsaPublicCert = []byte(`-----BEGIN CERTIFICATE-----
+MIIDOjCCAvWgAwIBAgIEPCY/UDANBglghkgBZQMEAwIFADBsMRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du
+MB4XDTE4MTAyMjEzNDMwN1oXDTQ2MDMwOTEzNDMwN1owbDEQMA4GA1UEBhMHVW5r
+bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE
+ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC
+AbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
+Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
+exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
+Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
+V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
+puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
+nwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDCriMPbEVBoRK4SOUeFwg7+VRf4TTp
+rcOQC9IVVoCjXzuWEGrp3ZI7YWJSpFnSch4lk29RH8O0HpI/NOzKnOBtnKr782pt
+1k/bJVMH9EaLd6MKnAVjrCDMYBB0MhebZ8QHY2elZZCWoqDYAcIDOsEx+m4NLErT
+ypPnjS5M0jm1PKMhMB8wHQYDVR0OBBYEFC0Yt5XdM0Kc95IX8NQ8XRssGPx7MA0G
+CWCGSAFlAwQDAgUAAzAAMC0CFQCIgQtrZZ9hdZG1ROhR5hc8nYEmbgIUAIlgC688
+qzy/7yePTlhlpj+ahMM=
+-----END CERTIFICATE-----`)
+
+type DSATestFixture struct {
+ Input []byte
+ Certificate *x509.Certificate
+}
+
+func UnmarshalDSATestFixture(testPEMBlock string) DSATestFixture {
+ var result DSATestFixture
+ var derBlock *pem.Block
+ var pemBlock = []byte(testPEMBlock)
+ for {
+ derBlock, pemBlock = pem.Decode(pemBlock)
+ if derBlock == nil {
+ break
+ }
+ switch derBlock.Type {
+ case "PKCS7":
+ result.Input = derBlock.Bytes
+ case "CERTIFICATE":
+ result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes)
+ }
+ }
+
+ return result
+}
ui/tests/acceptance/usage-metrics-test.js+35 0
@@ -2,9 +2,22 @@ import { module, test } from 'qunit';
import { visit, currentURL, findAll } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
+import { create } from 'ember-cli-page-object';
import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout';
+import consoleClass from 'vault/tests/pages/components/console/ui-panel';
+
+const consoleComponent = create(consoleClass);
+
+const tokenWithPolicy = async function(name, policy) {
+ await consoleComponent.runCommands([
+ `write sys/policies/acl/${name} policy=${btoa(policy)}`,
+ `write -field=client_token auth/token/create policies=${name}`,
+ ]);
+
+ return consoleComponent.lastLogOutput;
+};
module('Acceptance | usage metrics', function(hooks) {
setupApplicationTest(hooks);
@@ -65,6 +78,28 @@ module('Acceptance | usage metrics', function(hooks) {
assert.equal(currentURL(), '/vault/metrics');
assert.dom('[data-test-pricing-metrics-form]').exists('Pricing metrics date form exists');
+ assert.dom('[data-test-configuration-tab]').exists('Metrics config tab exists');
+ assert.dom('[data-test-tracking-disabled]').doesNotExist('Flash message does not exists');
+ assert.ok(findAll('.selectable-card').length === 3, 'renders the counts');
+ });
+
+ test('it shows metrics even if config endpoint not allowed', async function(assert) {
+ server.create('metrics/activity');
+ const deny_config_policy = `
+ path "sys/internal/counters/config" {
+ capabilities = ["deny"]
+ },
+ `;
+
+ const userToken = await tokenWithPolicy('no-metrics-config', deny_config_policy);
+ await logout.visit();
+ await authPage.login(userToken);
+
+ await visit('/vault/metrics');
+
+ assert.equal(currentURL(), '/vault/metrics');
+ assert.dom('[data-test-pricing-metrics-form]').exists('Pricing metrics date form exists');
+ assert.dom('[data-test-configuration-tab]').doesNotExist('Metrics config tab does not exist');
assert.dom('[data-test-tracking-disabled]').doesNotExist('Flash message does not exists');
assert.ok(findAll('.selectable-card').length === 3, 'renders the counts');
});
clearing the value (#12409) (#12416)
changelog/12409.txt | 3 +++
ui/lib/core/addon/components/form-field.js | 2 +-
ui/lib/core/addon/components/masked-input.js | 6 ++++++
ui/lib/core/addon/templates/components/form-field.hbs | 6 ++----
.../core/addon/templates/components/masked-input.hbs | 4 ++++
ui/tests/integration/components/masked-input-test.js | 11 ++++++++++-
6 files changed, 26 insertions(+), 6 deletions(-)
create mode 100644 changelog/12409.txt
changelog/12151.txt+3 0
@@ -0,0 +1,3 @@
+```release-note:bug
+identity: do not allow a role's token_ttl to be longer than the signing key's verification_ttl
+```
More files changed — see the full commit.

References