Skip to content

Commit 8da3bdc

Browse files
committed
Move util code to lock_utils
Signed-off-by: Hunsup Jung <hunsup.jung@samsung.com>
1 parent b983f96 commit 8da3bdc

File tree

2 files changed

+171
-170
lines changed

2 files changed

+171
-170
lines changed

drivers/SmartThings/matter-lock/src/lock_utils.lua

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
-- Copyright 2022 SmartThings, Inc.
22
-- Licensed under the Apache License, Version 2.0
33

4+
local security = require "st.security"
5+
local PUB_KEY_PREFIX = "04"
6+
47
local lock_utils = {
58
-- Lock device field names
69
LOCK_CODES = "lockCodes",
@@ -41,7 +44,8 @@ local lock_utils = {
4144
DEVICE_KEY_ID = "deviceKeyId",
4245
COMMAND_REQUEST_ID = "commandRequestId",
4346
MODULAR_PROFILE_UPDATED = "__MODULAR_PROFILE_UPDATED",
44-
ALIRO_READER_CONFIG_UPDATED = "aliroReaderConfigUpdated"
47+
ALIRO_READER_CONFIG_UPDATED = "aliroReaderConfigUpdated",
48+
LATEST_DOOR_LOCK_FEATURE_MAP = "__latest_door_lock_feature_map"
4549
}
4650
local capabilities = require "st.capabilities"
4751
local json = require "st.json"
@@ -104,6 +108,13 @@ end
104108
-- keys are the code slots that ST uses
105109
-- user_index and credential_index are used in the matter commands
106110
--
111+
function lock_utils.get_field_for_endpoint(device, field, endpoint)
112+
return device:get_field(string.format("%s_%d", field, endpoint))
113+
end
114+
115+
function lock_utils.set_field_for_endpoint(device, field, endpoint, value, additional_params)
116+
device:set_field(string.format("%s_%d", field, endpoint), value, additional_params)
117+
end
107118

108119
function lock_utils.optional_capabilities_list_changed(new_component_capability_list, previous_component_capability_list)
109120
local previous_capability_map = {}
@@ -146,4 +157,121 @@ function lock_utils.optional_capabilities_list_changed(new_component_capability_
146157
return false
147158
end
148159

160+
-- This function check busy_state and if busy_state is false, set it to true(current time)
161+
function lock_utils.is_busy_state_set(device)
162+
local c_time = os.time()
163+
local busy_state = device:get_field(lock_utils.BUSY_STATE) or false
164+
if busy_state == false or c_time - busy_state > 10 then
165+
device:set_field(lock_utils.BUSY_STATE, c_time, {persist = true})
166+
return false
167+
else
168+
return true
169+
end
170+
end
171+
172+
function lock_utils.hex_string_to_octet_string(hex_string)
173+
if hex_string == nil then
174+
return nil
175+
end
176+
local octet_string = ""
177+
for i = 1, #hex_string, 2 do
178+
local hex = hex_string:sub(i, i + 1)
179+
octet_string = octet_string .. string.char(tonumber(hex, 16))
180+
end
181+
return octet_string
182+
end
183+
184+
function lock_utils.create_group_id_resolving_key()
185+
math.randomseed(os.time())
186+
local result = string.format("%02x", math.random(0, 255))
187+
for i = 1, 15 do
188+
result = result .. string.format("%02x", math.random(0, 255))
189+
end
190+
return result
191+
end
192+
193+
function lock_utils.generate_keypair(device)
194+
local request_opts = {
195+
key_algorithm = {
196+
type = "ec",
197+
curve = "prime256v1"
198+
},
199+
signature_algorithm = "sha256",
200+
return_formats = {
201+
pem = true,
202+
der = true
203+
},
204+
subject = {
205+
common_name = "reader config"
206+
},
207+
validity_days = 36500,
208+
x509_extensions = {
209+
key_usage = {
210+
critical = true,
211+
digital_signature = true
212+
},
213+
certificate_policies = {
214+
critical = true,
215+
policy_2030_5_self_signed_client = true
216+
}
217+
}
218+
}
219+
local status = security.generate_self_signed_cert(request_opts)
220+
if not status or not status.key_der then
221+
device.log.error("generate_self_signed_cert returned no data")
222+
return nil, nil
223+
end
224+
225+
local der = status.key_der
226+
local privKey, pubKey = nil, nil
227+
-- Helper: Parse ASN.1 length (handles 1-byte and multi-byte lengths)
228+
local function get_length(data, start_pos)
229+
local b = string.byte(data, start_pos)
230+
if not b then return nil, start_pos end
231+
232+
if b < 0x80 then
233+
return b, start_pos + 1
234+
else
235+
local num_bytes = b - 0x80
236+
local len = 0
237+
for i = 1, num_bytes do
238+
len = (len * 256) + string.byte(data, start_pos + i)
239+
end
240+
return len, start_pos + 1 + num_bytes
241+
end
242+
end
243+
-- Start parsing after the initial SEQUENCE tag (0x30)
244+
-- Most keys start: [0x30][Length]. We find the first length to find the start of content.
245+
local _, pos = get_length(der, 2)
246+
247+
while pos < #der do
248+
local tag = string.byte(der, pos)
249+
local len, content_start = get_length(der, pos + 1)
250+
if not len then break end
251+
if tag == 0x04 then
252+
-- PRIVATE KEY: Octet String
253+
privKey = utils.bytes_to_hex_string(string.sub(der, content_start, content_start + len - 1))
254+
elseif tag == 0xA1 then
255+
-- PUBLIC KEY Wrapper: Explicit Tag [1]
256+
-- Inside 0xA1 is a BIT STRING (0x03)
257+
local inner_tag = string.byte(der, content_start)
258+
if inner_tag == 0x03 then
259+
local bit_len, bit_start = get_length(der, content_start + 1)
260+
-- BIT STRINGS have a "leading null byte" (unused bits indicator)
261+
-- We skip that byte (bit_start) and the 0x04 EC prefix to get the raw X/Y coordinates
262+
local actual_key_start = bit_start + 2
263+
local actual_key_len = bit_len - 2
264+
pubKey = PUB_KEY_PREFIX .. utils.bytes_to_hex_string(string.sub(der, actual_key_start, actual_key_start + actual_key_len - 1))
265+
end
266+
end
267+
-- Move pointer to the next tag
268+
pos = content_start + len
269+
end
270+
271+
if not privKey or not pubKey then
272+
device.log.error("Failed to extract keys from DER")
273+
end
274+
return privKey, pubKey
275+
end
276+
149277
return lock_utils

0 commit comments

Comments
 (0)