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 15 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
38 changes: 38 additions & 0 deletions sdk/attestation/azure-security-attestation/SWAGGER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Azure Mixed Reality Authentication Service client library for Python

## Setup

```ps
npm install -g autorest
```

## Generation

```ps
cd <swagger-folder>
autorest SWAGGER.md
```

### Code generation settings

```yaml
title: AzureAttestationRestClient
require:
- https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/attestation/data-plane/readme.md
output-folder: azure/security/attestation/_generated
namespace: azure.security.attestation._generated
no-namespace-folders: true
license-header: MICROSOFT_MIT_NO_VERSION
tag: package-2020-10-01
package-version: 1.0.0b2
enable-xml: false
clear-output-folder: true
python: true
v3: true
add-credentials: true
azure-arm: false
payload-flattening-threshold: 2
package-name: azure-attestation
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
credential-scopes: 'https://attest.azure.net/.default'

```
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,24 @@
# 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 AttestationResult, AttestationSigner, AttestationToken, SigningKey
from ._generated.models import AttestationType
from ._configuration import TokenValidationOptions
from ._version import VERSION

__version__ = VERSION
__all__ = ['AttestationClient']
__all__ = [
'AttestationClient',
'AttestationAdministrationClient',
'AttestationType',
'AttestationToken',
'AttestationSigner',
'AttestationResult',
'TokenValidationOptions',
'SigningKey',
]

try:
from ._patch import patch_sdk # type: ignore
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# 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.
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------

from typing import 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, StoredAttestationPolicy
from ._configuration import AttestationClientConfiguration
from ._models import AttestationSigner, AttestationToken, AttestationResult, StoredAttestationPolicy
from ._common import Base64Url
import cryptography
import cryptography.x509
import base64
from typing import List, Any
from azure.core.tracing.decorator import distributed_trace
from threading import Lock, Thread


class AttestationAdministrationClient(object):
"""Describes the interface for the per-tenant enclave service.
:param str base_url: base url of the service
:param credential: An object which can provide secrets for the attestation service
:type credential: azure.TokenCredentials or azure.AsyncTokenCredentials
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
: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: (str, Any, dict) -> None
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
base_url = '{instanceUrl}'
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

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

policyResult = self._client.policy.get(attestation_type)
token = AttestationToken[PolicyResult](token=policyResult.token)
token_body = token.get_body()
stored_policy = AttestationToken[StoredAttestationPolicy](token=token_body['x-ms-policy'])

actual_policy = Base64Url.decode(stored_policy.get_body()['AttestationPolicy']).decode()

if self._config.token_validation_options.validate_token:
token.validate_token(self._config.token_validation_options, self._get_signers())

return AttestationResult[str](token, actual_policy)

@distributed_trace
def set_policy(self, attestation_type: AttestationType, attestation_policy: str, signing_key=None) -> AttestationResult[PolicyResult]:
base64_policy = Base64Url.encode(attestation_policy.encode('utf-8'))
policy_token = AttestationToken[StoredAttestationPolicy](body={"AttestationPolicy": base64_policy})
policyResult = self._client.policy.set(attestation_type=attestation_type, new_attestation_policy=policy_token.serialize())
token = AttestationToken[PolicyResult](token=policyResult.token)
if self._config.token_validation_options.validate_token:
token.validate_token(self._config.token_validation_options, self._get_signers())

return AttestationResult[str](token, token.get_body())


@distributed_trace
def _get_signers(self) -> 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()
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)
cert = cryptography.x509.load_der_x509_certificate(der_cert)
certificates.append(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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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.
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------

from typing import TYPE_CHECKING

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

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 ._configuration import AttestationClientConfiguration
from ._models import AttestationSigner
import base64
import cryptography
import cryptography.x509
from typing import List, Any
from azure.core.tracing.decorator import distributed_trace


class AttestationClient(object):
"""Describes the interface for the per-tenant enclave service.
:param str base_url: base url of the service
:param credential: An object which can provide secrets for the attestation service
:type credential: azure.TokenCredentials or azure.AsyncTokenCredentials
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
: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: (str, Any, dict) -> None
base_url = '{instanceUrl}'
if not credential:
raise ValueError("Missing credential.")
self._config = AttestationClientConfiguration(credential, instance_url, **kwargs)
self._client = AzureAttestationRestClient(credential, instance_url, **kwargs)

@distributed_trace
def get_openidmetadata(self):
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
""" Retrieves the OpenID metadata configuration document for this attestation instance.
"""
return self._client.metadata_configuration.get()

@distributed_trace
def get_signing_certificates(self): # type: () ->List[AttestationSigner]
""" Returns the set of signing certificates used to sign attestation tokens.
"""
signing_certificates = self._client.signing_certificates.get()
assert signing_certificates.keys is not None
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
signers = []
for key in signing_certificates.keys:
assert key.x5_c is not None
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved

# Convert the returned certificate chain into an array of X.509 Certificates.
certificates = []
for x5c in key.x5_c:
der_cert = base64.b64decode(x5c)
cert = cryptography.x509.load_der_x509_certificate(der_cert)
certificates.append(cert)
signers.append(AttestationSigner(certificates, key.kid))
return signers

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

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

def __exit__(self, *exc_details):
# type: (Any) -> None
self._client.__exit__(*exc_details)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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.
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------
from typing import TYPE_CHECKING

if TYPE_CHECKING:
# pylint: disable=unused-import,ungrouped-imports
from typing import Any


import base64

class Base64Url:
"""Equivalent to base64.urlsafe_b64encode, but strips padding from the encoded and decoded strings.
"""
@staticmethod
def encode(unencoded):
base64val= base64.urlsafe_b64encode(unencoded)
strip_trailing=base64val.split(b'=')[0] # pick the string before the trailing =
return str(strip_trailing, 'utf-8')

@staticmethod
def decode(encoded):
padding_added = encoded + "=" * ((len(encoded)* -1) % 4)
return base64.urlsafe_b64decode(padding_added)
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved

from typing import TYPE_CHECKING
from typing import NoReturn, TYPE_CHECKING

from azure.core.configuration import Configuration
from azure.core.pipeline import policies

from ._version import VERSION
from ._models import TokenValidationOptions

if TYPE_CHECKING:
# pylint: disable=unused-import,ungrouped-imports
from typing import Any

from azure.core.credentials import TokenCredential


Expand All @@ -30,6 +30,8 @@ class AttestationClientConfiguration(Configuration):
:type credential: ~azure.core.credentials.TokenCredential
:param instance_url: The attestation instance base URI, for example https://mytenant.attest.azure.net.
:type instance_url: str
:keyword validation_options: Optional token validation options.
:type validation_options: TokenValidationOptions
"""

def __init__(
Expand All @@ -45,11 +47,14 @@ def __init__(
raise ValueError("Parameter 'instance_url' must not be None.")
super(AttestationClientConfiguration, self).__init__(**kwargs)

self.token_validation_options = kwargs.get('validation_options') # type: TokenValidationOptions
if (self.token_validation_options == None):
self.token_validation_options = TokenValidationOptions(validate_token=True)
self.credential = credential
self.instance_url = instance_url
self.api_version = "2020-10-01"
self.credential_scopes = kwargs.pop('credential_scopes', ['https://attest.azure.net/.default'])
kwargs.setdefault('sdk_moniker', 'security-attestation/{}'.format(VERSION))
kwargs.setdefault('sdk_moniker', 'attestation/{}'.format(VERSION))
self._configure(**kwargs)

def _configure(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 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.
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------

from ._azure_attestation_rest_client import AzureAttestationRestClient
from ._version import VERSION

__version__ = VERSION
__all__ = ['AzureAttestationRestClient']

try:
from ._patch import patch_sdk # type: ignore
patch_sdk()
except ImportError:
pass
Loading