diff --git a/CHANGELOG b/CHANGELOG index 528f37ac2..e1a01affc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +0.15.2 (TBD) +============ + +Changes: +- Use SHA256 fingerprints for TLS certificate trust instead of deprecated SHA1 + Improves security of certificate pinning and TOFU (Trust On First Use) +- Update TLS certificate UI messages to indicate SHA256 checksums +- Add comprehensive TLS security documentation to README + 0.15.1 (2025-08-22) =================== diff --git a/README.md b/README.md index 38cf3a714..82429ddff 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,94 @@ Feel free to follow us on [twitter](https://twitter.com/profanityim), join our [ ## Installation Our [user guide](https://profanity-im.github.io/userguide.html) contains an [install](https://profanity-im.github.io/guide/latest/install.html) section and a section for [building](https://profanity-im.github.io/guide/latest/build.html) from source yourself. +## Security Features + +### TLS Certificate Verification with SHA256 Fingerprints + +Profanity implements robust TLS certificate verification using **SHA256 fingerprints** for enhanced security. This addresses the deprecation of SHA1 and provides stronger protection against man-in-the-middle attacks. + +#### Key Features + +- **SHA256 Fingerprint Support**: Certificate fingerprints now use SHA256 hashing algorithm instead of deprecated SHA1 +- **Certificate Pinning**: Pin specific certificates by their SHA256 fingerprint to prevent MITM attacks +- **Trust On First Use (TOFU)**: Securely establish trust with servers on first connection +- **Manual Trust Management**: Full control over which certificates to trust +- **Backward Compatibility**: Automatically falls back to SHA1 for older libstrophe versions + +#### Why SHA256? + +SHA256 provides significant security improvements over SHA1: +- **Collision Resistant**: SHA1 has known collision vulnerabilities; SHA256 is cryptographically secure +- **Industry Standard**: SHA256 is the current standard for certificate fingerprinting (NIST SP 800-131A) +- **Future-Proof**: Provides long-term security for certificate verification +- **Compliance**: Meets modern security requirements and best practices + +#### TLS Commands + +``` +/tls cert Display current server's certificate with SHA256 fingerprint +/tls cert Show details of a trusted certificate by SHA256 fingerprint +/tls trust Add current certificate to manually trusted certificates +/tls trusted List all manually trusted certificates with SHA256 checksums +/tls revoke Remove a certificate from trusted list by SHA256 fingerprint +/tls allow Allow connection with current certificate (one-time) +/tls always Always allow connections with current certificate +/tls deny Abort connection with untrusted certificate +/tls certpath Show the trusted certificate path +/tls certpath set Specify filesystem path containing trusted certificates +/tls certpath clear Clear the trusted certificate path +/tls certpath default Use default system certificate path +``` + +#### Usage Example + +When connecting to a server with an untrusted certificate, Profanity will display: + +``` +TLS certificate verification failed: certificate not trusted +Certificate: + Subject: + Common name : xmpp.example.com + Issuer: + Common name : xmpp.example.com + Fingerprint (SHA256): A1:B2:C3:D4:E5:F6:07:08:09:0A:1B:2C:3D:4E:5F:60:71:82:93:A4:B5:C6:D7:E8:F9:0A:1B:2C:3D:4E:5F:60 + +Use '/tls allow' to accept this certificate. +Use '/tls always' to accept this certificate permanently. +Use '/tls deny' to reject this certificate. +``` + +To permanently trust this certificate: +``` +/tls always +``` + +To view all trusted certificates: +``` +/tls trusted +``` + +#### Implementation Details + +This feature is implemented across multiple components: + +- **Core**: `src/xmpp/connection.c` - Uses `XMPP_CERT_FINGERPRINT_SHA256` from libstrophe +- **Storage**: `src/config/tlscerts.c` - Stores trusted certificates with fingerprints +- **UI**: `src/ui/console.c` - Displays SHA256 fingerprints to users +- **Commands**: `src/command/cmd_funcs.c` - Implements TLS management commands + +#### Requirements + +- libstrophe >= 0.12.3 (provides SHA256 fingerprint support) +- OpenSSL or GnuTLS (for TLS support) + +#### Security Considerations + +- **Self-Signed Certificates**: SHA256 fingerprints allow secure use of self-signed certificates +- **Certificate Changes**: Users are alerted when a server's certificate changes +- **Persistent Trust**: Trusted certificates are stored locally for future connections +- **Manual Verification**: Users should verify fingerprints through a trusted channel before trusting + ## Donations We would highly appreciate it if you support us via [GitHub Sponsors](https://github.com/sponsors/jubalh/). Especially if you make feature requests or need help using Profanity. Sponsoring enables us to spend time on Profanity. diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index e33dcdb16..a16d202f9 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -199,16 +199,16 @@ static const struct cmd_t command_defs[] = { "/tls certpath clear", "/tls certpath default") CMD_DESC( - "Handle TLS certificates. ") + "Handle TLS certificates. Certificate fingerprints use SHA256 checksums for enhanced security.") CMD_ARGS( { "allow", "Allow connection to continue with TLS certificate." }, { "always", "Always allow connections with TLS certificate." }, { "deny", "Abort connection." }, - { "cert", "Show the current TLS certificate." }, - { "cert ", "Show details of trusted certificate." }, + { "cert", "Show the current TLS certificate with SHA256 fingerprint." }, + { "cert ", "Show details of trusted certificate by its SHA256 fingerprint." }, { "trust", "Add the current TLS certificate to manually trusted certificates." }, - { "trusted", "List summary of manually trusted certificates (with '/tls always' or '/tls trust')." }, - { "revoke ", "Remove a manually trusted certificate." }, + { "trusted", "List summary of manually trusted certificates with SHA256 checksums (with '/tls always' or '/tls trust')." }, + { "revoke ", "Remove a manually trusted certificate by its SHA256 fingerprint." }, { "certpath", "Show the trusted certificate path." }, { "certpath set ", "Specify filesystem path containing trusted certificates." }, { "certpath clear", "Clear the trusted certificate path." }, diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 7ebfa7d8c..686039249 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -292,11 +292,11 @@ cmd_tls_trust(ProfWin* window, const char* const command, gchar** args) } cafile_add(cert); if (tlscerts_exists(cert->fingerprint)) { - cons_show("Certificate %s already trusted.", cert->fingerprint); + cons_show("Certificate with SHA256 fingerprint %s already trusted.", cert->fingerprint); tlscerts_free(cert); return TRUE; } - cons_show("Adding %s to trusted certificates.", cert->fingerprint); + cons_show("Adding certificate with SHA256 fingerprint %s to trusted certificates.", cert->fingerprint); tlscerts_add(cert); tlscerts_free(cert); return TRUE; @@ -332,9 +332,9 @@ cmd_tls_revoke(ProfWin* window, const char* const command, gchar** args) } else { gboolean res = tlscerts_revoke(args[1]); if (res) { - cons_show("Trusted certificate revoked: %s", args[1]); + cons_show("Trusted certificate with SHA256 fingerprint revoked: %s", args[1]); } else { - cons_show("Could not find certificate: %s", args[1]); + cons_show("Could not find certificate with SHA256 fingerprint: %s", args[1]); } } return TRUE; diff --git a/src/ui/console.c b/src/ui/console.c index 82cf5ac69..c01b68632 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -178,9 +178,9 @@ cons_show_tlscert_summary(const TLSCertificate* cert) return; } - cons_show("Subject : %s", cert->subject_commonname); - cons_show("Issuer : %s", cert->issuer_commonname); - cons_show("Fingerprint : %s", cert->fingerprint); + cons_show("Subject : %s", cert->subject_commonname); + cons_show("Issuer : %s", cert->issuer_commonname); + cons_show("Fingerprint (SHA256): %s", cert->fingerprint); } void @@ -260,7 +260,7 @@ cons_show_tlscert(const TLSCertificate* cert) cons_show(" Start : %s", cert->notbefore); cons_show(" End : %s", cert->notafter); - cons_show(" Fingerprint : %s", cert->fingerprint); + cons_show(" Fingerprint (SHA256): %s", cert->fingerprint); } void diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index 1e2e1b327..691e5ffbf 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -1084,8 +1084,17 @@ _xmppcert_to_profcert(const xmpp_tlscert_t* xmpptlscert) { int version = (int)strtol( xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_VERSION), NULL, 10); + + // Use SHA256 fingerprint for better security (SHA1 is deprecated) + const char* fingerprint = xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_FINGERPRINT_SHA256); + + // Fallback to SHA1 if SHA256 is not available (for older libstrophe versions) + if (!fingerprint) { + fingerprint = xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_FINGERPRINT_SHA1); + } + return tlscerts_new( - xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_FINGERPRINT_SHA1), + fingerprint, version, xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_SERIALNUMBER), xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_SUBJECT),