adrienverge / openfortivpn

Client for PPP+TLS VPN tunnel services
GNU General Public License v3.0
2.71k stars 321 forks source link

TLSv1.3 and options --cipher-list and --seclevel-1 #687

Closed DimitriPapadopoulos closed 4 years ago

DimitriPapadopoulos commented 4 years ago

From the man page for SSL_CTX_set_cipher_list:

SSL_CTX_set_cipher_list() sets the list of available ciphers (TLSv1.2 and below) for ctx using the control string str. [...] SSL_CTX_set_ciphersuites() is used to configure the available TLSv1.3 ciphersuites for ctx.

I noticed openfortivpn only calls _SSL_set_cipherlist() so options --cipher-list and --seclevel-1 are only valid for TLSv1.2 and below. Perhaps this needs to be clarified in the documentation?

DimitriPapadopoulos commented 4 years ago

@martinetd Yes, I had noticed that _SSL_CTX_set_cipherlist() apply only to TLSv1.2 or lower.

Would a clarification in the docs be sufficient? In the long term I believe it would make sense to add a call to _SSL_CTX_setciphersuites() in addition to _SSL_CTX_set_cipherlist(). While the list of TLSv1.3 ciphers may look sensible today, we might want to disable some ciphers in the long term as a new attacks are disclosed and ciphers get obsolete.

martinetd commented 4 years ago

Having an option in the long term might make sense, for now I believe clarifying the documentation should be enough (e.g. adding a 'for TLS versions <= 1.2' or similar).

I'm not convinced adding the option before a usecase requires it is worth it but that's more a policy decision than anything else, leaving that choice up to you :)

DimitriPapadopoulos commented 4 years ago

Just to clarify, I wasn't thinking of changing command line options, just calling _SSL_CTX_setciphersuites() in addition to _SSL_CTX_set_cipherlist(), so that TLSv1.3 ciphers can be constrained the same way TLSv1.2 ciphers are constrained. That's really for the sake of correctness, not to support new use cases.

This would only apply only for:

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
#ifdef TLS1_3_VERSION
martinetd commented 4 years ago

Hm, not sure how reusing the same option would work.. the man page does say setting ciphers should never fail as resolving names is only done later on and unknown ciphers just ignored, so it ought to be possible, but if you give only TLSv1.3 ciphers you'll effectively disable TLSv1.2 (and same the other way around). Well if they come from openssl s_client as suggested in the help it would probably be better to set both even if more technical people might frown at the small abuse, if you've tested it works go for it?

DimitriPapadopoulos commented 4 years ago

indeed I was thinking of mixing ciphers for TLSv1.2 and below and TLSv1.3 in the same str argument. I'm really not knowledgeable in TLS but is TLSV1.3 really different from TLSv1.2 and lower? Aren't some ciphers specific to TLSv1.0 or TLSv1.2, the same way some are specific to TLSv1.3? Is there a more fundamental difference between TLSv1.2 and below and TLSv1.3?

martinetd commented 4 years ago

As far as I know it's really the same ciphers for SSLv3 + TLS1.0 to 1.2, and no common cipher suite with 1.3.

$ for prot in ssl3 tls1 tls1_1 tls1_2 tls1_3; do echo -n "$prot: "; openssl ciphers -s -$prot; done
ssl3: 
tls1: ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA
tls1_1: ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA
tls1_2: ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES128-GCM
-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES1
28-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES256-CCM:AES128-GCM-SHA256:AES128-CCM:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA
-CHACHA20-POLY1305:DHE-RSA-AES256-CCM:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-CCM:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA
tls1_3: TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256

(I assume I have nothing for ssl3 because my openssl no longer supports it, on an older openssl -ssl3 looks the same as tls1)

So using the same string really is abusing it a bit, but I'm curious how openssl would handle e.g. a server compatible with TLS1.3 with no common cipher (no cipher in list) but some cipher given for TLS1.2 -- would it fallback appropriately or error out because no cipher were given? I think whether we can do this or not depends on this test.

DimitriPapadopoulos commented 4 years ago

I have tried the same command on Ubuntu 16.04 and Ubuntu 20.04, I don't have access to earlier machines that would support ssl3. Not sure what the -s option is about, it doesn't work on Ubuntu 16.04.

Ubuntu 16.04

$ for prot in tls1 tls1_1 tls1_2 tls1_3; do echo -n "$prot: "; openssl ciphers -$prot; done
tls1: ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:SRP-AES-256-CBC-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:PSK-AES256-CBC-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:SRP-DSS-AES-128-CBC-SHA:SRP-RSA-AES-128-CBC-SHA:SRP-AES-128-CBC-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:SEED-SHA:CAMELLIA128-SHA:PSK-AES128-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:PSK-RC4-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:SRP-DSS-3DES-EDE-CBC-SHA:SRP-RSA-3DES-EDE-CBC-SHA:SRP-3DES-EDE-CBC-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:PSK-3DES-EDE-CBC-SHA
tls1_1: Error in cipher list
140287804364440:error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match:ssl_lib.c:1380:
tls1_2: Error in cipher list
140104451139224:error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match:ssl_lib.c:1380:
tls1_3: Error in cipher list
140693329577624:error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match:ssl_lib.c:1380:
$ 

Ubuntu 20.04

$ for prot in tls1 tls1_1 tls1_2 tls1_3; do echo -n "$prot: "; openssl ciphers -s -$prot; done
tls1: 
tls1_1: 
tls1_2: ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA
tls1_3: TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
$ 
martinetd commented 4 years ago

-s lists supported ciphers for the given protocol, not sure what openssl ciphers -tlsxyz does without -s... Looks like ubuntu 20.04 is more strict than my machine about using tls>=1.2 :)

Anyway, point is there are no common ciphers so if we give a string to the ciphersuite function without any TLS1.3 cipher we need to test how openssl reacts. From openssl s_client it might not be great...

$ openssl s_client -cipher ECDHE-RSA-AES256-GCM-SHA384 -ciphersuites ECDHE-RSA-AES256-GCM-SHA384 -connect <server that accepts tls1.2 with that cipher>:443
Error with command: "-ciphersuites ECDHE-RSA-AES256-GCM-SHA384"
140433478293312:error:1426E0B9:SSL routines:ciphersuite_cb:no cipher match:ssl/ssl_ciph.c:1301:
DimitriPapadopoulos commented 4 years ago

I'm not sure whether our test FortiGate appliance is compatible with TLS1.3:

$ openssl s_client -connect xxxxx.xxxx.xx:443 -tls1_3
[...]
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
[...]
$ 

On the other hand:

$ nmap --script ssl-enum-ciphers -p 443 xxxxx.xxxx.xx
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-04 14:47 CEST
Nmap scan report for xxxxx.xxxx.xx (xxx.xxx.xxx.xxx)
Host is up (0.042s latency).

PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers: 
|   TLSv1.1: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 2048) - A
|       TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 2048) - A
|       TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (secp384r1) - A
|       TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (dh 2048) - A
|       TLS_DHE_RSA_WITH_AES_256_CCM_8 (dh 2048) - A
|       TLS_DHE_RSA_WITH_AES_256_CCM (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 (secp384r1) - A
|       TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 (secp384r1) - A
|       TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 2048) - A
|       TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 2048) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CCM_8 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CCM (rsa 2048) - A
|       TLS_RSA_WITH_ARIA_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (dh 2048) - A
|       TLS_DHE_RSA_WITH_AES_128_CCM_8 (dh 2048) - A
|       TLS_DHE_RSA_WITH_AES_128_CCM (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 (secp384r1) - A
|       TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 (secp384r1) - A
|       TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 (dh 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp384r1) - A
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 2048) - A
|       TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 2048) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CCM_8 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CCM (rsa 2048) - A
|       TLS_RSA_WITH_ARIA_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|_  least strength: A

Nmap done: 1 IP address (1 host up) scanned in 17.35 seconds
$ 

EDIT: The Nmap script does not support TLSv1.3: https://github.com/nmap/nmap/issues/1348

martinetd commented 4 years ago

ssl-enum-ciphers nmap script doesn't do TLSv1.3 on my system, see /usr/share/nmap/scripts/ssl-enum-ciphers.nse -- it looks like it's basically looping through available ciphers and tls version explicitely with the server and checking if server accepts the handshake or not. If openssl s_client is happy with TLS1.3 then the server does support it.

Anyway, even if the server doesn't support TLSv1.3, the last command I gave should work -- by giving no ciphersuites I would have expected tls1.3 init to fail and fallback to 1.2

DimitriPapadopoulos commented 4 years ago

Indeed the combination of an appliance capable of TLSv1.3 with option -ciphersuites with TLSv1.2 ciphers only is fatal:

$ openssl s_client -cipher ECDHE-RSA-AES256-GCM-SHA384 -ciphersuites ECDHE-RSA-AES256-GCM-SHA384 -connect xxxxx.xxxx.xx:443
Error with command: "-ciphersuites ECDHE-RSA-AES256-GCM-SHA384"
139836601992512:error:1426E0B9:SSL routines:ciphersuite_cb:no cipher match:../ssl/ssl_ciph.c:1294:
$ 
$ openssl s_client -tls1_3 -ciphersuites ECDHE-RSA-AES256-GCM-SHA384 -connect xxxxx.xxxx.xx:443
Error with command: "-ciphersuites ECDHE-RSA-AES256-GCM-SHA384"
140655254193472:error:1426E0B9:SSL routines:ciphersuite_cb:no cipher match:../ssl/ssl_ciph.c:1294:
$ 
$  openssl s_client -ciphersuites ECDHE-RSA-AES256-GCM-SHA384 -connect xxxxx.xxxx.xx:443
Error with command: "-ciphersuites ECDHE-RSA-AES256-GCM-SHA384"
139825685968192:error:1426E0B9:SSL routines:ciphersuite_cb:no cipher match:../ssl/ssl_ciph.c:1294:
$ 
$ openssl s_client -tls1_3 -cipher ECDHE-RSA-AES256-GCM-SHA384 xxxxx.xxxx.xx:443
[...]
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
[...]
$ 
$ openssl s_client -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384 xxxxx.xxxx.xx:443
[...]
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
[...]
$ 

That said this doesn't surprise me, as the user:

What's really bad is that even a combination of TLSv1.2/TLSv1.3 ciphers fails:

$ openssl s_client -cipher 'ECDHE-RSA-AES256-GCM-SHA384:TLS_AES_256_GCM_SHA384' -ciphersuites 'ECDHE-RSA-AES256-GCM-SHA384:TLS_AES_256_GCM_SHA384' xxxxx.xxxx.xx:443
Error with command: "-ciphersuites ECDHE-RSA-AES256-GCM-SHA384:TLS_AES_256_GCM_SHA384"
140272218875200:error:1426E0B9:SSL routines:ciphersuite_cb:no cipher match:../ssl/ssl_ciph.c:1294:
$ 

This is counter-intuitive as the S_CLIENT(1) man page states that the lists of TLSv1.2 and TLSv1.3 ciphers will be "combined" anyway:

-cipher cipherlist This allows the TLSv1.2 and below cipher list sent by the client to be modified. This list will be combined with any TLSv1.3 ciphersuites that have been configured. Although the server determines which ciphersuite is used it should take the first supported cipher in the list sent by the client. See the ciphers command for more information.

-ciphersuites val This allows the TLSv1.3 ciphersuites sent by the client to be modified. This list will be combined with any TLSv1.2 and below ciphersuites that have been configured. Although the server determines which cipher suite is used it should take the first supported cipher in the list sent by the client. See the ciphers command for more information. The format for this list is a simple colon (":") separated list of TLSv1.3 ciphersuite names.

This doesn't make any sense to me. I think issue https://github.com/openssl/openssl/issues/9335 is related - cannot pass unknown ciphers to _SSL_CTX_setciphersuites().

DimitriPapadopoulos commented 4 years ago

OK, according to this https://github.com/openssl/openssl/issues/10936#issuecomment-578060278, TLSv1.2 and TLSv1.3 ciphers cannot be combined:

TLSv1.2 (and below) ciphersuites are handled separately to TLSv1.3 ciphersuites. The two are incompatible with each other (you can't use a TLSv1.2 ciphersuite in TLSv1.3 and vice versa).

This looks like a documentation bug, I've opened a documentation issue (https://github.com/openssl/openssl/issues/11724).

As far as openfortivpn is concerned, in the short term we can only update our own documentation and explain --cipher-list and --seclevel-1 can be used for TLSv1.2 and lower only.

mrbaseman commented 4 years ago

FortiOS 6.0.x supports TLS 1.1 and TLS 1.2 FortiOS 6.2.x also supports TLS 1.3, but it requires newer hardware. The D Series models are not supported anymore in 6.2.x. I believe E Series is the minimum. I have a 6.2 running on a FG 60 F. FortiOS 6.4 has been launced recently, but I haven't seen it live yet.