Add bound Samsung server certificate profile - #33
Conversation
|
I have a Samsung soundbar here (HW-S61B,
The leaf subject checks are where it breaks for this model:
This device selects manufacturer-cert OTM ( |
4138905 to
a95002f
Compare
|
Thanks — this was a good catch. You are right on both points. I had treated the UUID in the Samsung hardware leaf as though it were necessarily the runtime OCF UUID. They are separate identities on at least some Samsung PKI devices, so the latest revision now calls that input I also split the subject roles explicitly. The existing default remains For the server-authenticated manufacturer-cert channel you described, there is now a provider that does not load or send a client certificate: profile = SamsungServerProfile.bound_device(
factory_certificate_uuid,
role=SamsungServerRole.VD_DEVICE,
)
auth = ServerCertificateAuth(server_profile=profile)I added a complete synthetic DTLS handshake test where the server requests a client certificate, the client sends none, and the client still verifies the VD-role chain and pinned certificate UUID. The emitted ClientHello retains the P-256, cipher, sig-alg order, and no-ticket contract you observed. This should cover the authenticated transport part of the HW-S61B case. It deliberately does not perform manufacturer-cert OTM, and the caller still needs a trusted way to obtain the factory certificate UUID and bind that authenticated session to the expected runtime OCF UUID. If you are able to try the updated branch against the soundbar, that would be very helpful. |
|
I ran the updated branch against my soundbar. The connection side all works: the restricted ClientHello gets accepted, the client sends an empty Certificate when asked, the ECDHE-ECDSA-AES128-GCM-SHA256 session comes up, the chain verifies against the bundled root CA, and GET /oic/d over that session returns 2.05. The failure happens before the subject checks. The factory cert has trailing bytes inside What worked for me was falling back to OpenSSL's name parser when +_NAME_OIDS = {
+ "CN": NameOID.COMMON_NAME,
+ "OU": NameOID.ORGANIZATIONAL_UNIT_NAME,
+ "O": NameOID.ORGANIZATION_NAME,
+ "C": NameOID.COUNTRY_NAME,
+}
+
+def _subject_components(certificate):
+ try:
+ return [
+ (attribute.oid, attribute.value)
+ for relative_name in certificate.to_cryptography().subject.rdns
+ for attribute in relative_name
+ ]
+ except ValueError:
+ # Lenient path for factory leaves that are not clean DER
+ # (extra bytes inside TbsCertificate::signature_alg).
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+ pairs = certificate.get_subject().get_components()
+ return [
+ (_NAME_OIDS[key.decode()], value.decode())
+ for key, value in pairs
+ if key.decode() in _NAME_OIDS
+ ]( Only the parsing gets relaxed here, all the checks stay the same. With this in place, |
|
I also tried the same setup on a second device I have here — a Samsung TV (
With the parse fallback in place, So whatever issues these leaves does it the same way across VD model families and years: the strict-parse problem isn't specific to one device, and the VD role + certificate-identity pinning works as designed on both. |
|
vmonkey's second and third reports are the blocker here, and I think the fix is smaller than the patch they offered. The failure is a parser disagreement. OpenSSL has already parsed and verified this leaf by the time Two X.509 parsers, two answers. Samsung's factory leaves carry trailing bytes inside The blanket vmonkey's fallback works, but it keeps both parsers and adds a third path between them. I'd rather cut the second parser out of the peer-leaf path entirely: components = [
(key.decode(), value.decode())
for key, value in certificate.get_subject().get_components()
]That is the cert OpenSSL just verified, read through the API that verified it. No
Three asks before this goes in:
One non-blocking observation while you're in there. Pinning the exact Everything else in here looks right to me. The role split and the rename to This is the only thing blocking the merge. If the parser change lands quickly the stack order is unaffected; separate comment on #34 about what happens if it doesn't. |
a95002f to
83a5973
Compare
|
Thanks! agreed that the peer leaf should only go through the parser that already verified it. I rebased this onto v0.1.6 and changed _verify_peer to read the subject through OpenSSL’s get_subject().get_components() API. to_cryptography() remains only for the caller-supplied CA certificates, where we need the extension checks. I also removed the blanket exception. Subject parsing or decoding failures now produce a fixed warning and fail closed; ordinary role or UUID mismatches follow the normal verification-failure path without that parse warning. There’s also a comment explaining why the complete C/O/OU role remains deliberately pinned instead of following iotivity-lite’s CN-only check. The regression fixture is fully synthetic. It adds the trailing NULL to both signature AlgorithmIdentifier values, recomputes the leaf signature, verifies the chain through OpenSSL, and completes the full no-client-certificate DTLS handshake. The same leaf is still rejected with the wrong role or certificate UUID. It also captures the parser-version difference: cryptography 50 rejects the encoding while the declared dependency floor accepts it. |
|
Merged in v0.1.7, thanks. Reading the peer leaf through OpenSSL's One minor thing for a follow-up, non-blocking: On the soundbar: I don't have any VD hardware here, so I couldn't exercise the VD-role path against a real device; only the synthetic handshake test covers it. If you're able to point the |
|
Thanks! Re-ran merged v0.1.7 ( Three expectations, 3/3 met:
Data flow over the no-client-cert channel, all read-only GETs:
So the merged |
Context
This is the third authentication slice agreed in #28. The no-behavior-change
CertificateAuthrefactor (#31) and the isolatedPskAuthprovider (#32) are merged and released as v0.1.4 and v0.1.5.For the newer laundry certificate cohort, an already-authorized connection required all of these details together:
ECDHE-ECDSA-AES128-GCM-SHA256;RSA+SHA256:ECDSA+SHA256:RSA+SHA1:ECDSA+SHA1, with session tickets disabled;The HW-S61B details reported in this PR show that the same wire profile also appears on Samsung VD-family devices, while their certificate UUID can be distinct from the runtime OCF UUID reported by
/oic/d.What changes
SamsungServerProfile.bound_device(...).expected_certificate_identityas the UUID embedded in the hardware leaf, without assuming it equals the runtime OCF device UUID.SamsungServerRole.HOME_APPLIANCEandSamsungServerRole.VD_DEVICEroles forOU=OCF HA DeviceandOU=OCF VD Devicerespectively.CertificateAuth.from_filesandfrom_memoryfor client-certificate sessions.ServerCertificateAuthfor a server-authenticated channel that sends no client certificate.CertificateAuthcontext setup and verification callback unchanged.cryptographyreparsing it. Subject parse failures are logged and still fail closed.Identity and ownership boundary
The certificate UUID is a hardware identity, not necessarily the OCF application identity. A caller must obtain it through a trusted path, pin it before connecting, and separately bind the authenticated session to the expected runtime OCF UUID. The profile never learns an identity from the first LAN endpoint it reaches.
ServerCertificateAuthcovers only the authenticated DTLS carrier used by server-authenticated flows such as manufacturer-certificate OTM. Credential discovery, derivation, ownership decisions, OTM resource writes, provisioning, rotation, persistence, and cloud/account access remain outside this PR.How this relates to #16 and #20
Validation
AlgorithmIdentifiervalues: OpenSSL chain verification and the full DTLS handshake succeed, while the wrong subject role and UUID still fail.83a5973is signed and uses Jason’s GitHub noreply address.Related PRs
This PR is one commit directly on v0.1.6 and has no dependency on unmerged work.