Skip to content

feat(protocol): add pure OwnerPSK derivation - #45

Merged
QuiteYellow merged 1 commit into
QuiteYellow:mainfrom
Moballo-LLC:codex/owner-psk-derivation
Aug 17, 2026
Merged

feat(protocol): add pure OwnerPSK derivation#45
QuiteYellow merged 1 commit into
QuiteYellow:mainfrom
Moballo-LLC:codex/owner-psk-derivation

Conversation

@Jason-Morcos

@Jason-Morcos Jason-Morcos commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Implements the pure OwnerPSK portion agreed in #28.

After an authenticated manufacturer-certificate session, IoTivity derives the 128-bit OwnerPSK in two stages:

  1. expand the 48-byte TLS master secret with the TLS 1.2 SHA-256 PRF, the “key expansion” label, and server-random followed by client-random;
  2. expand that cipher-sized key block with the selected OXM label, owner UUID, and device UUID.

The new helper keeps every security-relevant input explicit and validates the exact TLS/OCF shapes before deriving anything. It accepts the eight IoTivity cipher names and matching key-block lengths used by pyOpenSSL, rejects nil UUIDs and unknown ciphers, and requires the caller to choose either the standard manufacturer-certificate label or the confirmed-manufacturer label. The documentation calls out that the label comes from the selected DOXM OXM, not the appliance model.

The PRF ordering and key-block table follow IoTivity’s CAsslGenerateOwnerPsk implementation. Its lower adapter uses historical resource/provisioning parameter names, but the actual ownership-transfer caller passes owner UUID first and target-device UUID second; the helper and a fixed reverse-order regression preserve that caller contract. The two accepted label values come from IoTivity’s security resource strings.

This is the same derivation path needed after the authenticated manufacturer-certificate channel on the Samsung profiles I have working locally. It stops at deterministic key derivation: obtaining authenticated TLS state remains the caller’s job, while OCF security-resource writes, ownership-transfer policy, persistence, and cloud/account work remain outside this package.

Coverage includes fixed GCM and CCM8 vectors, explicit owner/device ordering, both OXM labels, the complete immutable cipher table, exact type/length validation, nil UUID rejection, secret-safe errors, and an exact public signature contract.

Validation:

  • SmartThings-Local: 252 tests on Python 3.14
  • dependency floor: 252 tests on Python 3.11 with the minimum declared versions
  • current LocalThings main: 1,555 tests
  • wheel and sdist install/import checks
  • distribution and share-safety checks

This is one commit directly on v0.1.8. It is independent of #42, #43, #36, and #44; those changes can merge in any order.

@Jason-Morcos
Jason-Morcos force-pushed the codex/owner-psk-derivation branch from 8970113 to 79493fb Compare August 15, 2026 19:24
@QuiteYellow

Copy link
Copy Markdown
Owner

I checked this against the IoTivity source rather than against the tests, all looks good.

Two non-blocking notes.

The tests are named test_fixed_iotivity_gcm_vector and test_fixed_iotivity_ccm8_vector, but the values are generated by this implementation rather than captured from IoTivity or a device. They are right, but they could not have caught a wrong key-block length, which is the failure they are named for. A comment saying where they came from would stop the next reader trusting them for more than they cover.

The table also omits the three other suites IoTivity maps to 96: ECDH_ANON, ECDHE_PSK and ECDHE_RSA with AES-128-CBC-SHA256. Reasonable to leave out given the constant is scoped to manufacturer certificates, but one docstring line saying so would help.

Scope is right. Deterministic derivation with no session access, no writes, no persistence, and no caller yet. Merging.

@QuiteYellow
QuiteYellow merged commit cd86424 into QuiteYellow:main Aug 17, 2026
8 checks passed
@Jason-Morcos

Copy link
Copy Markdown
Contributor Author

Followed up in #46. The two tests are now named as synthetic regression vectors, with a short provenance note explaining that they are not captures and do not independently prove the key-block lengths. The derivation docstring also calls out the three other 96-byte IoTivity mappings that are intentionally outside the manufacturer-certificate allowlist. No behavior change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants