@@ -362,22 +362,26 @@ class Matter_Plugin_Root : Matter_Plugin
362
362
363
363
elif command == 0x0004 # ---------- CommissioningComplete p.636 ----------
364
364
# no data
365
- session._breadcrumb = 0 # clear breadcrumb
366
- session.fabric_completed () # fabric information is complete, persist
367
- session.set_no_expiration ()
368
- session.save ()
369
-
370
- # create CommissioningCompleteResponse
371
- # ID=1
372
- # 0=ErrorCode (OK=0)
373
- # 1=DebugText
374
- var ccr = TLV.Matter_TLV_struct ()
375
- ccr.add_TLV ( 0 , TLV.U1 , 0 ) # ErrorCode = OK
376
- ccr.add_TLV ( 1 , TLV.UTF1 , "" ) # DebugText = ""
377
- ctx.command = 0x05 # CommissioningCompleteResponse
378
-
379
- self .device.start_commissioning_complete_deferred ( session)
380
- return ccr
365
+ if session._fabric
366
+ session._breadcrumb = 0 # clear breadcrumb
367
+ session._fabric.fabric_completed () # fabric information is complete, persist
368
+ session.set_no_expiration ()
369
+ session.save ()
370
+
371
+ # create CommissioningCompleteResponse
372
+ # ID=1
373
+ # 0=ErrorCode (OK=0)
374
+ # 1=DebugText
375
+ var ccr = TLV.Matter_TLV_struct ()
376
+ ccr.add_TLV ( 0 , TLV.U1 , 0 ) # ErrorCode = OK
377
+ ccr.add_TLV ( 1 , TLV.UTF1 , "" ) # DebugText = ""
378
+ ctx.command = 0x05 # CommissioningCompleteResponse
379
+
380
+ self .device.start_commissioning_complete_deferred ( session)
381
+ return ccr
382
+ else
383
+ raise "context_error" , "CommissioningComplete: no fabric attached"
384
+ end
381
385
end
382
386
383
387
elif cluster == 0x003E # ========== Node Operational Credentials Cluster 11.17 p.704 ==========
@@ -427,6 +431,7 @@ class Matter_Plugin_Root : Matter_Plugin
427
431
var CSRNonce = val.findsubval ( 0 ) # octstr 32
428
432
if size ( CSRNonce) != 32 return nil end # check size on nonce
429
433
var IsForUpdateNOC = val.findsubval ( 1 , false ) # bool
434
+ tasmota.log ( string.format ( "MTR: CSRRequest CSRNonce=%s IsForUpdateNOC=%s" , str ( CSRNonce) , str ( IsForUpdateNOC)) , 3 )
430
435
431
436
var csr = session.gen_CSR ()
432
437
@@ -450,34 +455,47 @@ class Matter_Plugin_Root : Matter_Plugin
450
455
451
456
elif command == 0x000B # ---------- AddTrustedRootCertificate ----------
452
457
var RootCACertificate = val.findsubval ( 0 ) # octstr 400 max
453
- session.set_ca ( RootCACertificate)
458
+ # TODO - additional tests are expected according to 11.17.7.13. AddTrustedRootCertificate Command
459
+ session.set_temp_ca ( RootCACertificate)
454
460
tasmota.log ( "MTR: received ca_root=" + RootCACertificate.tohex () , 3 )
455
461
ctx.status = matter.SUCCESS # OK
456
462
return nil # trigger a standalone ack
457
463
458
464
elif command == 0x0006 # ---------- AddNOC ----------
465
+ tasmota.log ( "MTR: AddNoc Args=" + str ( val) , 3 )
459
466
var NOCValue = val.findsubval ( 0 ) # octstr max 400
460
467
var ICACValue = val.findsubval ( 1 ) # octstr max 400
461
468
# Apple sends an empty ICAC instead of a missing attribute, fix this
462
469
if size ( ICACValue) == 0 ICACValue = nil end
463
470
var IpkValue = val.findsubval ( 2 ) # octstr max 16
464
471
var CaseAdminSubject = val.findsubval ( 3 )
465
472
var AdminVendorId = val.findsubval ( 4 )
473
+ # tasmota.log("MTR: AddNoc NOCValue=" + (NOCValue ? NOCValue.tohex() : ""), 3)
474
+ # tasmota.log("MTR: AddNoc ICACValue=" + (ICACValue ? ICACValue.tohex() : ""), 3)
475
+ # tasmota.log("MTR: AddNoc IpkValue=" + str(IpkValue), 3)
476
+ # tasmota.log("MTR: AddNoc CaseAdminSubject=" + str(CaseAdminSubject), 3)
477
+ # tasmota.log("MTR: AddNoc AdminVendorId=" + str(AdminVendorId), 3)
466
478
467
- if session.get_ca () == nil
479
+ if session.get_temp_ca () == nil
468
480
tasmota.log ( "MTR: Error: AdNOC without CA" , 2 )
469
481
return nil
470
482
end
471
483
472
- session.set_noc ( NOCValue, ICACValue)
473
- session.set_ipk_epoch_key ( IpkValue)
474
- session.set_admin_subject_vendor ( CaseAdminSubject, AdminVendorId)
484
+ var new_fabric = self .device.sessions.create_fabric ()
485
+ new_fabric.set_ca ( session.get_temp_ca ()) # copy temporary CA to fabric
486
+ new_fabric.set_noc_icac ( NOCValue, ICACValue)
487
+ new_fabric.set_ipk_epoch_key ( IpkValue)
488
+ new_fabric.set_admin_subject_vendor ( CaseAdminSubject, AdminVendorId)
489
+ new_fabric.set_pk ( session.get_pk ()) # copy the temporary commissioning PK to the fabric
475
490
476
491
# extract important information from NOC
477
492
var noc_cert = matter.TLV.parse ( NOCValue)
478
493
var dnlist = noc_cert.findsub ( 6 )
479
494
var fabric_id = dnlist.findsubval ( 21 )
480
495
var deviceid = dnlist.findsubval ( 17 )
496
+ # tasmota.log("MTR: AddNoc noc_cert=" + str(noc_cert), 3)
497
+ # tasmota.log("MTR: AddNoc dnlist=" + str(dnlist), 3)
498
+
481
499
if ! fabric_id || ! deviceid
482
500
tasmota.log ( "MTR: Error: no fabricid nor deviceid in NOC certificate" , 2 )
483
501
return false
@@ -486,25 +504,37 @@ class Matter_Plugin_Root : Matter_Plugin
486
504
if type ( fabric_id) == 'int' fabric_id = int64.fromu32 ( fabric_id) .tobytes () else fabric_id = fabric_id.tobytes () end
487
505
if type ( deviceid) == 'int' deviceid = int64.fromu32 ( deviceid) .tobytes () else deviceid = deviceid.tobytes () end
488
506
489
- var root_ca = matter.TLV.parse ( session.get_ca ()) .findsubval ( 9 ) # extract public key from ca
490
- root_ca = root_ca[ 1 ..] # remove first byte as per Matter specification
507
+ # tasmota.log("MTR: AddNoc fabric_id=" + str(fabric_id), 3)
508
+ # tasmota.log("MTR: AddNoc deviceid=" + str(deviceid), 3)
509
+
510
+ var root_ca_pub = session.get_temp_ca_pub ()
511
+ # tasmota.log("MTR: AddNoc root_ca_pub=" + str(root_ca_pub), 3)
512
+ # tasmota.log("MTR: AddNoc root_ca_pub=" + root_ca_pub.tohex(), 3)
513
+ root_ca_pub = root_ca_pub[ 1 ..] # remove first byte as per Matter specification
491
514
var info = bytes () .fromstring ( "CompressedFabric" ) # as per spec, 4.3.2.2 p.99
492
515
var hk = crypto.HKDF_SHA256 ()
493
516
var fabric_rev = fabric_id.copy () .reverse ()
494
- var k_fabric = hk.derive ( root_ca, fabric_rev, info, 8 )
495
- session.set_fabric_device ( fabric_id, deviceid, k_fabric, self .device.commissioning_admin_fabric )
517
+ var k_fabric = hk.derive ( root_ca_pub, fabric_rev, info, 8 )
518
+ var parent_fabric = session._fabric ? session._fabric : self .device.commissioning_admin_fabric # get parent fabric whether CASE or PASE
519
+ new_fabric.set_fabric_device ( fabric_id, deviceid, k_fabric, parent_fabric)
496
520
521
+ # tasmota.log("MTR: AddNoc k_fabric=" + str(k_fabric), 3)
497
522
# We have a candidate fabric, add it as expirable for 2 minutes
498
- session.persist_to_fabric () # fabric object is completed, persist it
499
- session.fabric_candidate ()
523
+ new_fabric.fabric_candidate ()
500
524
501
525
# move to next step
502
- self .device.start_operational_discovery_deferred ( session)
503
- # session.fabric_completed()
504
- tasmota.log ( "MTR: ------------------------------------------" , 3 )
505
- tasmota.log ( "MTR: fabric=" + matter.inspect ( session._fabric ) , 3 )
506
- tasmota.log ( "MTR: ------------------------------------------" , 3 )
507
- session._fabric.log_new_fabric () # log that we registered a new fabric
526
+ self .device.start_operational_discovery_deferred ( new_fabric)
527
+
528
+ # we keep the PASE session for 1 minute
529
+ if session.is_PASE ()
530
+ session.set_expire_in_seconds ( 60 )
531
+ end
532
+
533
+ # tasmota.log("MTR: ------------------------------------------", 3)
534
+ # tasmota.log("MTR: session=" + matter.inspect(session), 3)
535
+ # tasmota.log("MTR: fabric=" + matter.inspect(session._fabric), 3)
536
+ # tasmota.log("MTR: ------------------------------------------", 3)
537
+ new_fabric.log_new_fabric () # log that we registered a new fabric
508
538
# create NOCResponse
509
539
# 0=StatusCode
510
540
# 1=FabricIndex (1-254) (opt)
0 commit comments