Rantanen / node-dtls

JavaScript DTLS implementation for Node.js
ISC License
58 stars 15 forks source link

I am now implementing the 'CertificateVerify' function. i need your advise. #18

Open hissinger opened 6 years ago

hissinger commented 6 years ago

hi. first, thank you for node-dtls.

I am developing a call generator for performance testing of the webrtc media server. It is based on nodejs, and is using your node-dtls. Since the media server uses the CeritificateRequest, I had to add some functionality to the ClientHandShakeHandler. I have implemented Cerificate and CerificateVerify but I get the following error on the media server.

"error: 04091068: rsa routines: INT_RSA_VERIFY: bad signature"

The cause of this error seems to be a wrong signature calculation in CerificateVerify, but I do not know how to resolve it. I referred to rfc6347, rfc5246. and I have been calculating the signature with the following pseudocode

cyrpto.createSign('sha256WithRSAEncryption') cyrpto.sign(ClientHello + ServerHello + ServerCertificate + CertificateRequest + ServerHelloDone + ClientCertificate + ClientKeyExchange)

i would appreciate it if you could give me some advices to resolve this problem.

hissinger commented 6 years ago

My code link being developed. https://github.com/hissinger/node-dtls/tree/implement_cerificate_verify

Rantanen commented 6 years ago

Unfortunately crypto implementations are notoriously difficult to debug as they are designed to not give too much information out.

Looking at the flow graph on Page 35 of RFC 5246, it lists the following messages:

      ClientHello                  -------->
                                                      ServerHello
                                                     Certificate*
                                               ServerKeyExchange*
                                              CertificateRequest*
                                   <--------      ServerHelloDone
      Certificate*
      ClientKeyExchange
      CertificateVerify*

Comparing that to your list, it seems ServerKeyExchange is misisng - although it is an optional message and I don't remember anymore when it is used. Though if I remember right; It's always included during the initial handshake and it's only optional when doing a fast session resume that doesn't need a new key exchange.

Also the CertificateVerify packet seems a bit off; Although I couldn't confirm this since the file doesn't seem to exist in the repository.

+    var certificateVerify = new DtlsCertificateVerify({
+        signature: sign
+    });
+    
+    var certificateVerifyHandshake = this.handshakeBuilder.createHandshakes(
+        certificateVerify).getBuffer();

Based on RFC 5246, the CertificateVerify is:

      struct {
           digitally-signed struct {
               opaque handshake_messages[handshake_messages_length];
           }
      } CertificateVerify;

Wehre digitally-signed is:

      struct {
         SignatureAndHashAlgorithm algorithm;
         opaque signature<0..2^16-1>;
      } DigitallySigned;

Looking at the code, the algorithm seems to be missing at least - not sure if you are hard coding that in the DtlsCertificateVerify constructor though.

hissinger commented 6 years ago

First, ServerKeyExchange is not included in the list, because the message from the media server does not include ServerKeyExchange.

I also tried to put the SignatureAndHashAlgorithm value before the signature, but another error occurred.

'Handshake error: error: 14088109: SSL routines: ssl3_get_cert_verify: wrong signature size'

For a successful dtls example, I've also tested with the chrome browser. I grabbed the packet and checked it. It looks as if the first two bytes of the CertificateVerify signature are not SignatureAndHashAlgorithm values.

2018-06-26 11 53 10

According to rfc5246

      enum {
          (1), sha1 (2), sha224 (3), sha256 (4), sha384 (5)
          sha512 (6), (255)
      } HashAlgorithm;
    
      enum {anonymous (0), rsa (1), dsa (2), ecdsa (3), (255)}
        SignatureAlgorithm;
    
      struct {
            HashAlgorithm hash;
            SignatureAlgorithm signature;
      } SignatureAndHashAlgorithm;

The SignatureAndHashAlgorithm is assumed to be 2 bytes. However, in the chrome test results, the first two bytes are not visible as HashAlgorithm and SignatureAlgorithm values.

Rantanen commented 6 years ago

The Chrome seems to be using DTLS 1.0, which is based on TLS 1.1. The TLS 1.2 RFC states the following:

The MD5/SHA-1 combination in the digitally-signed element has been replaced with a single hash. Signed elements now include a field that explicitly specifies the hash algorithm used.

So I'd imagine Chrome does not carry the algorithm info, but given node-dtls specifies DTLS 1.2 int he handshake, it should.

Rantanen commented 6 years ago

Also I'd double check the DtlsCertificateVerify packet spec. The signature data needs to be in a variable length array, not just as bytes.

hissinger commented 6 years ago

The version of ClientHello used by node-dtls is DTLS 1.0. I did not modify the version. If I transfer ClientHello to DTLS 1.2, will this work?

2018-06-28 2 00 12

and, here is the capture capture screen of CertificateVerify of the call generator being developed.

2018-06-28 2 04 16

That part is a bit confusing, but the length of the actual signature is not a problem.

Rantanen commented 6 years ago

Sorry; My bad. I guess it's the server negotiating DTLS 1.0 here since also the node-dtls CertificateVerify displays 1.0 packet layer.

The client handshake handler has a comment regarding the handling of the version. https://github.com/hissinger/node-dtls/blob/master/ClientHandshakeHandler.js#L139-L143

If Chrome is really using DTLS 1.0, which is based on TLS 1.1, then the signature length makes no sense though. TLS 1.1 defines the signature as way shorter than the 72 bytes displayed in the capture:

 struct {
       select (SignatureAlgorithm) {
           case anonymous: struct { };
           case rsa:
               digitally-signed struct {
                   opaque md5_hash[16];
                   opaque sha_hash[20];
               };
           case dsa:
               digitally-signed struct {
                   opaque sha_hash[20];
               };
           };
       };
   } Signature;