1# ------------------------------------ 2# Copyright (c) Microsoft Corporation. 3# Licensed under the MIT License. 4# ------------------------------------- 5from collections import namedtuple 6from ._shared import parse_key_vault_id 7from ._generated.v7_1.models import JsonWebKey as _JsonWebKey 8 9try: 10 from typing import TYPE_CHECKING 11except ImportError: 12 TYPE_CHECKING = False 13 14if TYPE_CHECKING: 15 # pylint:disable=unused-import 16 from typing import Any, Dict, Optional 17 from datetime import datetime 18 from ._generated.v7_0 import models as _models 19 from ._enums import KeyOperation 20 21KeyOperationResult = namedtuple("KeyOperationResult", ["id", "value"]) 22 23 24class JsonWebKey(object): 25 # pylint:disable=too-many-instance-attributes 26 """As defined in http://tools.ietf.org/html/draft-ietf-jose-json-web-key-18. All parameters are optional. 27 28 :param str kid: Key identifier. 29 :param kty: Key Type (kty), as defined in https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 30 :type kty: ~azure.keyvault.keys.KeyType or str 31 :param key_ops: Allowed operations for the key 32 :type key_ops: list[str or ~azure.keyvault.keys.KeyOperation] 33 :param bytes n: RSA modulus. 34 :param bytes e: RSA public exponent. 35 :param bytes d: RSA private exponent, or the D component of an EC private key. 36 :param bytes dp: RSA private key parameter. 37 :param bytes dq: RSA private key parameter. 38 :param bytes qi: RSA private key parameter. 39 :param bytes p: RSA secret prime. 40 :param bytes q: RSA secret prime, with p < q. 41 :param bytes k: Symmetric key. 42 :param bytes t: HSM Token, used with 'Bring Your Own Key'. 43 :param crv: Elliptic curve name. 44 :type crv: ~azure.keyvault.keys.KeyCurveName or str 45 :param bytes x: X component of an EC public key. 46 :param bytes y: Y component of an EC public key. 47 """ 48 49 _FIELDS = ("kid", "kty", "key_ops", "n", "e", "d", "dp", "dq", "qi", "p", "q", "k", "t", "crv", "x", "y") 50 51 def __init__(self, **kwargs): 52 # type: (**Any) -> None 53 for field in self._FIELDS: 54 setattr(self, field, kwargs.get(field)) 55 56 def _to_generated_model(self): 57 # type: () -> _JsonWebKey 58 jwk = _JsonWebKey() 59 for field in self._FIELDS: 60 setattr(jwk, field, getattr(self, field)) 61 return jwk 62 63 64class KeyProperties(object): 65 """A key's id and attributes.""" 66 67 def __init__(self, key_id, attributes=None, **kwargs): 68 # type: (str, Optional[_models.KeyAttributes], **Any) -> None 69 self._attributes = attributes 70 self._id = key_id 71 self._vault_id = KeyVaultKeyIdentifier(key_id) 72 self._managed = kwargs.get("managed", None) 73 self._tags = kwargs.get("tags", None) 74 75 def __repr__(self): 76 # type () -> str 77 return "<KeyProperties [{}]>".format(self.id)[:1024] 78 79 @classmethod 80 def _from_key_bundle(cls, key_bundle): 81 # type: (_models.KeyBundle) -> KeyProperties 82 """Construct a KeyProperties from an autorest-generated KeyBundle""" 83 return cls( 84 key_bundle.key.kid, attributes=key_bundle.attributes, managed=key_bundle.managed, tags=key_bundle.tags 85 ) 86 87 @classmethod 88 def _from_key_item(cls, key_item): 89 # type: (_models.KeyItem) -> KeyProperties 90 """Construct a KeyProperties from an autorest-generated KeyItem""" 91 return cls(key_id=key_item.kid, attributes=key_item.attributes, managed=key_item.managed, tags=key_item.tags) 92 93 @property 94 def id(self): 95 # type: () -> str 96 """The key's id 97 98 :rtype: str 99 """ 100 return self._id 101 102 @property 103 def name(self): 104 # type: () -> str 105 """The key's name 106 107 :rtype: str 108 """ 109 return self._vault_id.name 110 111 @property 112 def version(self): 113 # type: () -> str 114 """The key's version 115 116 :rtype: str 117 """ 118 return self._vault_id.version 119 120 @property 121 def enabled(self): 122 # type: () -> bool 123 """Whether the key is enabled for use 124 125 :rtype: bool 126 """ 127 return self._attributes.enabled 128 129 @property 130 def not_before(self): 131 # type: () -> datetime 132 """The time before which the key can not be used, in UTC 133 134 :rtype: ~datetime.datetime 135 """ 136 return self._attributes.not_before 137 138 @property 139 def expires_on(self): 140 # type: () -> datetime 141 """When the key will expire, in UTC 142 143 :rtype: ~datetime.datetime 144 """ 145 return self._attributes.expires 146 147 @property 148 def created_on(self): 149 # type: () -> datetime 150 """When the key was created, in UTC 151 152 :rtype: ~datetime.datetime 153 """ 154 return self._attributes.created 155 156 @property 157 def updated_on(self): 158 # type: () -> datetime 159 """When the key was last updated, in UTC 160 161 :rtype: ~datetime.datetime 162 """ 163 return self._attributes.updated 164 165 @property 166 def vault_url(self): 167 # type: () -> str 168 """URL of the vault containing the key 169 170 :rtype: str 171 """ 172 return self._vault_id.vault_url 173 174 @property 175 def recoverable_days(self): 176 # type: () -> Optional[int] 177 """The number of days the key is retained before being deleted from a soft-delete enabled Key Vault. 178 179 :rtype: int 180 """ 181 # recoverable_days was added in 7.1-preview 182 if self._attributes and hasattr(self._attributes, "recoverable_days"): 183 return self._attributes.recoverable_days 184 return None 185 186 @property 187 def recovery_level(self): 188 # type: () -> str 189 """The vault's deletion recovery level for keys 190 191 :rtype: str 192 """ 193 return self._attributes.recovery_level 194 195 @property 196 def tags(self): 197 # type: () -> Dict[str, str] 198 """Application specific metadata in the form of key-value pairs 199 200 :rtype: dict[str, str] 201 """ 202 return self._tags 203 204 @property 205 def managed(self): 206 # type: () -> bool 207 """Returns whether the key's lifetime is managed by key vault 208 209 :rtype: bool 210 """ 211 return self._managed 212 213 214class KeyVaultKey(object): 215 """A key's attributes and cryptographic material. 216 217 :param str key_id: 218 Key Vault's identifier for the key. Typically a URI, e.g. https://myvault.vault.azure.net/keys/my-key/version 219 :param jwk: 220 The key's cryptographic material as a JSON Web Key (https://tools.ietf.org/html/rfc7517). This may be provided 221 as a dictionary or keyword arguments. See :class:`~azure.keyvault.keys.models.JsonWebKey` for field names. 222 223 Providing cryptographic material as keyword arguments: 224 225 .. code-block:: python 226 227 from azure.keyvault.keys.models import KeyVaultKey 228 229 key_id = 'https://myvault.vault.azure.net/keys/my-key/my-key-version' 230 key_bytes = os.urandom(32) 231 key = KeyVaultKey(key_id, k=key_bytes, kty='oct', key_ops=['unwrapKey', 'wrapKey']) 232 233 Providing cryptographic material as a dictionary: 234 235 .. code-block:: python 236 237 from azure.keyvault.keys.models import KeyVaultKey 238 239 key_id = 'https://myvault.vault.azure.net/keys/my-key/my-key-version' 240 key_bytes = os.urandom(32) 241 jwk = {'k': key_bytes, 'kty': 'oct', 'key_ops': ['unwrapKey', 'wrapKey']} 242 key = KeyVaultKey(key_id, jwk=jwk) 243 244 """ 245 246 def __init__(self, key_id, jwk=None, **kwargs): 247 # type: (str, Optional[dict], **Any) -> None 248 self._properties = kwargs.pop("properties", None) or KeyProperties(key_id, **kwargs) 249 if isinstance(jwk, dict): 250 if any(field in kwargs for field in JsonWebKey._FIELDS): # pylint:disable=protected-access 251 raise ValueError( 252 "Individual keyword arguments for key material and the 'jwk' argument are mutually exclusive." 253 ) 254 self._key_material = JsonWebKey(**jwk) 255 else: 256 self._key_material = JsonWebKey(**kwargs) 257 258 def __repr__(self): 259 # type () -> str 260 return "<KeyVaultKey [{}]>".format(self.id)[:1024] 261 262 @classmethod 263 def _from_key_bundle(cls, key_bundle): 264 # type: (_models.KeyBundle) -> KeyVaultKey 265 """Construct a KeyVaultKey from an autorest-generated KeyBundle""" 266 # pylint:disable=protected-access 267 return cls( 268 key_id=key_bundle.key.kid, 269 jwk={field: getattr(key_bundle.key, field, None) for field in JsonWebKey._FIELDS}, 270 properties=KeyProperties._from_key_bundle(key_bundle), 271 ) 272 273 @property 274 def id(self): 275 # type: () -> str 276 """The key's id 277 278 :rtype: str 279 """ 280 return self._properties.id 281 282 @property 283 def name(self): 284 # type: () -> str 285 """The key's name 286 287 :rtype: str 288 """ 289 return self._properties.name 290 291 @property 292 def properties(self): 293 # type: () -> KeyProperties 294 """The key's properties 295 296 :rtype: ~azure.keyvault.keys.KeyProperties 297 """ 298 return self._properties 299 300 @property 301 def key(self): 302 # type: () -> JsonWebKey 303 """The JSON web key 304 305 :rtype: ~azure.keyvault.keys.JsonWebKey 306 """ 307 return self._key_material 308 309 @property 310 def key_type(self): 311 # type: () -> str 312 """The key's type. See :class:`~azure.keyvault.keys.KeyType` for possible values. 313 314 :rtype: ~azure.keyvault.keys.KeyType or str 315 """ 316 return self._key_material.kty # pylint:disable=no-member 317 318 @property 319 def key_operations(self): 320 # type: () -> list[KeyOperation] 321 """Permitted operations. See :class:`~azure.keyvault.keys.KeyOperation` for possible values. 322 323 :rtype: list[~azure.keyvault.keys.KeyOperation or str] 324 """ 325 return self._key_material.key_ops # pylint:disable=no-member 326 327 328class KeyVaultKeyIdentifier(object): 329 """Information about a KeyVaultKey parsed from a key ID. 330 331 :param str source_id: the full original identifier of a key 332 :raises ValueError: if the key ID is improperly formatted 333 Example: 334 .. literalinclude:: ../tests/test_parse_id.py 335 :start-after: [START parse_key_vault_key_id] 336 :end-before: [END parse_key_vault_key_id] 337 :language: python 338 :caption: Parse a key's ID 339 :dedent: 8 340 """ 341 342 def __init__(self, source_id): 343 # type: (str) -> None 344 self._resource_id = parse_key_vault_id(source_id) 345 346 @property 347 def source_id(self): 348 # type: () -> str 349 return self._resource_id.source_id 350 351 @property 352 def vault_url(self): 353 # type: () -> str 354 return self._resource_id.vault_url 355 356 @property 357 def name(self): 358 # type: () -> str 359 return self._resource_id.name 360 361 @property 362 def version(self): 363 # type: () -> Optional[str] 364 return self._resource_id.version 365 366 367class DeletedKey(KeyVaultKey): 368 """A deleted key's properties, cryptographic material and its deletion information. If soft-delete 369 is enabled, returns information about its recovery as well.""" 370 371 def __init__( 372 self, 373 properties, # type: KeyProperties 374 deleted_date=None, # type: Optional[datetime] 375 recovery_id=None, # type: Optional[str] 376 scheduled_purge_date=None, # type: Optional[datetime] 377 **kwargs # type: Any 378 ): 379 # type: (...) -> None 380 super(DeletedKey, self).__init__(properties=properties, **kwargs) 381 self._deleted_date = deleted_date 382 self._recovery_id = recovery_id 383 self._scheduled_purge_date = scheduled_purge_date 384 385 def __repr__(self): 386 # type () -> str 387 return "<DeletedKey [{}]>".format(self.id)[:1024] 388 389 @classmethod 390 def _from_deleted_key_bundle(cls, deleted_key_bundle): 391 # type: (_models.DeletedKeyBundle) -> DeletedKey 392 """Construct a DeletedKey from an autorest-generated DeletedKeyBundle""" 393 # pylint:disable=protected-access 394 return cls( 395 properties=KeyProperties._from_key_bundle(deleted_key_bundle), 396 key_id=deleted_key_bundle.key.kid, 397 jwk={field: getattr(deleted_key_bundle.key, field, None) for field in JsonWebKey._FIELDS}, 398 deleted_date=deleted_key_bundle.deleted_date, 399 recovery_id=deleted_key_bundle.recovery_id, 400 scheduled_purge_date=deleted_key_bundle.scheduled_purge_date, 401 ) 402 403 @classmethod 404 def _from_deleted_key_item(cls, deleted_key_item): 405 # type: (_models.DeletedKeyItem) -> DeletedKey 406 """Construct a DeletedKey from an autorest-generated DeletedKeyItem""" 407 return cls( 408 properties=KeyProperties._from_key_item(deleted_key_item), # pylint: disable=protected-access 409 key_id=deleted_key_item.kid, 410 deleted_date=deleted_key_item.deleted_date, 411 recovery_id=deleted_key_item.recovery_id, 412 scheduled_purge_date=deleted_key_item.scheduled_purge_date, 413 ) 414 415 @property 416 def deleted_date(self): 417 # type: () -> datetime 418 """When the key was deleted, in UTC 419 420 :rtype: ~datetime.datetime 421 """ 422 return self._deleted_date 423 424 @property 425 def recovery_id(self): 426 # type: () -> str 427 """An identifier used to recover the deleted key. Returns ``None`` if soft-delete is disabled. 428 429 :rtype: str 430 """ 431 return self._recovery_id 432 433 @property 434 def scheduled_purge_date(self): 435 # type: () -> datetime 436 """When the key is scheduled to be purged, in UTC. Returns ``None`` if soft-delete is disabled. 437 438 :rtype: ~datetime.datetime 439 """ 440 return self._scheduled_purge_date 441