Skip to content

Commit

Permalink
Added policy Reset APIs; Cleaned up several model types. (#18679)
Browse files Browse the repository at this point in the history
  • Loading branch information
LarryOsterman authored May 13, 2021
1 parent 064302e commit 494f407
Show file tree
Hide file tree
Showing 34 changed files with 1,107 additions and 306 deletions.
57 changes: 56 additions & 1 deletion sdk/attestation/azure-security-attestation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,62 @@
# Release History

## 1.0.0b3 (Unreleased)
## 1.0.0b3 (2021-05-12)

### Features Added

- Added reset_policy API which was missed in the previous API.
- Added models for all the generated API types.
- Documentation cleanup for several APIs.

### Breaking Changes

- Creating the `StoredAttestationPolicy` model type means that the `attestation_policy`
kwargs parameter for the constructor has been replaced with a positional `policy` parameter. As a result of this change, this code:

```python
StoredAttestationPolicy(attestation_policy=str(attestation_policy).encode('utf-8')))
```

changes to:

```python
StoredAttestationPolicy(attestation_policy)
```

- Several parameters for the `AttestationResult` type have been renamed, and
several parameters which were shared with `AttestationToken` have been
removed. In general, the naming changes removed some protocol specific
elements and replaced them with friendlier names. Finally, the deprecated
attributes have been removed from the `AttestationResult`

Full set of changes:
- `iss` renamed to `issuer`
- `cnf` renamed to `confirmation`
- `jti` renamed to `unique_identifier`
- `iat` removed
- `exp` removed
- `nbf` removed
- `deprecated_version` removed
- `deprecated_is_debuggable` removed
- `deprecated_sgx_collateral` removed
- `deprecated_enclave_held_data` removed
- `deprecated_enclave_held_data2` removed
- `deprecated_product_id` removed
- `deprecated_mr_enclave` removed
- `deprecated_mr_signer` removed
- `deprecated_svn` removed
- `deprecated_tee` removed
- `deprecated_policy_signer` removed
- `deprecated_policy_hash` removed
- `deprecated_rp_data` removed

If customers need to access the removed or renamed fields directly, they can
use the `get_body` method of the `AttestationResponse` object:

```python
if response.token.get_body().deprecated_tee != 'sgx':
print("Unexpected tee claim in token")
```

## 1.0.0b2 (2021-05-11)

Expand Down
35 changes: 21 additions & 14 deletions sdk/attestation/azure-security-attestation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ For a more complete view of Azure libraries, see the [azure sdk python release](

### Install the package

Install the Microsoft Azure Attestation client library for .NET with [PyPI][Attestation_pypi]:
Install the Microsoft Azure Attestation client library for Python with [PyPI][Attestation_pypi]:

```Powershell
pip install --pre azure-security-attestation
Expand Down Expand Up @@ -149,7 +149,7 @@ Currently, MAA supports the following Trusted Execution environments:

### Runtime Data and Inittime Data

RuntimeData refers to data which is presented to the Intel SGX Quote generation logic or the `oe_get_report`/`oe_get_evidence` APIs. The Azure Attestation service will validate that the first 32 bytes of the `report_data` field in the SGX Quote/OE Report/OE Evidence matches the SHA256 hash of the RuntimeData.
RuntimeData refers to data which is presented to the Intel SGX Quote generation logic or the `oe_get_report`/`oe_get_evidence` APIs. If the caller to the attest API provided a `runtime_data` attribute, The Azure Attestation service will validate that the first 32 bytes of the `report_data` field in the SGX Quote/OE Report/OE Evidence matches the SHA256 hash of the `runtime_data`.

InitTime data refers to data which is used to configure the SGX enclave being attested.

Expand Down Expand Up @@ -215,8 +215,8 @@ Clients need to be able to verify that the attestation policy document was not m

There are two properties provided in the [PolicyResult][attestation_policy_result] that can be used to verify that the service received the policy document:

* [`PolicySigner`][attestation_policy_result_signer] - if the `set_policy` call included a signing certificate, this will be the certificate provided at the time of the `set_policy` call. If no policy signer was set, this will be null.
* [`PolicyTokenHash`][attestation_policy_result_token_hash] - this is the hash of the [JSON Web Token][json_web_token] sent to the service.
* [`policy_signer`][attestation_policy_result_parameters] - if the `set_policy` call included a signing certificate, this will be the certificate provided at the time of the `set_policy` call. If no policy signer was set, this will be null.
* [`policy_token_hash`][attestation_policy_result_parameters] - this is the hash of the [JSON Web Token][json_web_token] sent to the service.

To verify the hash, clients can generate an attestation token and verify the hash generated from that token:

Expand All @@ -227,9 +227,8 @@ To verify the hash, clients can generate an attestation token and verify the has
# verify that the hash of the policy document returned from the Attestation
# Service matches the hash of an attestation token created locally.
expected_policy = AttestationToken(
body=StoredAttestationPolicy(
attestation_policy=str(attestation_policy).encode('ascii')),
signer=AttestationSigningKey(key, signing_certificate))
body=StoredAttestationPolicy(attestation_policy),
signer=AttestationSigningKey(key, signing_certificate))
hasher = hashes.Hash(hashes.SHA256())
hasher.update(expected_policy.serialize().encode('utf-8'))
expected_hash = hasher.finalize()
Expand All @@ -239,11 +238,11 @@ To verify the hash, clients can generate an attestation token and verify the has

### Attest SGX Enclave

Use the `AttestSgxEnclave` method to attest an SGX enclave.
Use the [`attest_sgx`][attest_sgx] method to attest an SGX enclave.

One of the core challenges customers have interacting with encrypted environments is how to ensure that you can reliably communicate with the code running in the environment ("enclave code").
One of the core challenges customers have interacting with encrypted environments is how to ensure that you can securely communicate with the code running in the environment ("enclave code").

One solution to this problem is what is known as "Secure Key Release", which is a pattern that enables this kind of communication with enclave code.
One solution to this problem is what is known as "Secure Key Release", which is a pattern that enables secure communication with enclave code.

To implement the "Secure Key Release" pattern, the enclave code generates an ephemeral asymmetric key. It then serializes the public portion of the key to some format (possibly a JSON Web Key, or PEM, or some other serialization format).

Expand All @@ -255,7 +254,7 @@ The client can then send that Attestation Token (which contains the serialized k

This example shows one common pattern of calling into the attestation service to retrieve an attestation token associated with a request.

This example assumes that you have an existing `AttestationClient` object which is configured with the base URI for your endpoint. It also assumes that you have an SGX Quote (`binaryQuote`) generated from within the SGX enclave you are attesting, and "Runtime Data" (`runtimeData`) which is referenced in the SGX Quote.
This example assumes that you have an existing `AttestationClient` object which is configured with the base URI for your endpoint. It also assumes that you have an SGX Quote (`quote`) generated from within the SGX enclave you are attesting, and "Runtime Data" (`runtime_data`) which is referenced in the SGX Quote.

```python
# Collect quote and runtime data from an SGX enclave.
Expand Down Expand Up @@ -288,11 +287,11 @@ This example assumes that you have an existing `AttestationClient` object which
# Now the encrypted data can be passed into the enclave which can decrypt that data.
```

Additional information on how to perform attestation token validation can be found in the [MAA Service Attestation Sample](https://github.com/gkostal/attestation/tree/d6a216cd6af5a509e20ac0a752197fdb242fabc3/sgx.attest.sample).
Additional information on how to perform attestation token validation can be found in the [MAA Service Attestation Sample](https://github.com/Azure-Samples/microsoft-azure-attestation).

### Retrieve Token Certificates

Use `GetSigningCertificatesAsync` to retrieve the certificates which can be used to validate the token returned from the attestation service.
Use `get_signing_certificates` to retrieve the certificates which can be used to validate the token returned from the attestation service.

```python
signers = attest_client.get_signing_certificates()
Expand All @@ -317,7 +316,7 @@ Most Attestation service operations will raise exceptions defined in [Azure Core
}
```

Additional troubleshooting information for the MAA service can be found [here](https://docs.microsoft.com/azure/attestation/troubleshoot-guide)
Additional troubleshooting information for the MAA service can be found [here](https://docs.microsoft.com/python/api/overview/azure/attestation?view=azure-python-preview)

## Next steps

Expand All @@ -341,6 +340,14 @@ section of the project.

<!-- LINKS -->
[source_code]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/attestation/azure-security-attestation
[azure_identity]: https://docs.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python-preview
[DefaultAzureCredential]: https://docs.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential?view=azure-python-preview
[attestation_policy_result]:https://docs.microsoft.com/python/api/azure-security-attestation/azure.security.attestation.policyresult?view=azure-python-preview
[attestation_client]: https://docs.microsoft.com/python/api/azure-security-attestation/azure.security.attestation.attestationclient?view=azure-python-preview
[attestation_admin_client]: https://docs.microsoft.com/python/api/azure-security-attestation/azure.security.attestation.attestationadministrationclient?view=azure-python-preview
[attestation_response]: https://docs.microsoft.com/python/api/azure-security-attestation/azure.security.attestation.attestationresponse?view=azure-python-preview
[attestation_policy_result_parameters]: https://docs.microsoft.com/python/api/azure-security-attestation/azure.security.attestation.policyresult?view=azure-python-preview#parameters
[attest_sgx]: https://docs.microsoft.com/python/api/azure-security-attestation/azure.security.attestation.attestationclient?view=azure-python-preview#attest-sgx-enclave-quote--inittime-data-none--runtime-data-none--draft-policy-none----kwargs-
[attestation_pypi]: https://aka.ms/azsdk/python/azure-security-attestation
[API_reference]:https://docs.microsoft.com/python/api/overview/azure/security-attestation-readme?view=azure-python-preview
[style-guide-msft]: https://docs.microsoft.com/style-guide/capitalization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
AttestationTokenValidationException,
PolicyCertificatesModificationResult,
AttestationType,
PolicyModification,
StoredAttestationPolicy,
CertificateModification)
from ._configuration import TokenValidationOptions
Expand All @@ -40,6 +41,7 @@
'AttestationSigningKey',
'TpmAttestationRequest',
'TpmAttestationResponse',
'PolicyModification',
'PolicyCertificatesModificationResult',
'AttestationTokenValidationException',
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from typing import List, Any, Optional, TYPE_CHECKING

from azure.core import PipelineClient
from msrest import Deserializer, Serializer
from six import python_2_unicode_compatible

if TYPE_CHECKING:
Expand All @@ -18,12 +17,25 @@
from azure.core.pipeline.transport import HttpRequest, HttpResponse

from ._generated import AzureAttestationRestClient
from ._generated.models import AttestationType, PolicyResult, PolicyCertificatesResult, JSONWebKey, AttestationCertificateManagementBody, PolicyCertificatesModificationResult as GeneratedPolicyCertificatesModificationResult
from ._generated.models import (
AttestationType,
PolicyResult as GeneratedPolicyResult,
PolicyCertificatesResult,
JSONWebKey,
AttestationCertificateManagementBody,
StoredAttestationPolicy as GeneratedStoredAttestationPolicy,
PolicyCertificatesModificationResult as GeneratedPolicyCertificatesModificationResult
)
from ._configuration import AttestationClientConfiguration
from ._models import AttestationSigner, AttestationToken, AttestationResponse, StoredAttestationPolicy, AttestationSigningKey, PolicyCertificatesModificationResult
from ._common import Base64Url
import cryptography
import cryptography.x509
from ._models import (
AttestationSigner,
AttestationToken,
AttestationResponse,
AttestationSigningKey,
PolicyCertificatesModificationResult,
PolicyResult,
AttestationTokenValidationException
)
import base64
from azure.core.tracing.decorator import distributed_trace
from threading import Lock, Thread
Expand Down Expand Up @@ -63,31 +75,35 @@ def get_policy(self, attestation_type, **kwargs):
which to retrieve the policy.
:return AttestationResponse[str]: Attestation service response encapsulating a string attestation policy.
:raises AttestationTokenValidationException: Raised when an attestation token is invalid.
"""

policyResult = self._client.policy.get(attestation_type, **kwargs)
token = AttestationToken[PolicyResult](token=policyResult.token, body_type=PolicyResult)
token = AttestationToken[GeneratedPolicyResult](token=policyResult.token, body_type=GeneratedPolicyResult)
token_body = token.get_body()
stored_policy = AttestationToken[StoredAttestationPolicy](token=token_body.policy, body_type=StoredAttestationPolicy)
stored_policy = AttestationToken[GeneratedStoredAttestationPolicy](token=token_body.policy, body_type=GeneratedStoredAttestationPolicy)

actual_policy = stored_policy.get_body().attestation_policy #type: bytes

if self._config.token_validation_options.validate_token:
token.validate_token(self._config.token_validation_options, self._get_signers(**kwargs))
if not token.validate_token(self._config.token_validation_options, self._get_signers(**kwargs)):
raise AttestationTokenValidationException("Token Validation of get_policy API failed.")

return AttestationResponse[str](token, actual_policy.decode('utf-8'))

@distributed_trace
def set_policy(self, attestation_type, attestation_policy, signing_key=None, **kwargs):
def set_policy(self, attestation_type, attestation_policy, **kwargs):
#type:(AttestationType, str, Optional[AttestationSigningKey], **Any) -> AttestationResponse[PolicyResult]
""" Sets the attestation policy for the specified attestation type.
:param azure.security.attestation.AttestationType attestation_type: :class:`azure.security.attestation.AttestationType` for
which to set the policy.
:param str attestation_policy: Attestation policy to be set.
:param Optional[AttestationSigningKey] signing_key: Optional signing key to be
:keyword AttestationSigningKey signing_key: Signing key to be
used to sign the policy before sending it to the service.
:return AttestationResponse[PolicyResult]: Attestation service response encapsulating a :class:`PolicyResult`.
:raises AttestationTokenValidationException: Raised when an attestation token is invalid.
.. note::
If the attestation instance is in *Isolated* mode, then the
Expand All @@ -96,20 +112,57 @@ def set_policy(self, attestation_type, attestation_policy, signing_key=None, **k
If the attestation instance is in *AAD* mode, then the `signing_key`
parameter does not need to be provided.
"""
policy_token = AttestationToken[StoredAttestationPolicy](
body=StoredAttestationPolicy(attestation_policy = attestation_policy.encode('ascii')),

signing_key = kwargs.get('signing_key', None) #type:AttestationSigningKey
policy_token = AttestationToken[GeneratedStoredAttestationPolicy](
body=GeneratedStoredAttestationPolicy(attestation_policy = attestation_policy.encode('ascii')),
signer=signing_key,
body_type=StoredAttestationPolicy)
body_type=GeneratedStoredAttestationPolicy)
policyResult = self._client.policy.set(attestation_type=attestation_type, new_attestation_policy=policy_token.serialize(), **kwargs)
token = AttestationToken[PolicyResult](token=policyResult.token,
body_type=PolicyResult)
token = AttestationToken[GeneratedPolicyResult](token=policyResult.token,
body_type=GeneratedPolicyResult)
if self._config.token_validation_options.validate_token:
if not token.validate_token(self._config.token_validation_options, self._get_signers(**kwargs)):
raise AttestationTokenValidationException("Token Validation of set_policy API failed.")

return AttestationResponse[PolicyResult](token, PolicyResult._from_generated(token.get_body()))

@distributed_trace
def reset_policy(self, attestation_type, **kwargs):
#type:(AttestationType, **dict[str, Any]) -> AttestationResponse[PolicyResult]
""" Resets the attestation policy for the specified attestation type to the default value.
:param azure.security.attestation.AttestationType attestation_type: :class:`azure.security.attestation.AttestationType` for
which to set the policy.
:param str attestation_policy: Attestation policy to be reset.
:keyword AttestationSigningKey signing_key: Signing key to be
used to sign the policy before sending it to the service.
:return AttestationResponse[PolicyResult]: Attestation service response encapsulating a :class:`PolicyResult`.
:raises AttestationTokenValidationException: Raised when an attestation token is invalid.
.. note::
If the attestation instance is in *Isolated* mode, then the
`signing_key` parameter MUST be a signing key containing one of the
certificates returned by :meth:`get_policy_management_certificates`.
If the attestation instance is in *AAD* mode, then the `signing_key`
parameter does not need to be provided.
"""
signing_key = kwargs.get('signing_key', None) #type:AttestationSigningKey
policy_token = AttestationToken(
body=None,
signer=signing_key)
policyResult = self._client.policy.reset(attestation_type=attestation_type, policy_jws=policy_token.serialize(), **kwargs)
token = AttestationToken[GeneratedPolicyResult](token=policyResult.token,
body_type=GeneratedPolicyResult)
if self._config.token_validation_options.validate_token:
if not token.validate_token(self._config.token_validation_options, self._get_signers(**kwargs)):
raise Exception("Token Validation of PolicySet API failed.")
raise AttestationTokenValidationException("Token Validation of reset_policy API failed.")

return AttestationResponse[PolicyResult](token, PolicyResult._from_generated(token.get_body()))

return AttestationResponse[PolicyResult](token, token.get_body())

@distributed_trace
def get_policy_management_certificates(self, **kwargs):
Expand Down
Loading

0 comments on commit 494f407

Please sign in to comment.