eebus icon indicating copy to clipboard operation
eebus copied to clipboard

Elli Wallbox eebus not detected

Open vheat opened this issue 3 years ago • 89 comments

Today (2022-05-31) Elli distributed a new firmware of Elli Wallbox (ID.charger and other Volkwagen models). This offers eebus interaction. However evcc-io/eebus does neither recognize the mDNS coming from the wallbox nor the wallbox lists the EVCC for a HEMS. Looks like the mDNS is also not recognized correctly by the box.

If needed I can provide a wireshark trace for the mDNS (both sources).

There are notices around that SMA can deal with the Elli for EEbus control, also the SMA looks to have some issues showing the Elli, but this is just early information.

I'm trying to get the EEbus SHIP specifcation to verify the mDNS records.

In EVCC log level trace there is not any indication of an mDNS entry being received (in [eebus] and other records)

vheat avatar May 31 '22 21:05 vheat

Same here. evcc 0.94 and Elli Wallbox updated to the latest firmware.

I can confirm receiving mDNS / SHIP announcements from the wallbox, with tcpdump I see SRV records with apparently valid A and AAAA fields.

The "evcc charger" returns nothing, and from the wallbox web UI I see no available "energy managers".

@vheat the SHIP specs are publicly available, let me know if you did not find them.

My wallbox config block in evcc.yaml is

- type: template
  template: eebus 
  ski: ABxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx3A
  enforcePVLimits: true  
  name: wallbox3

I have some experience with Go and had a quick look at the SHIP and SPINE specifications, if there's anything I can do to help in debugging this issue I will be glad to.

ginsolvibile avatar Jun 05 '22 10:06 ginsolvibile

@ginsolvibile SHIP only happens after the discovery. Without any logging I'm still unsure what exactly fails during discovery.

@DerAndereAndi mentioned mDNS but I'm not sure what goes wrong.

It would probably help to have the Go mDNS listener to add some logging.

andig avatar Jun 05 '22 13:06 andig

I am on it with some success and some problems. Not done yet. The current implementation is not compatible, you need to wait.

DerAndereAndi avatar Jun 05 '22 14:06 DerAndereAndi

@ginsolvibile SHIP only happens after the discovery. Without any logging I'm still unsure what exactly fails during discovery.

What I can see via wireshark is that the wallbox sends 3 mDNS queries (AAAA, A and SRV) and then immediately answers itself. The SRV query is:

Elli-Wallbox-1234xxxxxx._ship._tcp.local: type SRV, class IN, "QM" question
    Name: Elli-Wallbox-1234xxxxxx._ship._tcp.local
    [Name Length: 40]
    [Label Count: 4]
    Type: SRV (Server Selection) (33)

and the corresponding answer is

Elli-Wallbox-1234xxxxxx._ship._tcp.local: type SRV, class IN, cache flush, priority 0, weight 0, port 4712, target wallbox-1234xxxxxx.local
    Service: Elli-Wallbox-1234xxxxxx
    Protocol: _ship
    Name: _tcp.local
    Type: SRV (Server Selection) (33)
    .000 0000 0000 0001 = Class: IN (0x0001)
    1... .... .... .... = Cache flush: True
    Time to live: 120 (2 minutes)
    Data length: 27
    Priority: 0
    Weight: 0
    Port: 4712
    Target: wallbox-1234xxxxxx.local

@DerAndereAndi mentioned mDNS but I'm not sure what goes wrong.

It would probably help to have the Go mDNS listener to add some logging.

Let me know if you want me to do some tests on my network.

ginsolvibile avatar Jun 05 '22 15:06 ginsolvibile

I am on it with some success and some problems. Not done yet. The current implementation is not compatible, you need to wait.

Thank you @DerAndereAndi and @andig for all the work you've done, implementing eebus from scratch must have been a challenging task. After 2 years waiting for Elli to deliver the protocol they promised :) I can certainly wait all the time you need.

I'm available for further testing, if you need.

ginsolvibile avatar Jun 05 '22 15:06 ginsolvibile

The problem with mDNS here is the zeroconf library. Elli can not see the mDNS entry from this library and the library only sometimes sees entries from the Elli wallbox. When it does see the Elli wallbox, there are always some mandatory items missing, either the network record or the TXT record.

I implemented a workaround to use the native avahi linux system when available. That way discovery works flawlessly. My desire to make zerconf work here is very very low.

For ship to work I already have some fixes, they were minor. The bigger task is SPINE, as they use EEBUS very differently compared to Porsche/Audi and also very buggy (e.g. don't providing lots of mandatory data). I got the communication running, but the box still shows with its LED that the HEMS is not active and charging is fixed to 6A per current. All changes are accepted in EEBUS but ignored. I am trying to somehow get hold on someone at Elli.

The other remaining issue is the certificate Elli or better EVBox uses (they are the 3rd party building the wallbox and developing the software). The certificate is not conforming to the DER standard and marking the isCA flag with 0x1 for true instead of 0xFF and go crypto checks for 0xFF and rejects the certificate otherwise. More info here: https://forum.golangbridge.org/t/x509-certificate-parse-error-with-iot-device/27622/2

DerAndereAndi avatar Jun 05 '22 16:06 DerAndereAndi

Ich hab mal bei GE angefragt ob jemand von VW mit liest.

andig avatar Jun 06 '22 09:06 andig

@ginsolvibile

What I can see via wireshark is that the wallbox sends 3 mDNS queries

You could try the _ship._tcp queries in Go and check why Go doesn't decode the responses (if you can see them on tcpdump).

andig avatar Jun 06 '22 09:06 andig

For more discussions see https://github.com/evcc-io/evcc/discussions/1217#discussioncomment-2866752

vheat avatar Jun 06 '22 09:06 vheat

I am not an asn.1 expert, but if I read https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-boolean correct the bool is a valid true.

StefanSchoof avatar Jun 06 '22 09:06 StefanSchoof

Here the expertise from an SSL expert (not me):

..., im Debugger sieht man dass im Zertifikat von der Elli eine 0x01 kommt, cryptobyte/asn1.go erwartet aber 0xFF.

In der zugehörigen Spec zu ASN.1 https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.690-202102-I!!PDF-E&type=items Chapter 8.2 steht aber, dass alles außer 0x00 dann TRUE ergeben soll. Insofern ist go cryptobyte/asn1.go falsch.

image

Dann, der commit 06a226f https://cs.opensource.google/go/x/crypto/+/793ad666bf5ec61392092b27061be9618e4e219b:cryptobyte/asn1_test.go;bpv=1;bpt=0;drc=06a226fb4e3765ef3f48aa2852b401bc7b98e981;dlc=18b771bd64f19baf6611ce73e30afe2e1a50082c führt den Test ein und verweist hier auf DER in Chapter 11.1 und da steht dann tatsächlich, dass alle Bits 1 sein sollen.

Image

...

... sage ich mal, BER oder DER beschreiben die „encoding rules“, also wie sollte es eingepackt werden. Cryptobyte macht das Decoding und da könnte man das m.E. etwas relaxter handeln. Frage für mich wäre, ob beim Cert irgendwas steht, das es DER encrypted ist, also der Decoder auch DER machen soll. Dann wäre das Problem bei Elli. Evtl kann man ja auch beim TLS einstellen, wie das Cert decodiert werden soll, ob DER oder BER. Da ich in Cryptobyte aber keine Möglichkeit sehe, zwischen BER und DER zu unterscheiden, ist das müßig zu diskutieren.

Ich denke, Cryptobyte sollte die Condition beim Decodieren relaxen. Beim Encodieren machen sie immer 0 oder FF. Sie scheinen den Anspruch zu haben einen ASN1 DER En-/Decoder zu haben.

...

Elli könnte man auch ansprechen, die X.509 Spec https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 sagt, das ein V3 Cert nach DER encodiert sein soll. Hier ist Elli definitiv falsch.

Ich hab im Moment nur den RFC, die ISO wird dem aber vermutlich nicht widersprechen ISO/IEC 9594-8:2020 https://www.iso.org/standard/80325.html

DerAndereAndi avatar Jun 06 '22 10:06 DerAndereAndi

Das Cert der Elli bekommt man mit openssl s_client -connect 192.168.1.180:4712 -showcerts Bitte IP und Port entsprechend dem mDNS Eintrag für SHIP der Elli setzen. Für HTTPS/Port 443 ist das Cert korrekt (FF)

Aus dem Ergebnis das Cert ausschneiden (von BEGIN bis END CERTIFICATE und z.B. in wallbox.crt kopieren) Das Cert der Elli kann dann man auseinander nehmen mit :~/wallbox$ openssl asn1parse -in wallbox.crt .... 275:d=4 hl=2 l= 12 cons: SEQUENCE
277:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints 282:d=5 hl=2 l= 5 prim: OCTET STRING [HEX DUMP]:3003010101 289:d=4 hl=2 l= 29 cons: SEQUENCE
... Da sieht man sehr schön die fehlerhafte 01

Der freie OSS Nokalva ASN1 Decoder
https://asn1.io/PKI-inspector/default.aspx? (bitte X.509 auswählen) markiert die BasicConstraints der Elli auch rot als Fehler, decodiert aber dennoch nach TRUE. Ein FF würde ohne Fehler angezeigt.

Da scheint die Elli intern die Certs für https und SHIP unterschiedlich zu erzeugen.

vheat avatar Jun 06 '22 10:06 vheat

Bzgl. SSL: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ schreibt:

ASN.1’s main serialization format is “Distinguished Encoding Rules” (DER). They are a variant of “Basic Encoding Rules” (BER) with canonicalization added. For instance, if a type includes a SET OF, the members must be sorted for DER serialization.

Ich würde das Go issue aufmachen, weiß aber nicht wie ich mit

Frage für mich wäre, ob beim Cert irgendwas steht, das es DER encrypted ist, also der Decoder auch DER machen soll.

umgehen soll. Kann der Experte vllt nochmal sagen, ob/wie man ihm das ansieht? Hilfreich wäre auch ein Callstack von der Stelle wo das Bool Decoding aussteigt- dann wüsste man wenigstens was Go da in den höheren Levels gerade versucht.

andig avatar Jun 07 '22 20:06 andig

Allerdings sieht das schlecht aus: https://github.com/golang/go/issues/48210#issuecomment-913506442

It looks like there's no plan to add BER support

andig avatar Jun 07 '22 20:06 andig

Aber vielleicht hilft das ja: https://github.com/go-asn1-ber/asn1-ber

andig avatar Jun 07 '22 20:06 andig

Hallo, ich bedanke mich für die Blumen :-) Also die ASN1 Spec findet man hier https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.690-202102-I!!PDF-E&type=items

Direkt die Introduction in dieser Spec sagt, Seite v

This Recommendation | International Standard defines encoding rules that may be applied to values of types defined using
the ASN.1 notation. Application of these encoding rules produces a transfer syntax for such values. It is implicit in the
specification of these encoding rules that they are also to be used for decoding.

Damit ist Decoding wie Encoding und die Position von cryptobyte ist akzeptabel. Elli ist damit definitiv falsch. Schaut man sich die Decodierung z.B. mit dem freien OSS Nokalva https://asn1.io/PKI-inspector/ an, meldet der zwar keinen harten Fehler, markiert die BasicConstraints aber rot, um das Problem anzuzeigen.

Persönlich fände ich auch gut, die Varianz im TLS Cert möglichst gering zu halten, insofern hat die TLS/X.509 Spec auch DER gewählt. Es stört uns nur im Moment... Aber ich denke kurz-/mittelfristig muss Elli hier nachbessern, Ich halte ein falsch encodiertes TLS Cert schon für problematisch.

vheat avatar Jun 07 '22 20:06 vheat

Der asn1-ber vergleicht für BOOLEAN True mit be.go line 365

		case TagBoolean:
			val, _ := ParseInt64(content)

			p.Value = val != 0

Ebenso beim Encoding Line 526, da wird einfach der Wert != 0 genommen.

func NewBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet {
	intValue := int64(0)

	if value {
		intValue = 1
	}
	p := Encode(classType, tagType, tag, nil, description)
	p.Value = value
	p.Data.Write(encodeInteger(intValue))

Sollten wir den BER Code allgemein verwenden, könnten wir an anderer Stelle in Problem laufen, sollte die Elli intern dann doch DER machen.

Nur als Randbemerkung, das Cert, dass die Elli für https / lokales Config Interface verwendet, ist korrekt mit 0xFF kodiert.

vheat avatar Jun 07 '22 20:06 vheat

Bleibt die Frage wie wir die Library überhaupt nutzen könnten:

	// VerifyPeerCertificate, if not nil, is called after normal
	// certificate verification by either a TLS client or server. It
	// receives the raw ASN.1 certificates provided by the peer and also
	// any verified chains that normal processing found. If it returns a
	// non-nil error, the handshake is aborted and that error results.
	//
	// If normal verification fails then the handshake will abort before
	// considering this callback. If normal verification is disabled by
	// setting InsecureSkipVerify, or (for a server) when ClientAuth is
	// RequestClientCert or RequireAnyClientCert, then this callback will
	// be considered but the verifiedChains argument will always be nil.

Der zweite Absatz dürfte der spannende sein. Jetzt müsste man mal testen, ob sich die Verifikation damit umgehen lässt. Ich fürchte aber auch das würde das Problem nicht lösen, da uns das Zertifikat denn nicht zum Verschlüsseln zur Verfügung stünde?

andig avatar Jun 08 '22 06:06 andig

Nein, die Methode kann man nicht verwenden, er steigt vorher aus.

In https://github.com/golang/go/blob/master/src/crypto/tls/handshake_client.go#L856 geht er aus verifyServerCertificate raus. In der gleichen Methode in https://github.com/golang/go/blob/master/src/crypto/tls/handshake_client.go#L892 würde VerifyPeerCertificate aufgerufen.

Hilft also leider nicht. Es scheint es gibt hier nur 2 Lösungen:

  • Go erlaubt den invaliden Wert 1
  • Elli erstellt auf den Geräten ein neues Zertifikat, was auch zur Konsequenz hätte dass alle bisher gekoppelten HEMS Systeme neu gekoppelt werden müssten

Übersehe ich eine Lösungsvariante?

DerAndereAndi avatar Jun 08 '22 07:06 DerAndereAndi

Ich seh eigentlich nur die zweite Lösung, Elli benutzt hier ein formal inkorrektes Zertifikat (soll DER sein). Lösung 1 wäre für mich ein Workaround bis dahin. Gibt es bei Go eine Möglichkeit, privat einen Patch auf ein vorhandenes Paket zu setzen?

vheat avatar Jun 08 '22 07:06 vheat

Ein anderer (aber auch kein schöne) Möglichkeit wäre das über einen Proxy, der das Zert akzeptiert und ein eignes Präsentiert laufen zu lassen.

StefanSchoof avatar Jun 08 '22 07:06 StefanSchoof

Ich hatte versucht das in ein eigenes gepatchtes Crypto Paket zu forken, aber die internen Abhängigkeiten sind recht groß, so dass ich davon abgelassen habe. Einen anderen Weg das "schön" zu patchen, ist mir nicht bekannt. Vielleicht hat da aber @andig eine Idee, er hat da viel mehr Erfahrung.

Ob und bis Elli das korrigiert, wird kein kurzfristiger Prozess sein, sondern eher Monate dauernd (nach meiner Erfahrung).

Ein Proxy zu nutzen ist nicht trivial möglich. Es muss zwischen den Geräten eine stehende TLS WebSocket Verbindung geben und Informationen aus dem Zertifikat in ein Proxy Zertifikat müssten transparent übersetzt werden, da diese Zertifikatsinfos auch in der weiteren Kommunikation relevant sind. Nach meiner Einschätzung wäre das extrem viel Arbeit und zu wissen ob das auch funktionieren könnte. Den Aufwand imho nicht Wert.

DerAndereAndi avatar Jun 08 '22 08:06 DerAndereAndi

Ich seh eigentlich nur die zweite Lösung, Elli benutzt hier ein formal inkorrektes Zertifikat (soll DER sein). Lösung 1 wäre für mich ein Workaround bis dahin. Gibt es bei Go eine Möglichkeit, privat einen Patch auf ein vorhandenes Paket zu setzen?

Ja, go mod replace. Das wäre auch die Lösung um die zeroconf ohne Codeänderungen auszutauschen. Ich weiss allerdings nicht, ob das auch mit internen Modulen funktioniert- wäre aber einen Versuch wert (kann ich auch testen).

Ein anderer (aber auch kein schöne) Möglichkeit wäre das über einen Proxy, der das Zert akzeptiert und ein eignes Präsentiert laufen zu lassen.

Coole Idee. Gibts da was?

andig avatar Jun 08 '22 08:06 andig

Ja, go mod replace. Das wäre auch die Lösung um die zeroconf ohne Codeänderungen auszutauschen. Ich weiss allerdings nicht, ob das auch mit internen Modulen funktioniert- wäre aber einen Versuch wert (kann ich auch testen).

Kann man damit auch einzelne Files ersetzen? Das Modul zu extrahieren um es zu patchen hatte ich versucht aber die internen Querverbindungen (Referenzen auf /internal im Root) waren mit dann zu viel.

Ich versuche das mal fix nur mit Cryptobyte

Update: bekomme es nicht hin.

DerAndereAndi avatar Jun 08 '22 08:06 DerAndereAndi

libp2p/zeroconf hat das Interface ganz leicht geändert. Man kann nun einige Zeilen Init Code (den Resolver) rauslassen. D.h. ich würde da schon wechseln und dann evtl. mit replace auf nen Fork gehen. Da das momentan noch nicht viel bringt, würde ich erst mal warten ob die das mergen bzw. wie lange das dort dauert.

DerAndereAndi avatar Jun 08 '22 08:06 DerAndereAndi

Ich versuche das mal fix nur mit Cryptobyte Update: bekomme es nicht hin.

Ich denke das geht nur auf ganze Module und nicht einzelne Packages :( Und dann hängst Du wieder an den internal Dependencies. Geht wohl nicht :(

andig avatar Jun 08 '22 09:06 andig

https://groups.google.com/g/golang-nuts/c/-wJDIS2DZEI/m/JCVtzkpNAwAJ

andig avatar Jun 08 '22 09:06 andig

libp2p/zeroconf hat das Interface ganz leicht geändert.

@DerAndereAndi könntest Du den PR auch gegen die originale zeroconf auf machen? Der Fork ist nicht "offiziell" und es wäre zumindest schön auch Upstream die Möglichkeit zu geben, damit zu arbeiten und noch ist das Repo deutlich populärer. https://github.com/grandcat/zeroconf/issues/27#issuecomment-1148576616 scheint mir im Fork ebenfalls nicht gelöst zu sein. Ich kanns sonst auch gerne rüber schieben...

andig avatar Jun 08 '22 09:06 andig

Ein anderer (aber auch kein schöne) Möglichkeit wäre das über einen Proxy, der das Zert akzeptiert und ein eignes Präsentiert laufen zu lassen.

Coole Idee. Gibts da was?

Ich habe noch nichts benutzt. Per Google habe ich welche gefunden. "Leider" viele in go geschrieben, so dass sie nicht wahrscheinlich helfen können (https://github.com/suyashkumar/ssl-proxy, https://github.com/ghostunnel/ghostunnel). Gibt auch was in rust https://lib.rs/crates/proxyboi

StefanSchoof avatar Jun 08 '22 09:06 StefanSchoof

Update: hab es hinbekommen und den Branch einfach von nem älteren Commit gemacht: https://github.com/grandcat/zeroconf/pull/108

DerAndereAndi avatar Jun 08 '22 10:06 DerAndereAndi