-
Notifications
You must be signed in to change notification settings - Fork 46
/
entity.py
205 lines (153 loc) · 5.59 KB
/
entity.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import hashlib
import os
from dataclasses import dataclass
from enum import Enum, IntEnum
from typing import List, Optional, Union
from util.structable import represent
from util.tlv import TLV8Field, TLV8Object
class KeyType(IntEnum):
CURVE25519 = 0x01
SECP256R1 = 0x02
class Context(Enum):
PERSISTENT = "Persistent"
VOLATILE = "Volatile"
VOLATILE_FAST = "VolatileFast"
@dataclass
class Enrollment:
at: int
payload: Union[bytes, str]
@classmethod
def from_dict(cls, enrollment: dict):
return Enrollment(at=enrollment.get("at", 0), payload=enrollment.get("payload"))
def to_dict(self):
return {"at": self.at, "payload": self.payload}
@dataclass
class Enrollments:
hap: Optional[Enrollment]
attestation: Optional[Enrollment]
@classmethod
def from_dict(cls, enrollments: dict):
return Enrollments(
hap=Enrollment.from_dict(enrollments.get("hap", None))
if enrollments.get("hap", None) is not None
else None,
attestation=Enrollment.from_dict(enrollments.get("attestation", None))
if enrollments.get("attestation", None) is not None
else None,
)
def to_dict(self):
return {
"hap": self.hap.to_dict() if self.hap is not None else None,
"attestation": self.attestation.to_dict()
if self.attestation is not None
else None,
}
def __repr__(self) -> str:
return f"Enrollments({'hap' if self.hap else ''}, {'attestation' if self.attestation else ''})"
@dataclass
class Endpoint:
last_used_at: int
counter: int
key_type: KeyType
public_key: bytes
persistent_key: bytes
enrollments: Enrollments
@property
def id(self):
return hashlib.sha1(self.public_key).digest()[:6]
@classmethod
def from_dict(cls, endpoint: dict):
return Endpoint(
endpoint.get("last_used_at", 0),
endpoint.get("counter", 0),
KeyType(endpoint.get("key_type", 2)),
bytes.fromhex(endpoint.get("public_key", "04" + ("00" * 32))),
bytes.fromhex(endpoint.get("persistent_key", os.urandom(16).hex())),
Enrollments.from_dict(endpoint.get("enrollments", dict())),
)
def to_dict(self):
return {
"last_used_at": self.last_used_at,
"counter": self.counter,
"key_type": self.key_type,
"public_key": self.public_key.hex(),
"persistent_key": self.persistent_key.hex(),
"enrollments": self.enrollments.to_dict(),
}
def __repr__(self) -> str:
return f"Endpoint(last_used_at={self.last_used_at}, counter={self.counter}, key_type={represent(self.key_type)}, public_key={self.public_key.hex()}; persistent_key={self.persistent_key.hex()}, enrollments={self.enrollments})"
@dataclass
class Issuer:
public_key: bytes
endpoints: List[Endpoint]
@property
def id(self):
return hashlib.sha256("key-identifier".encode() + self.public_key).digest()[:8]
@classmethod
def from_dict(cls, issuer: dict):
return Issuer(
public_key=bytes.fromhex(issuer.get("public_key", "00" * 32)),
endpoints=[
Endpoint.from_dict(endpoint)
for _, endpoint in issuer.get("endpoints", {}).items()
],
)
def to_dict(self):
return {
"public_key": self.public_key.hex(),
"endpoints": {
endpoint.id.hex(): endpoint.to_dict() for endpoint in self.endpoints
},
}
def __repr__(self) -> str:
return f"Issuer(public_key={self.public_key.hex()}, endpoints={self.endpoints})"
class HardwareFinishColor(Enum):
TAN = bytes.fromhex("CED5DA00")
GOLD = bytes.fromhex("AAD6EC00")
SILVER = bytes.fromhex("E3E3E300")
BLACK = bytes.fromhex("00000000")
class Operation(IntEnum):
GET = 0x01
ADD = 0x02
REMOVE = 0x03
class Interface(IntEnum):
CONTACTLESS = 0x5E
class KeyState(IntEnum):
INACTIVE = 0x00
ACTIVE = 0x01
class OperationStatus(IntEnum):
SUCCESS = 0x00
OUT_OF_RESOURCES = 0x01
DUPLICATE = 0x02
DOES_NOT_EXIST = 0x03
NOT_SUPPORTED = 0x04
class HardwareFinishResponse(TLV8Object):
color: HardwareFinishColor = TLV8Field(1)
class SupportedConfigurationResponse(TLV8Object):
number_of_issuer_keys: int = TLV8Field(1)
number_of_inactive_credentials: int = TLV8Field(2)
class DeviceCredentialRequest(TLV8Object):
key_type: KeyType = TLV8Field(1)
credential_public_key: bytes = TLV8Field(2)
issuer_key_identifier: bytes = TLV8Field(3)
key_state: KeyState = TLV8Field(4)
key_identifier: bytes = TLV8Field(5)
class ReaderKeyRequest(TLV8Object):
key_type: KeyType = TLV8Field(1)
reader_private_key: bytes = TLV8Field(2)
unique_reader_identifier: bytes = TLV8Field(3)
key_identifier: bytes = TLV8Field(4)
class ControlPointRequest(TLV8Object):
operation: Operation = TLV8Field(1, optional=False)
device_credential_request: DeviceCredentialRequest = TLV8Field(4)
reader_key_request: ReaderKeyRequest = TLV8Field(6)
class DeviceCredentialResponse(TLV8Object):
key_identifier: bytes = TLV8Field(1)
issuer_key_identifier: bytes = TLV8Field(2)
status: OperationStatus = TLV8Field(3)
class ReaderKeyResponse(TLV8Object):
key_identifier: bytes = TLV8Field(1)
status: OperationStatus = TLV8Field(2)
class ControlPointResponse(TLV8Object):
device_credential_response: DeviceCredentialResponse = TLV8Field(5)
reader_key_response: DeviceCredentialResponse = TLV8Field(7)