gematik / lib-vau-csharp

This repository serves as a sample implementation in C# and implements the cryptographic part of the specification of the VAU protocol for ePA for all (gemSpec_Crypto Chapter 7).
Apache License 2.0
3 stars 1 forks source link

DC-Mocks cannot decrypt message created by lib-vau-csharp, exception occurs #5

Open CEiderEVIDENT opened 1 week ago

CEiderEVIDENT commented 1 week ago

After establishing a connection to the VAU-server within latest docker-image dc-mocks we try to send a request to /VAU-Status. Building the required inner request on using this c# implementation results in an exception:

2024-10-08 07:22:32 05:22:32.594 VPS TRACE EncryptedVauMessage: trying to decrypt:
2024-10-08 07:22:32       Complete message     : 0200010000000000000000a747e577ccfaf6ded00a4678f702d70bf47434ee26c7b4cb423081b420f272460148584f000000000000000083bcf012777196d5902fe34f02703580133e962f97e520df809402c4d063d1374b2e891ace023fff9de96d72cbc517f48842e0e06838785c96ea30d9ead871c250f5829aa06cd418fce8928a371c7ae7d4c720162e27592f8aa6c49fe1c1e62cb6df0f740f
2024-10-08 07:22:32       Message size (Bytes) : 156
2024-10-08 07:22:32       Complete header      : 0200010000000000000000a747e577ccfaf6ded00a4678f702d70bf47434ee26c7b4cb423081b420f27246
2024-10-08 07:22:32       Key to decrypt (K2_c2s_app_data): f141033ea97ac7c6d8d432dc5c590134393d18a98d1c3113c973d0d194382c0a
2024-10-08 07:22:32       -------------------------------
2024-10-08 07:22:32       Version  (1 Byte): 02
2024-10-08 07:22:32       PU       (1 Byte): 00
2024-10-08 07:22:32       Request  (1 Byte): 01
2024-10-08 07:22:32       Counter  (8 Byte): 0000000000000000
2024-10-08 07:22:32       KeyId   (32 Byte): a747e577ccfaf6ded00a4678f702d70bf47434ee26c7b4cb423081b420f27246
2024-10-08 07:22:32       IV      (12 Byte): 0148584f0000000000000000
2024-10-08 07:22:32       CT + GMAC        : 83bcf012777196d5902fe34f02703580133e962f97e520df809402c4d063d1374b2e891ace023fff9de96d72cbc517f48842e0e06838785c96ea30d9ead871c250f5829aa06cd418fce8928a371c7ae7d4c720162e27592f8aa6c49fe1c1e62cb6df0f740f
2024-10-08 07:22:32 
2024-10-08 07:22:32 05:22:32.595 VPS ERROR VauException: Transcript Error: Exception thrown whilst trying to decrypt VAU message: Tag mismatch
2024-10-08 07:22:32 de.gematik.vau.lib.exceptions.VauDecryptionException: Exception thrown whilst trying to decrypt VAU message: Tag mismatch
2024-10-08 07:22:32     at de.gematik.vau.lib.AbstractVauStateMachine.decryptVauMessage(AbstractVauStateMachine.java:181)
2024-10-08 07:22:32     at de.gematik.vau.server.VauServerController.decryptAndForwardRequest(VauServerController.java:125)
2024-10-08 07:22:32     at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
2024-10-08 07:22:32     at java.base/java.lang.reflect.Method.invoke(Unknown Source)
2024-10-08 07:22:32     at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:198)
2024-10-08 07:22:32     at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132)
2024-10-08 07:22:32     at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:297)
2024-10-08 07:22:32     at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:478)
2024-10-08 07:22:32     at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
2024-10-08 07:22:32     at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
2024-10-08 07:22:32     at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
2024-10-08 07:22:32     at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
2024-10-08 07:22:32     at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
2024-10-08 07:22:32     at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
2024-10-08 07:22:32     at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
2024-10-08 07:22:32     at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
2024-10-08 07:22:32     at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2097)
2024-10-08 07:22:32     at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145)
2024-10-08 07:22:32     at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
2024-10-08 07:22:32     at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
2024-10-08 07:22:32     at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
2024-10-08 07:22:32     at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:415)
2024-10-08 07:22:32     at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:445)
2024-10-08 07:22:32     at reactor.netty.http.server.HttpServerOperations.handleLastHttpContent(HttpServerOperations.java:862)
2024-10-08 07:22:32     at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:784)
2024-10-08 07:22:32     at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:115)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
2024-10-08 07:22:32     at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:311)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
2024-10-08 07:22:32     at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
2024-10-08 07:22:32     at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
2024-10-08 07:22:32     at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
2024-10-08 07:22:32     at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
2024-10-08 07:22:32     at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1407)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
2024-10-08 07:22:32     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
2024-10-08 07:22:32     at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)
2024-10-08 07:22:32     at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:799)
2024-10-08 07:22:32     at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
2024-10-08 07:22:32     at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
2024-10-08 07:22:32     at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:994)
2024-10-08 07:22:32     at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
2024-10-08 07:22:32     at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
2024-10-08 07:22:32     at java.base/java.lang.Thread.run(Unknown Source)
2024-10-08 07:22:32 Caused by: javax.crypto.AEADBadTagException: Tag mismatch
2024-10-08 07:22:32     at java.base/com.sun.crypto.provider.GaloisCounterMode$GCMDecrypt.doFinal(Unknown Source)
2024-10-08 07:22:32     at java.base/com.sun.crypto.provider.GaloisCounterMode.engineDoFinal(Unknown Source)
2024-10-08 07:22:32     at java.base/javax.crypto.Cipher.doFinal(Unknown Source)
2024-10-08 07:22:32     at de.gematik.vau.lib.AbstractVauStateMachine.decryptWithAesGcm(AbstractVauStateMachine.java:150)
2024-10-08 07:22:32     at de.gematik.vau.lib.AbstractVauStateMachine.decryptVauMessage(AbstractVauStateMachine.java:175)
2024-10-08 07:22:32     ... 50 common frames omitted
2024-10-08 07:22:32 05:22:32.602 VPS ERROR ServerExceptionHandler: {ErrorCode=5, MessageType=Error, Message=Transcript Error: Exception thrown whilst trying to decrypt VAU message: Tag mismatch}

From my point of view the vau-server recieves the inner message, can detect the structural header information but anything is wron how the message was encrypted

KillerCodeMonkey commented 1 week ago

same

KillerCodeMonkey commented 1 week ago

@CEiderEVIDENT make sure you are incrementing/setting the correct request counter. I forgot it and was sending another request with counter 0 which led to the mismatch error.

CEiderEVIDENT commented 1 week ago

@CEiderEVIDENT make sure you are incrementing/setting the correct request counter. I forgot it and was sending another request with counter 0 which led to the mismatch error.

@KillerCodeMonkey Do I have to increment the requestcounter manually?

KillerCodeMonkey commented 1 week ago

yes.

it is not necessary for the handshake requests, but if you want to get the nonce and so on in the next step you need to do it.

I just used the provided code here as a base to work on and created my own VauClient. There i can set and pass the current request count.

My Vau-CLient is more or less stateless, so i pass all necessary information (vaucid, encryption, decryption keys, keyid, request counter) between software parts, because we are forced to mix programming languages -> using c# for all those encryption stuff and nodejs/electron for our main application.

KillerCodeMonkey commented 1 week ago

@CEiderEVIDENT If you need more help or just some idea/information exchange you can contact me per mail bengt@codaline.io

But to be honest... i am far away from a dotnet/c# expert... and i never will/wanted to be it. :P

CEiderEVIDENT commented 1 week ago

After making some changes to ste the request-counter to a value!=0, the message is being succesfully decrypted

2024-10-09 09:57:21 07:57:21.857 VPS INFO VauServerController: Received request for VAU-CID: /1728460637227
2024-10-09 09:57:21 07:57:21.857 VPS TRACE EncryptedVauMessage: trying to decrypt:
2024-10-09 09:57:21       Complete message     : 0200010000000000000001e5bb9d5fc4932a24b7ea98db99981c6c0bccc3d7cb780bb3658de0cae941f25b493f20e200000000000000016d349873aa1067c84291e13ec82be9dda5555fa0a9e929fe472878654048b3972cf3dc2bbff4e235ceef36dc26f44309bddb9a4c7723ab9407f0f5ad481b3344dfe9c5a8d1b7df388468f53a70866f2f9b2b5d7c55b67f4c72786874a878e513b72b33116e
2024-10-09 09:57:21       Message size (Bytes) : 156
2024-10-09 09:57:21       Complete header      : 0200010000000000000001e5bb9d5fc4932a24b7ea98db99981c6c0bccc3d7cb780bb3658de0cae941f25b
2024-10-09 09:57:21       Key to decrypt (K2_c2s_app_data): 6e904dc0d4d91b7ebc7759cd7f7a447666e8fe20a78d6a48321902fbb6878f15
2024-10-09 09:57:21       -------------------------------
2024-10-09 09:57:21       Version  (1 Byte): 02
2024-10-09 09:57:21       PU       (1 Byte): 00
2024-10-09 09:57:21       Request  (1 Byte): 01
2024-10-09 09:57:21       Counter  (8 Byte): 0000000000000001
2024-10-09 09:57:21       KeyId   (32 Byte): e5bb9d5fc4932a24b7ea98db99981c6c0bccc3d7cb780bb3658de0cae941f25b
2024-10-09 09:57:21       IV      (12 Byte): 493f20e20000000000000001
2024-10-09 09:57:21       CT + GMAC        : 6d349873aa1067c84291e13ec82be9dda5555fa0a9e929fe472878654048b3972cf3dc2bbff4e235ceef36dc26f44309bddb9a4c7723ab9407f0f5ad481b3344dfe9c5a8d1b7df388468f53a70866f2f9b2b5d7c55b67f4c72786874a878e513b72b33116e
2024-10-09 09:57:21 
2024-10-09 09:57:21 07:57:21.858 VPS TRACE AbstractVauStateMachine: Successful decrypted ct as: 
2024-10-09 09:57:21  GET /VAU-Status HTTP/1.1

And the decrypted resutl looks the same as that what has been encrypted

CEiderEVIDENT commented 1 week ago

After making some changes to ste the request-counter to a value!=0, the message is being succesfully decrypted Only works for first request with counter=1. On Counter =2 the same error as before occurs

KillerCodeMonkey commented 1 week ago

@CEiderEVIDENT make sure you are sending maybe another request. I am doing the handshake, after that getting the nonce with counter 1 and after that the VAU-Status with counter 2 and it is working.

KillerCodeMonkey commented 1 week ago

@gematik1 i tried it again. And it is the case, that when the "epa-vau-proxy-server" receives a request counter >1 results in a tag mismatch.

e.g. first handshake, then get nonce with counter 1 (as bytes 00000001) and after that try to start authentication process or just get the vau-status with counter 2 (00000002) leads to a "tag mismatch error".

Just sending 1 all the time is a workaround for development.

But i guess this is then more an issue with the vau-proxy image?

i already tried latest image 1.0.11. Same problem there.

Request with counter 1

vau-proxy-server           | 06:59:30.318 VPS INFO VauServerController: Received request for VAU-CID: /1728543569434
....
vau-proxy-server           |       Message size (Bytes) : 188
vau-proxy-server           |       Complete header      : 020001000000000000000157fdea7f694801cec9a43711f4b73f431ec884864d5e60d66934a0364250f3bd
vau-proxy-server           |       Key to decrypt (K2_c2s_app_data): 2a1ae0426c9f27b422fd1273586753f5c2a773118147e4fa5fd58cad5b6dc18b
vau-proxy-server           |       -------------------------------
vau-proxy-server           |       Version  (1 Byte): 02
vau-proxy-server           |       PU       (1 Byte): 00
vau-proxy-server           |       Request  (1 Byte): 01
vau-proxy-server           |       Counter  (8 Byte): 0000000000000001
vau-proxy-server           |       KeyId   (32 Byte): 57fdea7f694801cec9a43711f4b73f431ec884864d5e60d66934a0364250f3bd
vau-proxy-server           |       IV      (12 Byte): 1e051a640000000000000001
vau-proxy-server           |       CT + GMAC        : 41340a500abd47c072c95299919bc46b2f747b726eba8edf0ed01b4bb8918d5bd86475b1563d3d9a42b6fdda69560ec0175fa18ec628e197d9217454af889b7362ad2aa8ee2f757212e45c122b44f8b688409b75df10e140e875165c1f71faf17fcc2c1d3d7e37e39bc869e51095c82d8e6f2c85ae578b5c21823fa61c39ff299cd14a6c5b
vau-proxy-server           | 
vau-proxy-server           | 06:59:30.321 VPS TRACE AbstractVauStateMachine: Successful 

Request with counter 2

vau-proxy-server           | 06:59:31.096 VPS INFO VauServerController: Received request for VAU-CID: /1728543569434
vau-proxy-server           | 06:59:31.096 VPS TRACE EncryptedVauMessage: trying to decrypt:
vau-proxy-server           |       Complete message     : 020001000000000000000257fdea7f694801cec9a43711f4b73f431ec884864d5e60d66934a0364250f3bd0cf094b90000000000000002dabbecb5195ea0383a2b0d767469b07666b1900931b7af2ff68a62a903f3af27fc185bad4aefad3c2d1b272bce3cdb3cc122f8863aa19d84362a81faa70272436830ea01531a44b89df41c1a3c5c365f2d4cc67d19e5d501fe342ac4a0b3fe5ee9e1eb6cdf53da6b4326804180cbfe80b7efdb40e8cd33d6684e98bba74ae342e733cdd928c26d2cfe305fb0d9fcaf
vau-proxy-server           |       Message size (Bytes) : 198
vau-proxy-server           |       Complete header      : 020001000000000000000257fdea7f694801cec9a43711f4b73f431ec884864d5e60d66934a0364250f3bd
vau-proxy-server           |       Key to decrypt (K2_c2s_app_data): 2a1ae0426c9f27b422fd1273586753f5c2a773118147e4fa5fd58cad5b6dc18b
vau-proxy-server           |       -------------------------------
vau-proxy-server           |       Version  (1 Byte): 02
vau-proxy-server           |       PU       (1 Byte): 00
vau-proxy-server           |       Request  (1 Byte): 01
vau-proxy-server           |       Counter  (8 Byte): 0000000000000002
vau-proxy-server           |       KeyId   (32 Byte): 57fdea7f694801cec9a43711f4b73f431ec884864d5e60d66934a0364250f3bd
vau-proxy-server           |       IV      (12 Byte): 0cf094b90000000000000002
vau-proxy-server           |       CT + GMAC        : dabbecb5195ea0383a2b0d767469b07666b1900931b7af2ff68a62a903f3af27fc185bad4aefad3c2d1b272bce3cdb3cc122f8863aa19d84362a81faa70272436830ea01531a44b89df41c1a3c5c365f2d4cc67d19e5d501fe342ac4a0b3fe5ee9e1eb6cdf53da6b4326804180cbfe80b7efdb40e8cd33d6684e98bba74ae342e733cdd928c26d2cfe305fb0d9fcaf
vau-proxy-server           | 
vau-proxy-server           | 06:59:31.097 VPS ERROR VauException: Transcript Error: Exception thrown whilst trying to decrypt VAU message: Tag mismatch
vau-proxy-server           | de.gematik.vau.lib.exceptions.VauDecryptionException: Exception thrown whilst trying to decrypt VAU message: Tag mismatch
vau-proxy-server           |    at de.gematik.vau.lib.AbstractVauStateMachine.decryptVauMessage(AbstractVauStateMachine.java:181)
vau-proxy-server           |    at de.gematik.vau.server.VauServerController.decryptAndForwardRequest(VauServerController.java:126)
MikeKurtze commented 4 days ago

We have found the Problem and we will provide an update. The Problem is that the library AesGcm changes the initialization vector in function initializeIV() before it's used for encryption.

KillerCodeMonkey commented 4 days ago

@MikeKurtze i am not a crypto expert, but just for your information - if i remember it correctly an iv should not be reused especially when used with AesGCM.

So please make sure it will not introduce an attack opportunity or weakening the whole procedure.

MikeKurtze commented 4 days ago

Yes, this it's correct and the AesGCM creates an IV via random value. The Bug here is, that our impementation extends/change the IV accordingly to spezification, but in the header value of the encrypted message is placed the random value, not the used IV. Thats the reason, the decryption fails.

KillerCodeMonkey commented 4 days ago

ah okay. thanks for clarification :)

KillerCodeMonkey commented 3 days ago

@MikeKurtze in the latest release only the EncryptMessage method was changed, but i guess the DecryptMessage needs to be changed as well.

EDIT: Decryption seems to work when the request counter is correctly passed in AESGcm

KillerCodeMonkey commented 3 days ago

And in /crypto/AesGcm.cs initializeAes the lcounter parameter to initialize the iv is set to 1 as fixed value. so it seems the changes are not doing much?

In the AbstractState-Class the request counter as byte[] is concatenated to the random byte part... and in the initializiation it is sliced back to just the random byte part and the counter part is not used?

AbstractStateClass:

byte[] a = new byte[4];
new SecureRandom().NextBytes(a);
byte[] random = a.Concat(requestCounterBytes).ToArray();

AesGcm aesGcm = new AesGcm(random, header, encryptionVauKey);

AES Class

ivValue = initializeIV(random.Take(random.Length - 8).ToArray(), 1);

i guess the random part and the request counter part should be used as parameters for the initializeIV method call?

So instead of 1 pass the real counter, because those 8 byte would match the 64 Bit part of the iv :)

Something like this should work

ivValue = initializeIV(random.Take(random.Length - 8).ToArray(), BitConverter.ToInt64(random.Skip(random.Length - 8).Reverse().ToArray(), 0));
CEiderEVIDENT commented 21 hours ago

Version 1.0.2 still has the error, fix does not work

KillerCodeMonkey commented 21 hours ago

@CEiderEVIDENT check the PR i created. Adapt the changes and it should work

CEiderEVIDENT commented 21 hours ago

@KillerCodeMonkey : Your fix in PR#1 fiexs the error succesfully. Great job