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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,12 @@
"racwd"
]
},
{
"filename": "sdk/keyvault/**",
"words": [
"eddsa"
]
},
{
"filename": "sdk/monitor/test-resources.json",
"words": [
Expand Down
2 changes: 1 addition & 1 deletion sdk/keyvault/azure-keyvault-administration/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/keyvault/azure-keyvault-administration",
"Tag": "python/keyvault/azure-keyvault-administration_ed5ac87a41"
"Tag": "python/keyvault/azure-keyvault-administration_6f40de8fcf"
}
14 changes: 9 additions & 5 deletions sdk/keyvault/azure-keyvault-keys/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Release History

## 4.8.0b2 (Unreleased)
## 4.8.0b2 (2022-11-15)

### Features Added

### Breaking Changes

### Bugs Fixed
- Added support for service API version `7.4-preview.1`
- `KeyClient` has a `create_okp_key` method to create an octet key pair (OKP) on Managed HSM
- Added `eddsa` to `SignatureAlgorithm` enum to support signing and verifying using an
Edwards-Curve Digital Signature Algorithm (EdDSA) on Managed HSM
- Added `okp` and `okp_hsm` to `KeyType` enum for octet key pairs
- Added `ed25519` to `KeyCurveName` enum to support use of the Ed25519 Edwards curve

### Other Changes
- Key Vault API version `7.4-preview.1` is now the default
- Dropped `msrest` requirement
- Dropped `six` requirement
Comment thread
laiapat marked this conversation as resolved.
Outdated
- Added requirement for `isodate>=0.6.1` (`isodate` was required by `msrest`)
- Added requirement for `typing-extensions>=4.0.1`

Expand Down
2 changes: 1 addition & 1 deletion sdk/keyvault/azure-keyvault-keys/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/keyvault/azure-keyvault-keys",
"Tag": "python/keyvault/azure-keyvault-keys_51a137948c"
"Tag": "python/keyvault/azure-keyvault-keys_5cd3484c57"
}
42 changes: 40 additions & 2 deletions sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@

if TYPE_CHECKING:
# pylint:disable=unused-import
from typing import Any, List, Optional, Union
from typing import Any, Optional, Union
from azure.core.paging import ItemPaged
from azure.core.polling import LROPoller
from ._models import JsonWebKey
from ._enums import KeyType
from ._enums import KeyCurveName, KeyType


def _get_key_id(vault_url, key_name, version=None):
Expand Down Expand Up @@ -268,6 +268,44 @@ def create_oct_key(self, name, **kwargs):
hsm = kwargs.pop("hardware_protected", False)
return self.create_key(name, key_type="oct-HSM" if hsm else "oct", **kwargs)

@distributed_trace
def create_okp_key(self, name: str, **kwargs: "Any") -> KeyVaultKey:
Comment thread
laiapat marked this conversation as resolved.
Outdated
"""Create a new octet key pair or, if ``name`` is in use, create a new version of the key.

Requires the keys/create permission.

:param str name: The name for the new key.

:keyword curve: Elliptic curve name.
:paramtype curve: ~azure.keyvault.keys.KeyCurveName or str
:keyword key_operations: Allowed key operations.
:paramtype key_operations: list[~azure.keyvault.keys.KeyOperation or str]
:keyword bool hardware_protected: Whether the key should be created in a hardware security module.
Defaults to ``False``.
:keyword bool enabled: Whether the key is enabled for use.
:keyword tags: Application specific metadata in the form of key-value pairs.
:paramtype tags: dict[str, str]
:keyword ~datetime.datetime not_before: Not before date of the key in UTC
:keyword ~datetime.datetime expires_on: Expiry date of the key in UTC
:keyword bool exportable: Whether the key can be exported.
:keyword release_policy: The policy rules under which the key can be exported.
:paramtype release_policy: ~azure.keyvault.keys.KeyReleasePolicy

:returns: The created key
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`

Example:
.. literalinclude:: ../tests/test_samples_keys.py
:start-after: [START create_okp_key]
:end-before: [END create_okp_key]
:language: python
:caption: Create an octet key pair (OKP)
:dedent: 8
"""
hsm = kwargs.pop("hardware_protected", False)
return self.create_key(name, key_type="OKP-HSM" if hsm else "OKP", **kwargs)

@distributed_trace
def begin_delete_key(self, name, **kwargs):
# type: (str, **Any) -> LROPoller
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class KeyCurveName(str, Enum, metaclass=CaseInsensitiveEnumMeta):
p_384 = "P-384" #: The NIST P-384 elliptic curve, AKA SECG curve SECP384R1.
p_521 = "P-521" #: The NIST P-521 elliptic curve, AKA SECG curve SECP521R1.
p_256_k = "P-256K" #: The SECG SECP256K1 elliptic curve.
ed25519 = "Ed25519" #: The Ed25519 Edwards curve.


class KeyExportEncryptionAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta):
Expand Down Expand Up @@ -56,6 +57,8 @@ class KeyType(str, Enum, metaclass=CaseInsensitiveEnumMeta):
rsa_hsm = "RSA-HSM" #: RSA with a private key which is not exportable from the HSM
oct = "oct" #: Octet sequence (used to represent symmetric keys)
oct_hsm = "oct-HSM" #: Octet sequence with a private key which is not exportable from the HSM
okp = "OKP" #: Octet key pair (https://tools.ietf.org/html/rfc8037)
okp_hsm = "OKP-HSM" #: Octet key pair with a private key which is not exportable from the HSM

@classmethod
def _missing_(cls, value):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ class ApiVersion(str, Enum, metaclass=CaseInsensitiveEnumMeta):
"""Key Vault API versions supported by this package"""

#: this is the default version
V7_4_PREVIEW_1 = "7.4-preview.1"
V7_3 = "7.3"
V7_2 = "7.2"
V7_1 = "7.1"
V7_0 = "7.0"
V2016_10_01 = "2016-10-01"


DEFAULT_VERSION = ApiVersion.V7_3
DEFAULT_VERSION = ApiVersion.V7_4_PREVIEW_1


class KeyVaultClientBase(object):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,44 @@ async def create_oct_key(self, name: str, **kwargs: "Any") -> KeyVaultKey:
hsm = kwargs.pop("hardware_protected", False)
return await self.create_key(name, key_type="oct-HSM" if hsm else "oct", **kwargs)

@distributed_trace_async
async def create_okp_key(self, name: str, **kwargs: "Any") -> KeyVaultKey:
"""Create a new octet key pair or, if ``name`` is in use, create a new version of the key.

Requires the keys/create permission.

:param str name: The name for the new key.

:keyword curve: Elliptic curve name.
:paramtype curve: ~azure.keyvault.keys.KeyCurveName or str
:keyword key_operations: Allowed key operations.
:paramtype key_operations: list[~azure.keyvault.keys.KeyOperation or str]
:keyword bool hardware_protected: Whether the key should be created in a hardware security module.
Defaults to ``False``.
:keyword bool enabled: Whether the key is enabled for use.
:keyword tags: Application specific metadata in the form of key-value pairs.
:paramtype tags: dict[str, str]
:keyword ~datetime.datetime not_before: Not before date of the key in UTC
:keyword ~datetime.datetime expires_on: Expiry date of the key in UTC
:keyword bool exportable: Whether the key can be exported.
:keyword release_policy: The policy rules under which the key can be exported.
:paramtype release_policy: ~azure.keyvault.keys.KeyReleasePolicy

:returns: The created key
:rtype: ~azure.keyvault.keys.KeyVaultKey
:raises: :class:`~azure.core.exceptions.HttpResponseError`

Example:
.. literalinclude:: ../tests/test_samples_keys_async.py
:start-after: [START create_okp_key]
:end-before: [END create_okp_key]
:language: python
:caption: Create an octet key pair (OKP)
:dedent: 8
"""
hsm = kwargs.pop("hardware_protected", False)
return await self.create_key(name, key_type="OKP-HSM" if hsm else "OKP", **kwargs)

@distributed_trace_async
async def delete_key(self, name: str, **kwargs: "Any") -> DeletedKey:
"""Delete all versions of a key and its cryptographic material.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ class SignatureAlgorithm(str, Enum, metaclass=CaseInsensitiveEnumMeta):
es384 = "ES384" #: ECDSA using P-384 and SHA-384
es512 = "ES512" #: ECDSA using P-521 and SHA-512
es256_k = "ES256K" #: ECDSA using P-256K and SHA-256
eddsa = "EdDSA" #: Edwards-Curve Digital Signature Algorithm, as described in https://tools.ietf.org/html/rfc8032
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def get_local_cryptography_provider(key):
return RsaCryptographyProvider(key)
if key.kty in (KeyType.oct, KeyType.oct_hsm): # type: ignore[attr-defined]
return SymmetricCryptographyProvider(key)
if key.kty in (KeyType.okp, KeyType.okp_hsm): # type: ignore[attr-defined]
return NoLocalCryptography()

raise ValueError('Unsupported key type "{}"'.format(key.kty)) # type: ignore[attr-defined]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get_test_parameters(only_hsm=False, only_vault=False, api_versions=None):
"""generates a list of parameter pairs for test case parameterization, where [x, y] = [api_version, is_hsm]"""
combinations = []
versions = api_versions or ApiVersion
hsm_supported_versions = {ApiVersion.V7_2, ApiVersion.V7_3}
hsm_supported_versions = {ApiVersion.V7_2, ApiVersion.V7_3, ApiVersion.V7_4_PREVIEW_1}

for api_version in versions:
if not only_vault and api_version in hsm_supported_versions:
Expand Down
3 changes: 2 additions & 1 deletion sdk/keyvault/azure-keyvault-keys/tests/_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get_test_parameters(only_hsm=False, only_vault=False, api_versions=None):
"""generates a list of parameter pairs for test case parameterization, where [x, y] = [api_version, is_hsm]"""
combinations = []
versions = api_versions or ApiVersion
hsm_supported_versions = {ApiVersion.V7_2, ApiVersion.V7_3}
hsm_supported_versions = {ApiVersion.V7_2, ApiVersion.V7_3, ApiVersion.V7_4_PREVIEW_1}

for api_version in versions:
if not only_vault and api_version in hsm_supported_versions:
Expand Down Expand Up @@ -103,6 +103,7 @@ def _set_mgmt_settings_real_values(self):
os.environ["AZURE_CLIENT_SECRET"] = os.environ["KEYVAULT_CLIENT_SECRET"]

def _skip_if_not_configured(self, api_version, is_hsm):

if self.is_live and api_version != DEFAULT_VERSION:
pytest.skip("This test only uses the default API version for live tests")
if self.is_live and is_hsm and self.managed_hsm_url is None:
Expand Down
25 changes: 24 additions & 1 deletion sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import pytest
from azure.core.exceptions import AzureError, HttpResponseError
from azure.core.pipeline.policies import SansIOHTTPPolicy
from azure.keyvault.keys import (JsonWebKey, KeyCurveName, KeyOperation,
from azure.keyvault.keys import (ApiVersion, JsonWebKey, KeyCurveName, KeyOperation,
KeyVaultKey)
from azure.keyvault.keys.crypto import (CryptographyClient,
EncryptionAlgorithm, KeyWrapAlgorithm,
Expand All @@ -36,6 +36,7 @@
NO_GET = Permissions(keys=[p.value for p in KeyPermissions if p.value != "get"])

all_api_versions = get_decorator()
only_7_4_hsm = get_decorator(only_hsm=True, api_versions=[ApiVersion.V7_4_PREVIEW_1])
only_hsm = get_decorator(only_hsm=True)
no_get = get_decorator(permissions=NO_GET)

Expand Down Expand Up @@ -210,6 +211,28 @@ def test_sign_and_verify(self, key_client, is_hsm, **kwargs):
assert result.algorithm == SignatureAlgorithm.rs256
assert verified.is_valid

@pytest.mark.parametrize("api_version,is_hsm", only_7_4_hsm)
@KeysClientPreparer()
@recorded_by_proxy
def test_sign_and_verify_okp(self, key_client, is_hsm, **kwargs):
key_name = self.get_resource_name("keysign")

md = hashlib.sha256()
md.update(self.plaintext)
digest = md.digest()

# Local crypto isn't supported for OKP, so operations will be remote even without explicit NO_GET permissions
key = key_client.create_okp_key(key_name, curve=KeyCurveName.ed25519)
crypto_client = self.create_crypto_client(key.id, api_version=key_client.api_version)

result = crypto_client.sign(SignatureAlgorithm.eddsa, digest)
assert result.key_id == key.id

verified = crypto_client.verify(result.algorithm, digest, result.signature)
assert result.key_id == key.id
assert result.algorithm == SignatureAlgorithm.eddsa
assert verified.is_valid

@pytest.mark.parametrize("api_version,is_hsm", no_get)
@KeysClientPreparer(permissions=NO_GET)
@recorded_by_proxy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import pytest
from azure.core.exceptions import AzureError, HttpResponseError
from azure.core.pipeline.policies import SansIOHTTPPolicy
from azure.keyvault.keys import (JsonWebKey, KeyCurveName,
from azure.keyvault.keys import (ApiVersion, JsonWebKey, KeyCurveName,
KeyOperation, KeyVaultKey)
from azure.keyvault.keys.crypto._key_validity import _UTC
from azure.keyvault.keys.crypto._providers import (
Expand All @@ -34,6 +34,7 @@
NO_GET = Permissions(keys=[p.value for p in KeyPermissions if p.value != "get"])

all_api_versions = get_decorator(is_async=True)
only_7_4_hsm = get_decorator(only_hsm=True, api_versions=[ApiVersion.V7_4_PREVIEW_1])
only_hsm = get_decorator(only_hsm=True, is_async=True)
no_get = get_decorator(is_async=True, permissions=NO_GET)

Expand Down Expand Up @@ -210,6 +211,29 @@ async def test_sign_and_verify(self, key_client, is_hsm, **kwargs):
assert result.algorithm == SignatureAlgorithm.rs256
assert verified.is_valid

@pytest.mark.asyncio
@pytest.mark.parametrize("api_version,is_hsm", only_7_4_hsm)
@AsyncKeysClientPreparer()
@recorded_by_proxy_async
async def test_sign_and_verify_okp(self, key_client, is_hsm, **kwargs):
key_name = self.get_resource_name("keysign")

md = hashlib.sha256()
md.update(self.plaintext)
digest = md.digest()

# Local crypto isn't supported for OKP, so operations will be remote even without explicit NO_GET permissions
key = await key_client.create_okp_key(key_name, curve=KeyCurveName.ed25519)
crypto_client = self.create_crypto_client(key.id, is_async=True, api_version=key_client.api_version)

result = await crypto_client.sign(SignatureAlgorithm.eddsa, digest)
assert result.key_id == key.id

verified = await crypto_client.verify(result.algorithm, digest, result.signature)
assert result.key_id == key.id
assert result.algorithm == SignatureAlgorithm.eddsa
assert verified.is_valid

@pytest.mark.asyncio
@pytest.mark.parametrize("api_version,is_hsm",no_get)
@AsyncKeysClientPreparer()
Expand Down
17 changes: 16 additions & 1 deletion sdk/keyvault/azure-keyvault-keys/tests/test_samples_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
import time

import pytest
from azure.keyvault.keys import KeyType
from azure.keyvault.keys import ApiVersion, KeyCurveName, KeyType
from devtools_testutils import recorded_by_proxy

from _shared.test_case import KeyVaultTestCase
from _test_case import KeysClientPreparer, get_decorator
from _keys_test_case import KeysTestCase

all_api_versions = get_decorator(only_vault=True)
only_7_4_hsm = get_decorator(only_hsm=True, api_versions=[ApiVersion.V7_4_PREVIEW_1])
only_hsm = get_decorator(only_hsm=True)


Expand Down Expand Up @@ -146,6 +147,20 @@ def test_example_create_oct_key(self, key_client, **kwargs):
print(key.key_type)
# [END create_oct_key]

@pytest.mark.parametrize("api_version,is_hsm",only_7_4_hsm)
@KeysClientPreparer()
@recorded_by_proxy
def test_example_create_okp_key(self, key_client, **kwargs):
key_name = self.get_resource_name("key")

# [START create_okp_key]
key = key_client.create_okp_key(key_name, curve=KeyCurveName.ed25519, hardware_protected=True)

print(key.id)
print(key.name)
print(key.key_type)
# [END create_okp_key]

@pytest.mark.parametrize("api_version,is_hsm",all_api_versions)
@KeysClientPreparer()
@recorded_by_proxy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
import os

import pytest
from azure.keyvault.keys import KeyType
from azure.keyvault.keys import ApiVersion, KeyCurveName, KeyType
from devtools_testutils.aio import recorded_by_proxy_async

from _async_test_case import AsyncKeysClientPreparer, get_decorator
from _shared.test_case_async import KeyVaultTestCase

all_api_versions = get_decorator(is_async=True, only_vault=True)
only_7_4_hsm = get_decorator(only_hsm=True, api_versions=[ApiVersion.V7_4_PREVIEW_1])
only_hsm = get_decorator(only_hsm=True, is_async=True)


Expand Down Expand Up @@ -145,6 +146,21 @@ async def test_example_create_oct_key(self, key_client, **kwargs):
print(key.key_type)
# [END create_oct_key]

@pytest.mark.asyncio
@pytest.mark.parametrize("api_version,is_hsm",only_7_4_hsm)
@AsyncKeysClientPreparer()
@recorded_by_proxy_async
async def test_example_create_okp_key(self, key_client, **kwargs):
key_name = self.get_resource_name("key")

# [START create_okp_key]
key = await key_client.create_okp_key(key_name, curve=KeyCurveName.ed25519, hardware_protected=True)

print(key.id)
print(key.name)
print(key.key_type)
# [END create_okp_key]

@pytest.mark.asyncio
@pytest.mark.parametrize("api_version,is_hsm",all_api_versions)
@AsyncKeysClientPreparer()
Expand Down
Loading