Package: libjwt / 1.10.2-1+deb12u1

CVE-2024-25189-1.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
commit f73bac57c5bece16ac24f1a70022aa34355fc1bf
Author: Ben Collins <bcollins@maclara-llc.com>
Date:   Fri Feb 9 09:03:35 2024 -0500

    Implement a safer strcmp() function
    
    As noted, the strcmp() function can be used for time-based side attacks.
    
    I tried to test this and could not find a reasonable way to implement
    this attack for several reasons:
    
    1) strcmp() is optimized to compare 4 and 8 bytes at a time when possible
       on almost every modern system, making the attack almost impossible.
    2) Running 128 million iterations of strcmp() for a single byte attack
       gave sub-nanosecond average differences (locally on same excution stack)
       and almost as often as the comparison was correct, it was also wrong in
       the reverse sense (i.e. two byte strcmp() took less time than single
       byte).
    3) Adding noise from network, application stack, web server, etc. would
       only add to the failure rate of guessing the differences above.
    
    Erwan noted that there are proofs out there showing that signal noise
    reduction can make this guessing more "accurate", but this proof also
    noted it would take up to 4 billion guesses to completely cover this
    attack surface. The claim was that 50k attempts per second would break
    a 256-bit hmac in 22 hours. While this isn't impossible, it's very
    implausible.
    
    However, for the sake of cryptographic correctness, I implemented
    jwt_strcmp() which always compares all bytes, and does so up to the
    longest string in the 2-string set, without passing string boundaries.
    
    This makes it time-consistent for len(max(a,b)) comparisons. I proofed
    this using a 128 million interation average for various scenarious.
    
    Reported-by: Erwan Legrand <moi@erwanlegrand.com>
    Signed-off-by: Ben Collins <bcollins@maclara-llc.com>

Index: libjwt-1.10.2/libjwt/jwt-gnutls.c
===================================================================
--- libjwt-1.10.2.orig/libjwt/jwt-gnutls.c	2024-02-19 22:38:58.575655983 +0100
+++ libjwt-1.10.2/libjwt/jwt-gnutls.c	2024-02-19 22:38:58.571655984 +0100
@@ -90,7 +90,7 @@
 		jwt_Base64encode(buf, sig_check, len);
 		jwt_base64uri_encode(buf);
 
-		if (!strcmp(sig, buf))
+		if (!jwt_strcmp(sig, buf))
 			ret = 0;
 
 		free(sig_check);
Index: libjwt-1.10.2/libjwt/jwt-openssl.c
===================================================================
--- libjwt-1.10.2.orig/libjwt/jwt-openssl.c	2024-02-19 22:38:58.575655983 +0100
+++ libjwt-1.10.2/libjwt/jwt-openssl.c	2024-02-19 22:38:58.571655984 +0100
@@ -140,7 +140,7 @@
 	jwt_base64uri_encode(buf);
 
 	/* And now... */
-	ret = strcmp(buf, sig) ? EINVAL : 0;
+	ret = jwt_strcmp(buf, sig) ? EINVAL : 0;
 
 jwt_verify_hmac_done:
 	BIO_free_all(b64);
Index: libjwt-1.10.2/libjwt/jwt-private.h
===================================================================
--- libjwt-1.10.2.orig/libjwt/jwt-private.h	2024-02-19 22:38:58.575655983 +0100
+++ libjwt-1.10.2/libjwt/jwt-private.h	2024-02-19 22:41:41.667637551 +0100
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015-2017 Ben Collins <ben@cyphre.com>
+/* Copyright (C) 2015-2024 Ben Collins <ben@cyphre.com>
    This file is part of the JWT C Library
 
    This Source Code Form is subject to the terms of the Mozilla Public
@@ -36,4 +36,7 @@
 
 int jwt_verify_sha_pem(jwt_t *jwt, const char *head, const char *sig_b64);
 
+/* A time-safe strcmp function */
+int jwt_strcmp(const char *str1, const char *str2);
+
 #endif /* JWT_PRIVATE_H */
Index: libjwt-1.10.2/libjwt/jwt.c
===================================================================
--- libjwt-1.10.2.orig/libjwt/jwt.c	2024-02-19 22:38:58.575655983 +0100
+++ libjwt-1.10.2/libjwt/jwt.c	2024-02-19 22:44:53.223612621 +0100
@@ -1,4 +1,4 @@
-/* Copyright (C) 2015-2018 Ben Collins <ben@cyphre.com>
+/* Copyright (C) 2015-2024 Ben Collins <ben@cyphre.com>
    This file is part of the JWT C Library
 
    This Source Code Form is subject to the terms of the Mozilla Public
@@ -16,6 +16,37 @@
 #include "jwt-private.h"
 #include "config.h"
 
+/* A time-safe strcmp function */
+int jwt_strcmp(const char *str1, const char *str2)
+{
+       /* Get the LONGEST length */
+       int len1 = strlen(str1);
+       int len2 = strlen(str2);
+       int len_max = len1 >= len2 ? len1 : len2;
+
+       int i, ret = 0;
+
+       /* Iterate the entire longest string no matter what. Only testing
+        * the shortest string would still allow attacks for
+        * "a" == "aKJSDHkjashaaHJASJ", adding a character each time one
+        * is found. */
+       for (i = 0; i < len_max; i++) {
+               char c1, c2;
+
+               c1 = len1 < i ? str1[i] : '\0';
+               c2 = len2 < i ? str2[i] : '\0';
+
+               if (c1 != c2)
+                       ret = 1;
+       }
+
+       /* Don't forget to check length */
+       if (len1 != len2)
+               ret = -1;
+
+       return ret;
+}
+
 int jwt_Base64encode(char *coded_dst, const char *plain_src, int len_plain_src)
 {
   base64_encodestate _state;