# Copyright (C) 2016-2019 Virgil Security Inc.
#
# Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# (1) Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# (3) Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import binascii
from virgil_sdk.cards.card_signature import CardSignature
from virgil_sdk.client import RawSignature
from virgil_sdk.utils import Utils
[docs]class Card(object):
"""
The Card class is the main entity of Virgil Services. Every user/device is
represented with a Virgil Card which contains a public key and information about identity.
"""
def __init__(
self,
card_id, # type: str
identity, # type: str
public_key, # type: PublicKey
version, # type: str
created_at, # type: datetime
signatures, # type: list[CardSignature]
previous_card_id, # type: str
content_snapshot, # type: bytearray
is_outdated=False # type: bool
):
# type: (...) -> None
super(Card, self).__init__(identity, public_key, version, created_at, previous_card_id)
self._id = card_id
self._identity = identity
self._version = version
self._created_at = created_at
self._previous_card_id = previous_card_id
self._public_key = public_key
self.__signatures = signatures
self.previous_card = None
self._content_snapshot = content_snapshot
self.is_outdated = is_outdated
@classmethod
def __generate_card_id(cls, card_crypto, content_snapshot):
# type: (Any, dict) -> str
"""
Generate card id from content snapshot
Args:
card_crypto: Users CardCrypto witch provides cryptographic operations.
content_snapshot: Card content snapshot
Returns:
Generated Card id.
"""
fingerprint = card_crypto.generate_sha512(bytearray(content_snapshot))
card_id = binascii.hexlify(bytearray(fingerprint)[:32]).decode()
return card_id
[docs] @classmethod
def from_snapshot(cls, content_snapshot):
# type: (str) -> Card
"""
Creates card from content snapshot.
Args:
content_snapshot: Model content snapshot.
Returns:
Card created from model content snapshot.
"""
card_content = cls.__new__(cls)
loaded_snapshot = Utils.json_loads(Utils.b64_decode(content_snapshot))
card_content._identity = loaded_snapshot["identity"]
card_content._public_key = loaded_snapshot["public_key"]
card_content._version = loaded_snapshot["version"]
card_content._created_at = loaded_snapshot["created_at"]
if "previous_card_id" in loaded_snapshot.keys():
card_content._previous_card_id = loaded_snapshot["previous_card_id"]
else:
card_content._previous_card_id = None
card_content._content_snapshot = content_snapshot
return card_content
[docs] @classmethod
def from_signed_model(cls, card_crypto, raw_singed_model, is_outdated=False):
# type: (Any, RawSignedModel, bool) -> Card
"""
Creates card from SignedModel snapshot and signatures.
Args:
card_crypto: Users CardCrypto witch provides cryptographic operations.
raw_singed_model: Card RawSignedModel
is_outdated: State of obsolescence
Returns:
Card created from RawSignedModel.
"""
card = cls.from_snapshot(raw_singed_model.content_snapshot)
card.previous_card = None
card.is_outdated = is_outdated
card._id = cls.__generate_card_id(card_crypto, Utils.b64_decode(raw_singed_model.content_snapshot))
card._public_key = card_crypto.import_public_key(bytearray(Utils.b64_decode(card._public_key)))
signatures = list()
if raw_singed_model.signatures:
for sign in raw_singed_model.signatures:
if isinstance(sign, dict):
if "snapshot" in sign.keys():
card_signature = CardSignature(
sign["signer"],
bytearray(Utils.b64decode(sign["signature"])),
sign["snapshot"]
)
else:
card_signature = CardSignature(sign["signer"], bytearray(Utils.b64decode(sign["signature"])))
signatures.append(card_signature)
if isinstance(sign, CardSignature):
card_signature = sign
signatures.append(card_signature)
if isinstance(sign, RawSignature):
card_signature = CardSignature(sign.signer, sign.signature, sign.snapshot)
signatures.append(card_signature)
card.__signatures = signatures
return card
@property
def id(self):
"""
Gets the Card ID that uniquely identifies the Card in Virgil Services.
Returns:
Card id.
"""
return self._id
@property
def identity(self):
"""
Gets the identity value that can be anything which identifies the user in your application.
Returns:
User identity.
"""
return self._identity
@property
def public_key(self):
"""
Gets the public key.
Returns:
Public key.
"""
return self._public_key
@property
def version(self):
"""
Gets the version of the card.
Returns:
Card version.
"""
return self._version
@property
def created_at(self):
"""
Gets the date and time fo card creation in UTC.
Returns:
Creation date in UTC datetime.
"""
return self._created_at
@property
def previous_card_id(self):
"""
Get previous Card ID that current card is used to override to.
Returns:
Previous card id.
"""
return self._previous_card_id
@property
def signatures(self):
"""
Gets a list of signatures.
Returns:
List of signatures
"""
return self.__signatures
@property
def content_snapshot(self):
"""
Card content snapshot
Returns:
Card snapshot.
"""
if not self._content_snapshot:
content = {
"identity": self._identity,
"public_key": Utils.b64encode(self._public_key.raw_key),
"version": self._version,
"created_at": self._created_at,
}
if self._previous_card_id:
content.update({"previous_card_id": self._previous_card_id})
self._content_snapshot = Utils.b64encode(
Utils.json_dumps(content, sort_keys=True, separators=(',', ':')).encode()
)
return self._content_snapshot