fix: enable DTLS peer verification against Samsung OCF Root CA

Bundle certs/ocf_root_ca.pem as samsung_appliance/ocf_root_ca.pem and
switch from SSL.VERIFY_NONE to SSL.VERIFY_PEER. The appliance's server
certificate must now chain to the Samsung Electronics OCF Root CA,
closing the ARP-spoof / MITM attack surface on the local network.

.gitignore: carve out explicit exception for the bundled public CA cert.
This commit is contained in:
Marc Billow
2026-06-30 16:34:10 -05:00
parent ceab7585d9
commit d5dc92e673
4 changed files with 28 additions and 14 deletions
+3
View File
@@ -9,6 +9,9 @@ certs/
*.p12
*.pfx
*.key
# Exception: bundled Samsung OCF Root CA (public cert, no key material)
!samsung_appliance/ocf_root_ca.pem
token.txt
# Python artifacts
+5 -13
View File
@@ -5,20 +5,12 @@ not malicious code — the repo is safe to run. Fix before exposing to untrusted
---
## 1. DTLS Certificate Verification Disabled
## 1. DTLS Certificate Verification Disabled ✅ RESOLVED
**File:** `samsung_appliance/coap_dtls.py:228`
**Severity:** High | **Category:** Authentication Bypass / Crypto
The DTLS context uses `SSL.VERIFY_NONE` with a lambda that accepts any certificate.
The bridge does not verify the appliance's identity.
**Exploit:** LAN attacker ARP-spoofs the appliance IP, presents a self-signed cert,
and either poisons state payloads (e.g. fake oven temp) or intercepts/drops commands.
**Fix:** Use `SSL.VERIFY_PEER` + `ctx.load_verify_locations()` pointing to the Samsung
CA cert already on disk. The `@SECLEVEL=0` SHA-1 workaround can coexist with peer
verification — `VERIFY_NONE` is not required for it.
`certs/ocf_root_ca.pem` (Samsung Electronics OCF Root CA) bundled at
`samsung_appliance/ocf_root_ca.pem`. `coap_dtls.py` now uses
`SSL.VERIFY_PEER` + `ctx.load_verify_locations(_OCF_ROOT_CA)`.
ARP-spoof mitigation: the appliance's cert must chain to the Samsung OCF Root CA.
---
+5 -1
View File
@@ -23,9 +23,12 @@ import socket
import struct
import threading
import time
from pathlib import Path
from OpenSSL import SSL
_OCF_ROOT_CA = str(Path(__file__).parent / 'ocf_root_ca.pem')
import logging
logger = logging.getLogger(__name__)
@@ -226,7 +229,8 @@ class DtlsCoapSession:
"""DTLS handshake. Blocks up to HANDSHAKE_TIMEOUT_S. Raises
ConnectionError / TimeoutError on failure."""
ctx = SSL.Context(SSL.DTLS_METHOD)
ctx.set_verify(SSL.VERIFY_NONE, lambda *_: True)
ctx.load_verify_locations(_OCF_ROOT_CA)
ctx.set_verify(SSL.VERIFY_PEER, lambda conn, cert, err, depth, ok: ok)
ctx.set_cipher_list(b'ECDHE-ECDSA-AES128-GCM-SHA256')
ctx.use_certificate_chain_file(self.cert_path)
ctx.use_privatekey_file(self.key_path)
+15
View File
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICXTCCAgGgAwIBAgIBATAMBggqhkjOPQQDAgUAMGsxKDAmBgNVBAMTH1NhbXN1
bmcgRWxlY3Ryb25pY3MgT0NGIFJvb3QgQ0ExFDASBgNVBAsTC09DRiBSb290IENB
MRwwGgYDVQQKExNTYW1zdW5nIEVsZWN0cm9uaWNzMQswCQYDVQQGEwJLUjAgFw0x
NjExMjQwMjU1MTFaGA8yMDY5MTIzMTE0NTk1OVowazEoMCYGA1UEAxMfU2Ftc3Vu
ZyBFbGVjdHJvbmljcyBPQ0YgUm9vdCBDQTEUMBIGA1UECxMLT0NGIFJvb3QgQ0Ex
HDAaBgNVBAoTE1NhbXN1bmcgRWxlY3Ryb25pY3MxCzAJBgNVBAYTAktSMFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAEYsf/Kx+sUFBQESbuytTDPwLPIe0X/8/B1L7b
3abxE9w0gQZAfI8WYUkKfNfP7HXh1M5SCnOkfwWraltGOKTeX6OBkTCBjjAOBgNV
HQ8BAf8EBAMCAcYwMgYDVR0fBCswKTAnoCWgI4YhaHR0cDovL3Byb2RjYS5zYW1z
dW5naW90cy5jb20vY3JsMA8GA1UdEwEB/wQFMAMBAf8wNwYIKwYBBQUHAQEEKzAp
MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5zYW1zdW5naW90cy5jb20wDAYIKoZI
zj0EAwIFAANIADBFAiARY9aSE30q31q5v8B4sJczBqOp7AsD9o8ZIuNmH7IwSwIh
ALfR56jcXoFis/nDx0tQ2xTI/f0b7F6tWPqj2vyKQeNR
-----END CERTIFICATE-----