Skip to content

v4.1.0

Choose a tag to compare

@GianfriAur GianfriAur released this 13 Apr 07:46
· 57 commits to master since this release
4c33e2b

[v4.1.0] - 2026-04-13

Added

  • ECC security policies: ECC_nistP256, ECC_nistP384, ECC_brainpoolP256r1, and ECC_brainpoolP384r1. Full Elliptic Curve Cryptography support for OPC UA secure channels, including (see ECC disclaimer and 1.05.4 compliance roadmap):

    • ECDSA signatures (SHA-256 / SHA-384) for OpenSecureChannel (sign-only, no asymmetric encryption)
    • ECDH ephemeral key agreement for symmetric key derivation
    • HKDF-SHA256 / HKDF-SHA384 key derivation with mode-dependent salt (replaces P_SHA for ECC)
    • HMAC-SHA256 / HMAC-SHA384 symmetric signing for MSG messages
    • AES-128-CBC (P-256) / AES-256-CBC (P-384) symmetric encryption
    • Auto-generated ECC certificates when no client certificate is provided (NIST P-256/P-384 or Brainpool P-256/P-384)
    • Username/password authentication via EccEncryptedSecret protocol (ECDH + AES + ECDSA signature)
    • ECDHPolicyUri request in CreateSession AdditionalHeader to obtain server ephemeral key
    • Parsing of ECDHKey (EphemeralKeyType) from server's AdditionalHeader response
    • Raw R||S ECDSA signature format conversion (DER to/from raw) for OPN, ActivateSession, and EncryptedSecret
  • New SecurityPolicy enum cases: EccNistP256, EccNistP384, EccBrainpoolP256r1, EccBrainpoolP384r1 with methods isEcc(), getEcdhCurveName(), getEphemeralKeyLength().

  • New MessageSecurity methods: computeEcdhSharedSecret(), deriveKeysHkdf(), generateEphemeralKeyPair(), loadEcPublicKeyFromBytes(), ecdsaDerToRaw(), ecdsaRawToDer().

  • New CertificateManager methods: getKeyType(), ECC certificate generation via optional $eccCurveName parameter on generateSelfSignedCertificate().

  • 7 new NIST ECC integration tests against the uanetstandard-test-suite ECC server (port 4848): P-256 Sign, P-256 SignAndEncrypt (anonymous + admin + read), P-384 SignAndEncrypt (anonymous + admin), P-384 Sign.

  • 7 new Brainpool ECC integration tests against the uanetstandard-test-suite Brainpool server (port 4849): brainpoolP256r1 Sign, brainpoolP256r1 SignAndEncrypt (anonymous + admin + read), brainpoolP384r1 SignAndEncrypt (anonymous + admin), brainpoolP384r1 Sign.

  • 5 new granular exception classes for more precise error handling (all backward-compatible — extend existing exceptions):

    • OpenSslException (extends SecurityException) — thrown when an OpenSSL function returns false.
    • SignatureVerificationException (extends SecurityException) — thrown when OPN or MSG signature verification fails.
    • UnsupportedCurveException (extends SecurityException) — thrown for unsupported ECC curves, with $curveName property.
    • MessageTypeException (extends ProtocolException) — thrown when the server responds with an unexpected message type, with $expected and $actual properties.
    • HandshakeException (extends ProtocolException) — thrown when the HEL/ACK handshake fails with a server error, with $errorCode property.
  • CertificateParseException (extends SecurityException) — thrown for missing fields in parsed certificates.

  • ECC disclaimer in README and Security documentation noting that no commercial OPC UA vendor supports ECC endpoints yet, and the implementation is tested exclusively against UA-.NETStandard.

  • ECC 1.05.4 compliance section in ROADMAP with detailed analysis of LegacySequenceNumbers and per-message IV requirements.

Changed

  • Unit test coverage raised to 99%+. Added 200+ unit tests across all source files. Every src/ class and trait now has dedicated coverage — Client traits, Security (SecureChannel, MessageSecurity, CertificateManager), Protocol (SessionService, MonitoredItemService), TrustStore (FileTrustStore), Cache (FileCache), and Types (DataValue).
  • Test infrastructure reorganized. Eliminated *BoostTest.php files. Extracted shared test helpers into tests/Unit/Helpers/ClientTestHelpers.php. Each source class now has its tests in the matching path (src/Foo/Bar.phptests/Unit/Foo/BarTest.php).
  • Exception hierarchy granularized. Generic SecurityException and ProtocolException throws replaced with specific subclasses (OpenSslException, SignatureVerificationException, UnsupportedCurveException, MessageTypeException, HandshakeException). All existing catch (SecurityException) and catch (ProtocolException) code continues to work unchanged.
  • EnsuresOpenSslSuccess trait. Extracted the duplicated ensureNotFalse() method from CertificateManager and MessageSecurity into a shared trait in src/Security/EnsuresOpenSslSuccess.php. Now throws OpenSslException instead of generic SecurityException.
  • MessageSecurity::getCoordinateSize(). Extracted duplicated EC coordinate size match expression into a reusable private method.
  • Diagnostic info parsing in BrowseService and WriteService. Replaced manual byte-reading loops with $decoder->skipDiagnosticInfoArray() for correct OPC UA DiagnosticInfo format parsing.
  • Removed all inline comments from function bodies per CONTRIBUTING.md guidelines. Extracted SessionService::readEccServerEphemeralKey() to replace commented ECC key parsing logic.
  • Removed glob() === false dead branches in FileTrustStore and FileCacheglob() never returns false on Linux with a valid pattern.
  • FileTrustStore::parseCertificateInfo changed from private to protected to enable proper subclass-based testing.
  • FileTrustStore::throwCertificateParseExceptionIfNull — new protected method replacing nullable ternary operators for certificate date fields, with dedicated CertificateParseException.