mirror of
				https://github.com/drwetter/testssl.sh.git
				synced 2025-10-30 21:35:26 +01:00 
			
		
		
		
	Support EdDSA
This commit adds support for EdDSA (Ed25519 and Ed448). In particular: * It modifies prepare_tls_clienthello() to include Ed25519 and Ed448 in the signature_algorithms extension of the TLS 1.2 and earlier ClientHello (RFC 8422). * It modifies run_server_defaults() and get_server_certificate() to check whether the server offers EdDSA certificates with TLS 1.3. * It modifies certificate_info() to handle certificates signed with EdDSA or with EdDSA public keys, even if $OPENSSL does not support pretty printing such keys and signatures. * It modifies read_sigalg_from_file() to recognize EdDSA signatures even if $OPENSSL does not.
This commit is contained in:
		| @@ -16,6 +16,7 @@ | ||||
| * Don't use external pwd anymore | ||||
| * STARTTLS: XMPP server support | ||||
| * Rating (SSL Labs, not complete) | ||||
| * Added support for certificates with EdDSA signatures and pubilc keys | ||||
|  | ||||
| ### Features implemented / improvements in 3.0 | ||||
|  | ||||
|   | ||||
							
								
								
									
										70
									
								
								testssl.sh
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								testssl.sh
									
									
									
									
									
								
							| @@ -1040,7 +1040,7 @@ set_key_str_score() { | ||||
| 
 | ||||
|      # TODO: We need to get the size of DH params (follows the same table as the "else" clause) | ||||
|      # For now, verifying the key size will do... | ||||
|      if [[ $type == EC ]]; then | ||||
|      if [[ $type == EC || $type == EdDSA ]]; then | ||||
|           if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then | ||||
|                let KEY_EXCH_SCORE=20 | ||||
|                set_grade_cap "F" "Using an insecure key" | ||||
| @@ -6249,7 +6249,15 @@ read_dhtype_from_file() { | ||||
| 
 | ||||
| # arg1: certificate file | ||||
| read_sigalg_from_file() { | ||||
|      $OPENSSL x509 -noout -text -in "$1" 2>/dev/null | awk -F':' '/Signature Algorithm/ { print $2; exit; }' | ||||
|      local sig_alg | ||||
| 
 | ||||
|      sig_alg="$(strip_leading_space "$($OPENSSL x509 -noout -text -in "$1" 2>/dev/null | awk -F':' '/Signature Algorithm/ { print $2; exit; }')")" | ||||
|      case "$sig_alg" in | ||||
|           1.3.101.112|ED25519) tm_out "Ed25519" ;; | ||||
|           1.3.101.113|ED448)   tm_out "Ed448" ;; | ||||
|           *)                   tm_out "$sig_alg" ;; | ||||
|      esac | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @@ -7547,7 +7555,7 @@ get_server_certificate() { | ||||
|      CERTIFICATE_LIST_ORDERING_PROBLEM=false | ||||
|      if [[ "$1" =~ "tls1_3" ]]; then | ||||
|           [[ $(has_server_protocol "tls1_3") -eq 1 ]] && return 1 | ||||
|           if "$HAS_TLS13" && "$HAS_SIGALGS"; then | ||||
|           if "$HAS_TLS13" && "$HAS_SIGALGS" && [[ ! "$1" =~ "tls1_3_EdDSA" ]]; then | ||||
|                if [[ "$1" =~ "tls1_3_RSA" ]]; then | ||||
|                     $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -showcerts -connect $NODEIP:$PORT $PROXY $SNI -tls1_3 -tlsextdebug -status -msg -sigalgs PSS+SHA256:PSS+SHA384") </dev/null 2>$ERRFILE >$TMPFILE | ||||
|                elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then | ||||
| @@ -7568,6 +7576,8 @@ get_server_certificate() { | ||||
|                     tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,10,00,0e,08,04,08,05,08,06,04,01,05,01,06,01,02,01" | ||||
|                elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then | ||||
|                     tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,0a,00,08,04,03,05,03,06,03,02,03" | ||||
|                elif [[ "$1" =~ "tls1_3_EdDSA" ]]; then | ||||
|                     tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,06,00,04,08,07,08,08" | ||||
|                else | ||||
|                     return 1 | ||||
|                fi | ||||
| @@ -8332,8 +8342,16 @@ certificate_info() { | ||||
|      GOOD_CA_BUNDLE="" | ||||
|      cert_sig_algo="$(awk -F':' '/Signature Algorithm/ { print $2; if (++Match >= 1) exit; }' <<< "$cert_txt")" | ||||
|      cert_sig_algo="${cert_sig_algo// /}" | ||||
|      case "$cert_sig_algo" in | ||||
|           1.3.101.112|ED25519) cert_sig_algo="Ed25519" ;; | ||||
|           1.3.101.113|ED448)   cert_sig_algo="Ed448" ;; | ||||
|      esac | ||||
|      cert_key_algo="$(awk -F':' '/Public Key Algorithm:/ { print $2; if (++Match >= 1) exit; }' <<< "$cert_txt")" | ||||
|      cert_key_algo="${cert_key_algo// /}" | ||||
|      case "$cert_key_algo" in | ||||
|           1.3.101.112|E[Dd]25519) cert_key_algo="Ed25519"; cert_keysize=253 ;; | ||||
|           1.3.101.113|E[Dd]448)   cert_key_algo="Ed448"; cert_keysize=456 ;; | ||||
|      esac | ||||
| 
 | ||||
|      out "$indent" ; pr_bold " Signature Algorithm          " | ||||
|      jsonID="cert_signatureAlgorithm" | ||||
| @@ -8441,6 +8459,10 @@ certificate_info() { | ||||
|                fileout "${jsonID}${json_postfix}" "CRITICAL" "MD5" | ||||
|                set_grade_cap "F" "Supports a insecure signature (MD5)" | ||||
|                ;; | ||||
|           Ed25519|Ed448) | ||||
|                prln_svrty_good "$cert_sig_algo" | ||||
|                fileout "${jsonID}${json_postfix}" "OK" "$cert_sig_algo" | ||||
|                ;; | ||||
|           *) | ||||
|                out "$cert_sig_algo (" | ||||
|                pr_warning "FIXME: can't tell whether this is good or not" | ||||
| @@ -8461,6 +8483,7 @@ certificate_info() { | ||||
|           case $cert_key_algo in | ||||
|                *RSA*|*rsa*)             short_keyAlgo="RSA";; | ||||
|                *ecdsa*|*ecPublicKey)    short_keyAlgo="EC";; | ||||
|                *Ed25519*|*Ed448*)       short_keyAlgo="EdDSA";; | ||||
|                *DSA*|*dsa*)             short_keyAlgo="DSA";; | ||||
|                *GOST*|*gost*)           short_keyAlgo="GOST";; | ||||
|                *dh*|*DH*)               short_keyAlgo="DH" ;; | ||||
| @@ -8523,6 +8546,10 @@ certificate_info() { | ||||
|                     ((ret++)) | ||||
|                fi | ||||
| 
 | ||||
|                set_key_str_score "$short_keyAlgo" "$cert_keysize" | ||||
|           elif [[ $cert_key_algo == Ed* ]]; then | ||||
|                pr_svrty_good "$cert_key_algo" | ||||
|                json_rating="OK"; json_msg="$short_keyAlgo $cert_key_algo" | ||||
|                set_key_str_score "$short_keyAlgo" "$cert_keysize" | ||||
|           else | ||||
|                out "$cert_key_algo + $cert_keysize bits (" | ||||
| @@ -8586,7 +8613,7 @@ certificate_info() { | ||||
|      cert_keyusage="$(strip_leading_space "$(awk '/X509v3 Key Usage:/ { getline; print $0 }' <<< "$cert_txt")")" | ||||
|      if [[ -n "$cert_keyusage" ]]; then | ||||
|           outln "$cert_keyusage" | ||||
|           if ( [[ " $cert_type " =~ " RSASig " ]] || [[ " $cert_type " =~ " DSA " ]] || [[ " $cert_type " =~ " ECDSA " ]] ) && \ | ||||
|           if ( [[ " $cert_type " =~ " RSASig " ]] || [[ " $cert_type " =~ " DSA " ]] || [[ " $cert_type " =~ " ECDSA " ]] || [[ " $cert_type " =~ " EdDSA " ]] ) && \ | ||||
|              [[ ! "$cert_keyusage" =~ "Digital Signature" ]]; then | ||||
|                prln_svrty_high "$indent                              Certificate incorrectly used for digital signatures" | ||||
|                fileout "${jsonID}${json_postfix}" "HIGH" "Certificate incorrectly used for digital signatures: \"$cert_keyusage\"" | ||||
| @@ -9257,27 +9284,28 @@ run_server_defaults() { | ||||
|      ciphers_to_test[7]="" | ||||
|      ciphers_to_test[8]="tls1_3_RSA" | ||||
|      ciphers_to_test[9]="tls1_3_ECDSA" | ||||
|      ciphers_to_test[10]="tls1_3_EdDSA" | ||||
|      certificate_type[1]="" ; certificate_type[2]="" | ||||
|      certificate_type[3]=""; certificate_type[4]="" | ||||
|      certificate_type[5]="" ; certificate_type[6]="" | ||||
|      certificate_type[7]="" ; certificate_type[8]="RSASig" | ||||
|      certificate_type[9]="ECDSA" | ||||
|      certificate_type[9]="ECDSA" ; certificate_type[10]="EdDSA" | ||||
| 
 | ||||
|      for (( n=1; n <= 16 ; n++ )); do | ||||
|      for (( n=1; n <= 17 ; n++ )); do | ||||
|           # Some servers use a different certificate if the ClientHello | ||||
|           # specifies TLSv1.1 and doesn't include a server name extension. | ||||
|           # So, for each public key type for which a certificate was found, | ||||
|           # try again, but only with TLSv1.1 and without SNI. | ||||
|           if [[ $n -ne 1 ]] && [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then | ||||
|                ciphers_to_test[n]="" | ||||
|           elif [[ $n -ge 10 ]]; then | ||||
|           elif [[ $n -ge 11 ]]; then | ||||
|                ciphers_to_test[n]="" | ||||
|                [[ ${success[n-9]} -eq 0 ]] && [[ $(has_server_protocol "tls1_1") -ne 1 ]] && \ | ||||
|                     ciphers_to_test[n]="${ciphers_to_test[n-9]}" && certificate_type[n]="${certificate_type[n-9]}" | ||||
|                [[ ${success[n-10]} -eq 0 ]] && [[ $(has_server_protocol "tls1_1") -ne 1 ]] && \ | ||||
|                     ciphers_to_test[n]="${ciphers_to_test[n-10]}" && certificate_type[n]="${certificate_type[n-10]}" | ||||
|           fi | ||||
| 
 | ||||
|           if [[ -n "${ciphers_to_test[n]}" ]]; then | ||||
|                if [[ $n -ge 10 ]]; then | ||||
|                if [[ $n -ge 11 ]]; then | ||||
|                     sni="$SNI" | ||||
|                     SNI="" | ||||
|                     get_server_certificate "${ciphers_to_test[n]}" "tls1_1" | ||||
| @@ -9288,7 +9316,7 @@ run_server_defaults() { | ||||
|                     success[n]=$? | ||||
|                fi | ||||
|                if [[ ${success[n]} -eq 0 ]] && [[ -s "$HOSTCERT" ]]; then | ||||
|                     [[ $n -ge 10 ]] && [[ ! -e $HOSTCERT.nosni ]] && cp $HOSTCERT $HOSTCERT.nosni | ||||
|                     [[ $n -ge 11 ]] && [[ ! -e $HOSTCERT.nosni ]] && cp $HOSTCERT $HOSTCERT.nosni | ||||
|                     cp "$TEMPDIR/$NODEIP.get_server_certificate.txt" $TMPFILE | ||||
|                     >$ERRFILE | ||||
|                     if [[ -z "$sessticket_lifetime_hint" ]]; then | ||||
| @@ -9370,7 +9398,7 @@ run_server_defaults() { | ||||
|                          fi | ||||
|                          i=$((i + 1)) | ||||
|                     done | ||||
|                     if ! "$match_found" && [[ $n -ge 10 ]] && [[ $certs_found -ne 0 ]]; then | ||||
|                     if ! "$match_found" && [[ $n -ge 11 ]] && [[ $certs_found -ne 0 ]]; then | ||||
|                          # A new certificate was found using TLSv1.1 without SNI. | ||||
|                          # Check to see if the new certificate should be displayed. | ||||
|                          # It should be displayed if it is either a match for the | ||||
| @@ -9427,7 +9455,7 @@ run_server_defaults() { | ||||
|                          [[ -n "${previous_intermediates[certs_found]}" ]] && [[ -r $TEMPDIR/hostcert_issuer.pem ]] && \ | ||||
|                               previous_hostcert_issuer[certs_found]=$(cat $TEMPDIR/hostcert_issuer.pem) | ||||
|                          previous_ordering_problem[certs_found]=$CERTIFICATE_LIST_ORDERING_PROBLEM | ||||
|                          [[ $n -ge 10 ]] && sni_used[certs_found]="" || sni_used[certs_found]="$SNI" | ||||
|                          [[ $n -ge 11 ]] && sni_used[certs_found]="" || sni_used[certs_found]="$SNI" | ||||
|                          tls_version[certs_found]="$DETECTED_TLS_VERSION" | ||||
|                          previous_hostcert_type[certs_found]=" ${certificate_type[n]}" | ||||
|                          if [[ $DEBUG -ge 1 ]]; then | ||||
| @@ -10738,7 +10766,15 @@ get_pub_key_size() { | ||||
|      "$HAS_PKEY" || return 1 | ||||
| 
 | ||||
|      # OpenSSL displays the number of bits for RSA and ECC | ||||
|      pubkeybits=$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | $OPENSSL pkey -pubin -text 2>>$ERRFILE | awk -F'(' '/Public-Key/ { print $2 }') | ||||
|      pubkeybits=$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | $OPENSSL pkey -pubin -text 2>>$ERRFILE) | ||||
|      if [[ "$pubkeybits" =~ E[Dd]25519 ]]; then | ||||
|           echo "Server public key is 253 bit" >> $TMPFILE | ||||
|           return 0 | ||||
|      elif [[ "$pubkeybits" =~ E[Dd]448 ]]; then | ||||
|           echo "Server public key is 456 bit" >> $TMPFILE | ||||
|           return 0 | ||||
|      fi | ||||
|      pubkeybits=$(awk -F'(' '/Public-Key/ { print $2 }' <<< "$pubkeybits") | ||||
|      if [[ -n $pubkeybits ]]; then | ||||
|           # remainder e.g. "256 bit)" | ||||
|           pubkeybits="${pubkeybits//\)/}" | ||||
| @@ -14279,10 +14315,10 @@ prepare_tls_clienthello() { | ||||
| 
 | ||||
|           if [[ 0x$tls_low_byte -le 0x03 ]]; then | ||||
|                extension_signature_algorithms=" | ||||
|                00, 0d,                    # Type: signature_algorithms , see RFC 5246 | ||||
|                00, 20, 00,1e,             # lengths | ||||
|                00, 0d,                    # Type: signature_algorithms , see RFC 5246 and RFC 8422 | ||||
|                00, 24, 00,22,             # lengths | ||||
|                06,01, 06,02, 06,03, 05,01, 05,02, 05,03, 04,01, 04,02, 04,03, | ||||
|                03,01, 03,02, 03,03, 02,01, 02,02, 02,03" | ||||
|                03,01, 03,02, 03,03, 02,01, 02,02, 02,03, 08,07, 08,08" | ||||
|           else | ||||
|                extension_signature_algorithms=" | ||||
|                00, 0d,                    # Type: signature_algorithms , see RFC 8446 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 David Cooper
					David Cooper