Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Create the Python Track 2 SDK for the Microsoft Azure Attestation Service. #18023

Merged
merged 69 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
5c676c3
Added local autorest generation file for python
LarryOsterman Apr 9, 2021
1d57996
Extremely bare bones implementation of the start of a python SDK for …
LarryOsterman Apr 9, 2021
21a1069
Added local autorest generation file for python
LarryOsterman Apr 9, 2021
29b1b2d
Extremely bare bones implementation of the start of a python SDK for …
LarryOsterman Apr 9, 2021
deb3fbd
Merge branch 'LarryO-CreatePythonSDK' of https://github.com/LarryOste…
LarryOsterman Apr 12, 2021
38739ee
Added get_policy API and some more infrastructure
LarryOsterman Apr 12, 2021
7bdbb8d
Python starts to limp - get_policy and set_policy implemented
LarryOsterman Apr 13, 2021
cbd6717
Merge branch 'master' into LarryO-CreatePythonSDK
LarryOsterman Apr 13, 2021
6ba4a1e
Fixed typo
LarryOsterman Apr 13, 2021
bcf96d5
CI Test pass fixes
LarryOsterman Apr 13, 2021
1b50afa
Test pass setup
LarryOsterman Apr 13, 2021
3487871
Removed duplicate attestation_location_short_name
LarryOsterman Apr 13, 2021
5e1a5d3
More CI pipeline fixes
LarryOsterman Apr 13, 2021
5409a03
Converted async test to use TestPreparer; re-ran recorded tests
LarryOsterman Apr 13, 2021
d517396
Only check issuer on live tests
LarryOsterman Apr 13, 2021
98fe4fd
Updates to unit tests, recorded tests now all pass
LarryOsterman Apr 14, 2021
4df18a1
Removed Python 3 specific constructs
LarryOsterman Apr 14, 2021
a63cbb4
A couple more Py3 constructs
LarryOsterman Apr 14, 2021
eb68997
More Py3 constructs
LarryOsterman Apr 14, 2021
af65901
Hopefully final Py3 constructs
LarryOsterman Apr 14, 2021
33635d4
Try passing bytes into base64 decoder, not str
LarryOsterman Apr 14, 2021
a6b3791
Another Py3/Py27 fix
LarryOsterman Apr 14, 2021
08362e1
Try to remove envvars from tests.yml
LarryOsterman Apr 14, 2021
7f6a4b5
Try to remove envvars from tests.yml
LarryOsterman Apr 14, 2021
3da92db
Validate attestation tokens
LarryOsterman Apr 15, 2021
22f6ed8
Convert bytes to str to make json encoder happy
LarryOsterman Apr 15, 2021
0b91fbb
Changed how we convert base64 to string
LarryOsterman Apr 15, 2021
110c66d
Added test for secured and unsecured policy set
LarryOsterman Apr 15, 2021
4bfbe1d
Added attestation team to codeowners
LarryOsterman Apr 15, 2021
daa6aa7
Pass in type of AttestationToken into constructor, making it dramatic…
LarryOsterman Apr 16, 2021
26f4cd6
Start converting attributes in AttestationToken to properties
LarryOsterman Apr 16, 2021
2ac1c55
Added support for sgx and openenclave, and tests for both
LarryOsterman Apr 17, 2021
2dcb2bc
Added kwargs to all the calls into generated code
LarryOsterman Apr 19, 2021
e7ed6ad
Updated recordings
LarryOsterman Apr 19, 2021
a059294
Merge branch 'master' into LarryO-CreatePythonSDK
LarryOsterman Apr 19, 2021
521c1d1
Update sdk/attestation/azure-security-attestation/SWAGGER.md
LarryOsterman Apr 19, 2021
27492ca
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman Apr 19, 2021
9fd1db6
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman Apr 19, 2021
38ab274
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman Apr 19, 2021
8e52d67
Update sdk/attestation/azure-security-attestation/tests/test_attestat…
LarryOsterman Apr 19, 2021
e399c3e
Next round of pull request feedback
LarryOsterman Apr 19, 2021
ad5a401
Integrated first round of API changes from .Net API review; Removed C…
LarryOsterman May 4, 2021
1cff859
Client documentation updates
LarryOsterman May 4, 2021
b91bb79
Ported readme.md from .Net to Python - snippets are still .Net unfort…
LarryOsterman May 4, 2021
e8efb6b
Sphinx updates
LarryOsterman May 4, 2021
9d48ebf
Updates to readme.md to include python snippets
LarryOsterman May 5, 2021
92a4a54
More readme CI changes
LarryOsterman May 5, 2021
c94d7ff
Removed en-us from link
LarryOsterman May 5, 2021
07aae3c
Finished attestation callback test; re-generated swagger; moved swagg…
LarryOsterman May 5, 2021
1836e36
Working and tests for add policy certificate
LarryOsterman May 6, 2021
3ccee2b
CI Fixes; cleaned up more configuration options
LarryOsterman May 6, 2021
1ef5652
Added attestation swagger to docsettings
LarryOsterman May 6, 2021
545bbda
Updated recordings; added TPM attestation test; added remove policy m…
LarryOsterman May 6, 2021
2c6d43e
Added recordings for TPM attestation
LarryOsterman May 6, 2021
fc4c333
Copyright note updates
LarryOsterman May 6, 2021
df6c230
Fixed typos in CODEOWNERS file
LarryOsterman May 6, 2021
cc55553
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman May 6, 2021
8f469a2
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman May 6, 2021
724aa34
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman May 6, 2021
a60e1e8
Update sdk/attestation/azure-security-attestation/azure/security/atte…
LarryOsterman May 7, 2021
516ff1f
Removed a couple of ._generated exported types; cleaned up token vali…
LarryOsterman May 7, 2021
df8c4c1
Removed generated types from __init__.py
LarryOsterman May 7, 2021
25a6542
More pull request feedback
LarryOsterman May 7, 2021
d71ea80
Tweaks to client API - cleaned up some documentation; use list to fil…
LarryOsterman May 7, 2021
2f6e9aa
Merge branch 'master' into LarryO-CreatePythonSDK
LarryOsterman May 7, 2021
3acce02
More documentation cleanup; minor changes to async test
LarryOsterman May 7, 2021
6a5cbd0
lmanzuel first round of pull request feedback
LarryOsterman May 7, 2021
cadb558
Removed base_url attrbute/property
LarryOsterman May 7, 2021
c0e4d88
Attestation requires cryptography
LarryOsterman May 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
/sdk/applicationinsights/azure-applicationinsights/ @divya-jay @geneh @alongafni
/sdk/loganalytics/azure-loganalytics/ @divya-jay @geneh @alongafni

/sdk/attestation/azure-security-attestation @larryosterman @anilba06 @Azure/azure-sdk-write-attestation

# PRLabel: %Batch
/sdk/batch/ @cRui861 @paterasMSFT @dpwatrous @gingi @zfengms
/sdk/cognitiveservices/azure-cognitiveservices-vision-customvision/ @areddish
Expand Down
1 change: 1 addition & 0 deletions eng/.docsettings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ known_content_issues:
- ['sdk/media/azure-media-nspkg/README.md', '#4554']
- ['sdk/containerregistry/azure-containerregistry/swagger/README.md', '#4554']
- ['sdk/appconfiguration/azure-appconfiguration/swagger/README.md', '#4554']
- ['sdk/attestation/azure-security-attestation/swagger/README.md', '#4554']

# nspckg and common.
- ['sdk/appconfiguration/azure-appconfiguration/README.md', 'nspkg and common']
Expand Down
10 changes: 9 additions & 1 deletion sdk/attestation/azure-security-attestation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# Release History

## 1.0.0b2 (Unreleased)
## 1.0.0b2 (2021-05-11)

### Features Added

- Preliminary implementation of a Track 2 SDK for the attestation service.

### Breaking Changes

- Complete reimplementation of the API surface, follows the API patterns already
established for the attestation service.

## 1.0.0b1 (2021-01-15)

Expand Down
356 changes: 334 additions & 22 deletions sdk/attestation/azure-security-attestation/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,47 @@
# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------

from ._attestation_client import AttestationClient
from ._client import AttestationClient
from ._administration_client import AttestationAdministrationClient
from ._models import (AttestationResponse,
AttestationSigner,
AttestationToken,
AttestationSigningKey,
AttestationData,
PolicyResult,
AttestationResult,
TpmAttestationResponse,
TpmAttestationRequest,
AttestationTokenValidationException,
PolicyCertificatesModificationResult,
AttestationType,
StoredAttestationPolicy,
CertificateModification)
from ._configuration import TokenValidationOptions
from ._version import VERSION

__version__ = VERSION
__all__ = ['AttestationClient']
__all__ = [
'AttestationClient',
'AttestationAdministrationClient',
'AttestationType',
'AttestationToken',
'AttestationSigner',
'AttestationResponse',
'AttestationResult',
'AttestationData',
'TokenValidationOptions',
'StoredAttestationPolicy',
'PolicyResult',
'CertificateModification',
'AttestationSigningKey',
'TpmAttestationRequest',
'TpmAttestationResponse',
'PolicyCertificatesModificationResult',
'AttestationTokenValidationException',
]

try:
from ._patch import patch_sdk # type: ignore
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# coding=utf-8
# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------

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:
# pylint: disable=unused-import,ungrouped-imports
from typing import Any

from azure.core.credentials import TokenCredential
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 ._configuration import AttestationClientConfiguration
from ._models import AttestationSigner, AttestationToken, AttestationResponse, StoredAttestationPolicy, AttestationSigningKey, PolicyCertificatesModificationResult
from ._common import Base64Url
import cryptography
import cryptography.x509
import base64
from azure.core.tracing.decorator import distributed_trace
from threading import Lock, Thread


class AttestationAdministrationClient(object):
"""Provides administrative APIs for managing an instance of the Attestation Service.

:param str instance_url: base url of the service
:param credential: An object which can provide secrets for the attestation service
:type credential: azure.core.credentials.TokenCredential
:keyword Pipeline pipeline: If omitted, the standard pipeline is used.
:keyword HttpTransport transport: If omitted, the standard pipeline is used.
:keyword list[HTTPPolicy] policies: If omitted, the standard pipeline is used.
"""

def __init__(
self,
credential, # type: "TokenCredential"
instance_url, # type: str
**kwargs # type: Any
):
# type: (...) -> None
self._base_url = instance_url
if not credential:
raise ValueError("Missing credential.")
self._config = AttestationClientConfiguration(credential, instance_url, **kwargs)
self._client = AzureAttestationRestClient(credential, instance_url, **kwargs)
self._statelock = Lock()
self._signing_certificates = None

@property
def base_url(self):
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
#type:()->str
""" Returns the base URL configured for this instance of the AttestationClient.

:returns str: The base URL for the client instance.
"""
return self._base_url

@distributed_trace
def get_policy(self, attestation_type, **kwargs):
#type(AttestationType) -> AttestationResult[str]:
""" Retrieves the attestation policy for a specified attestation type.

:param azure.security.attestation.AttestationType attestation_type: :class:`azure.security.attestation.AttestationType` for
which to retrieve the policy.
:return AttestationResponse[str]: Attestation service response encapsulating a string attestation policy.
"""

policyResult = self._client.policy.get(attestation_type, **kwargs)
token = AttestationToken[PolicyResult](token=policyResult.token, body_type=PolicyResult)
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
token_body = token.get_body()
stored_policy = AttestationToken[StoredAttestationPolicy](token=token_body.policy, body_type=StoredAttestationPolicy)

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))

return AttestationResponse[str](token, actual_policy.decode('utf-8'))
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved

@distributed_trace
def set_policy(self, attestation_type, attestation_policy, signing_key=None, **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 AttestationSigningKey signing_key: Optional 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`.
"""
policy_token = AttestationToken[StoredAttestationPolicy](
body=StoredAttestationPolicy(attestation_policy = attestation_policy.encode('ascii')),
signer=signing_key,
body_type=StoredAttestationPolicy)
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)
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.")
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved


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

@distributed_trace
def get_policy_management_certificates(self, **kwargs):
#type:(Any) -> AttestationResponse[list[list[bytes]]]
""" Retrieves the set of policy management certificates for the instance.

The list of policy management certificates will only be non-empty if the
attestation service instance is in Isolated mode.

:return AttestationResponse[list[list[bytes]]: Attestation service response
encapsulating a list of DER encoded X.509 certificate chains.
"""

cert_response = self._client.policy_certificates.get(**kwargs)
token = AttestationToken[PolicyCertificatesResult](
token=cert_response.token,
body_type=PolicyCertificatesResult)
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 PolicyCertificates API failed.")
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
certificates = []

cert_list = token.get_body()

for key in cert_list.policy_certificates.keys:
key_certs = list()
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
for cert in key.x5_c:
key_certs.append(base64.b64decode(cert))
certificates.append(key_certs)
return AttestationResponse[list](token, certificates)
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved

@distributed_trace
def add_policy_management_certificate(self, certificate_to_add, signing_key, **kwargs):
#type:(bytes, AttestationSigningKey, Any)-> AttestationResponse[PolicyCertificatesModificationResult]
""" Adds a new policy management certificate to the set of policy management certificates for the instance.

:param bytes certificate_to_add: DER encoded X.509 certificate to add to
the list of attestation policy management certificates.
:param AttestationSigningKey signing_key: Signing Key representing one of
the *existing* attestation signing certificates.
:return AttestationResponse[PolicyCertificatesModificationResult]: Attestation service response
encapsulating a list of DER encoded X.509 certificate chains.
"""
key=JSONWebKey(kty='RSA', x5_c = [ base64.b64encode(certificate_to_add).decode('ascii')])
add_body = AttestationCertificateManagementBody(policy_certificate=key)
cert_add_token = AttestationToken[AttestationCertificateManagementBody](
body=add_body,
signer=signing_key,
body_type=AttestationCertificateManagementBody)

cert_response = self._client.policy_certificates.add(cert_add_token.serialize(), **kwargs)
token = AttestationToken[GeneratedPolicyCertificatesModificationResult](token=cert_response.token,
body_type=GeneratedPolicyCertificatesModificationResult)
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 PolicyCertificate Add API failed.")
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
return AttestationResponse[PolicyCertificatesModificationResult](token, PolicyCertificatesModificationResult._from_generated(token.get_body()))

@distributed_trace
def remove_policy_management_certificate(self, certificate_to_add, signing_key, **kwargs):
#type:(bytes, AttestationSigningKey, Any)-> AttestationResponse[PolicyCertificatesModificationResult]
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
""" Removes a new policy management certificate to the set of policy management certificates for the instance.

:param bytes certificate_to_add: DER encoded X.509 certificate to add to
the list of attestation policy management certificates.
:param AttestationSigningKey signing_key: Signing Key representing one of
the *existing* attestation signing certificates.
:return AttestationResponse[PolicyCertificatesModificationResult]: Attestation service response
encapsulating a list of DER encoded X.509 certificate chains.
"""
key=JSONWebKey(kty='RSA', x5_c = [ base64.b64encode(certificate_to_add).decode('ascii')])
add_body = AttestationCertificateManagementBody(policy_certificate=key)
cert_add_token = AttestationToken[AttestationCertificateManagementBody](
body=add_body,
signer=signing_key,
body_type=AttestationCertificateManagementBody)

cert_response = self._client.policy_certificates.remove(cert_add_token.serialize(), **kwargs)
token = AttestationToken[GeneratedPolicyCertificatesModificationResult](token=cert_response.token,
body_type=GeneratedPolicyCertificatesModificationResult)
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 PolicyCertificate Remove API failed.")
return AttestationResponse[PolicyCertificatesModificationResult](token, PolicyCertificatesModificationResult._from_generated(token.get_body()))

def _get_signers(self, **kwargs):
#type(Any) -> List[AttestationSigner]
""" Returns the set of signing certificates used to sign attestation tokens.
"""

with self._statelock:
if (self._signing_certificates == None):
signing_certificates = self._client.signing_certificates.get(**kwargs)
self._signing_certificates = []
for key in signing_certificates.keys:
# Convert the returned certificate chain into an array of X.509 Certificates.
certificates = []
for x5c in key.x5_c:
der_cert = base64.b64decode(x5c)
certificates.append(der_cert)
self._signing_certificates.append(AttestationSigner(certificates, key.kid))
signers = self._signing_certificates
return signers

def close(self):
# type: () -> None
self._client.close()

def __enter__(self):
# type: () -> AttestationAdministrationClient
self._client.__enter__()
return self

def __exit__(self, *exc_details):
# type: (Any) -> None
self._client.__exit__(*exc_details)
Loading