*** This bug is a security vulnerability ***

Public security bug reported:

This issue was reported to the Security team over email and originally
posted to https://github.com/weidai11/cryptopp/issues/1269

> I typically never use Crypto++, but I had to yesterday, and I then 
> experienced a strange behavior that I felt I had to somehow report. Having 
> read your [security 
> policy](https://github.com/weidai11/cryptopp/security/policy), I decided that 
> the appropriate course of action was to open an issue here.
>
> ### Background
> 
> I used the default Crypto++ package provided by [Ubuntu 20.04.6 LTS (Focal 
> Fossa)](https://releases.ubuntu.com/focal/) running on a computer with a 
> 64-bit Intel CPU.
> 
> More specifically, Crypto++ was installed on the machine via `apt` as follows:
> 
> ```
> $ sudo apt update && sudo apt upgrade
> (..)
> $ sudo apt install libcrypto++-dev 
> (..)
> libcrypto++-dev is already the newest version (5.6.4-9build1).
> ```
> 
> The package version 5.6.4 leads me to think that it installs the (old) v5.6.4 
> release of Crypto++ from [this GitHub 
> repository](https://github.com/weidai11/cryptopp), although it is not 
> entirely clear from the metadata for the package.
> ### The issue
> 
> When using Crypto++ as provided by the above package, it seems 
> `ECP::ScalarMultiply()` may miscompute. Specifically, it seems to miscompute 
> if the scalar is on [2, 32), i.e. of bit length less than or equal to 5. This 
> would appear to be related to the difference in behavior induced by the 
> branching on [this 
> line](https://github.com/weidai11/cryptopp/blob/782057f5f18fbdad2bd2b291fb1ec558a8ab8225/ecp.cpp#L387)
>  in the source code for Crypto++.
> 
> To exemplify, I obtain the below result:
> 
> ```
> Q1.x = 
> 33306590390930540189669946118275349837741820479536661896440526521039379673897.
> Q1.y = 
> 51671163428562425671907826722938384860953039014408454870632045822359784767650.
> 
> >> Q1 is *NOT* as expected.
> >> Q1 is *NOT* on E.
> 
> Q2.x = 
> 33898744863829483362161709717034397769364896634277352921440311777960767108802.
> Q2.y = 
> 23483645583050324501141112153509270605088748325709409281081826839369927198174.
> 
> >> Q2 is as expected.
> >> Q2 is on E.
> 
> >> T1 is equal to T2 for d = 1.
> >> T1 is *NOT* equal to T2 for d = 2.
> >> T1 is *NOT* equal to T2 for d = 3.
> >> T1 is *NOT* equal to T2 for d = 4.
> >> T1 is *NOT* equal to T2 for d = 5.
> >> T1 is *NOT* equal to T2 for d = 6.
> >> T1 is *NOT* equal to T2 for d = 7.
> >> T1 is *NOT* equal to T2 for d = 8.
> >> T1 is *NOT* equal to T2 for d = 9.
> >> T1 is *NOT* equal to T2 for d = 10.
> >> T1 is *NOT* equal to T2 for d = 11.
> >> T1 is *NOT* equal to T2 for d = 12.
> >> T1 is *NOT* equal to T2 for d = 13.
> >> T1 is *NOT* equal to T2 for d = 14.
> >> T1 is *NOT* equal to T2 for d = 15.
> >> T1 is *NOT* equal to T2 for d = 16.
> >> T1 is *NOT* equal to T2 for d = 17.
> >> T1 is *NOT* equal to T2 for d = 18.
> >> T1 is *NOT* equal to T2 for d = 19.
> >> T1 is *NOT* equal to T2 for d = 20.
> >> T1 is *NOT* equal to T2 for d = 21.
> >> T1 is *NOT* equal to T2 for d = 22.
> >> T1 is *NOT* equal to T2 for d = 23.
> >> T1 is *NOT* equal to T2 for d = 24.
> >> T1 is *NOT* equal to T2 for d = 25.
> >> T1 is *NOT* equal to T2 for d = 26.
> >> T1 is *NOT* equal to T2 for d = 27.
> >> T1 is *NOT* equal to T2 for d = 28.
> >> T1 is *NOT* equal to T2 for d = 29.
> >> T1 is *NOT* equal to T2 for d = 30.
> >> T1 is *NOT* equal to T2 for d = 31.
> >> T1 is equal to T2 for d = 32.
> >> T1 is equal to T2 for d = 33.
> >> T1 is equal to T2 for d = 34.
> 
> >> T1 is equal to T2 for d = 
> >> 4838386420901692723041175965060989195194280026704430236348655611663611748562.
> ```
> 
> The source code in `main.cpp` is as follows:
> 
> ```c++
> #include <iostream>
> 
> using std::cout;
> using std::endl;
> 
> #include "cryptopp/ecp.h"
> 
> using CryptoPP::Integer;
> using CryptoPP::ECPPoint;
> using CryptoPP::ECP;
> 
> int main() {
>   const Integer 
> p("68563679381982577622739666783671143994995151030968464702867583019834252739659");
> 
>   const Integer 
> a("38340410290425650555291103033366954895786709470949111520317038818740559472271");
>   const Integer 
> b("61862461829344747002414367293848044144907923329445405487651446734863421214369");
> 
>   const ECP E = ECP(p, a, b);
> 
>   const Integer 
> q("17140919845495644405684916695917785998672015991198074381415721324869292128811");
> 
>   /* Note: The curve E has order r = 2^2 * q where q is prime. */
> 
>   const Integer 
> x("49783729659862894673603312242618433622969024866008586212478256625771510792958");
>   const Integer 
> y("18916745246771588809190938755787142016135405279727789454979776401687407939506");
> 
>   const ECPPoint P = ECP::Point(x, y);
> 
>   /* Note: The point P is on E and of order r so it generates all of E. */
> 
>   /* Note: Let us now compute the point Q = [4] P of prime order q. */
> 
>   const Integer 
> expected_Qx("33898744863829483362161709717034397769364896634277352921440311777960767108802");
>   const Integer 
> expected_Qy("23483645583050324501141112153509270605088748325709409281081826839369927198174");
> 
>   const Integer cofactor(4);
> 
>   const ECPPoint Q1 = E.ScalarMultiply(P, cofactor);
> 
>   std::cout << "Q1.x = " << Q1.x << std::endl;
>   std::cout << "Q1.y = " << Q1.y << std::endl << std::endl;
> 
>   if ((expected_Qx == Q1.x) && (expected_Qy == Q1.y)) {
>     std::cout << ">> Q1 is as expected." << std::endl;
>   } else {
>     std::cout << ">> Q1 is *NOT* as expected." << std::endl;
>   }
> 
>   if (E.VerifyPoint(Q1)) {
>     std::cout << ">> Q1 is on E." << std::endl;
>   } else {
>     std::cout << ">> Q1 is *NOT* on E." << std::endl;
>   }
> 
>   std::cout << std::endl;
> 
>   /* Let us compute Q by a different function call and compare: */
> 
>   ECPPoint Q2;
>   E.SimultaneousMultiply(&Q2, P, &cofactor, 1);
> 
>   std::cout << "Q2.x = " << Q2.x << std::endl;
>   std::cout << "Q2.y = " << Q2.y << std::endl << std::endl;
> 
>   if ((expected_Qx == Q2.x) && (expected_Qy == Q2.y)) {
>     std::cout << ">> Q2 is as expected." << std::endl;
>   } else {
>     std::cout << ">> Q2 is *NOT* as expected." << std::endl;
>   }
> 
>   if (E.VerifyPoint(Q2)) {
>     std::cout << ">> Q2 is on E." << std::endl;
>   } else {
>     std::cout << ">> Q2 is *NOT* on E." << std::endl;
>   }
> 
>   std::cout << std::endl;
> 
>   /* It seems Crypto++ treats the case where the scalar is of bit length <= 5 
>    * differently from the case where it is of longer length, see:
>    * 
>    *  
> https://github.com/weidai11/cryptopp/blob/782057f5f18fbdad2bd2b291fb1ec558a8ab8225/ecp.cpp#L387
>    * 
>    * Could this be the issue? Let us perform the below tests: */
> 
>   for (unsigned int i = 1; i <= 34; i++) {
>     Integer d(i);
> 
>     /* Compute T = [d] P by two different function calls and compare: */
> 
>     const ECPPoint T1 = E.ScalarMultiply(P, d);
> 
>     ECPPoint T2;
>     E.SimultaneousMultiply(&T2, P, &d, 1);
> 
>     if (T1 == T2) {
>       std::cout << ">> T1 is equal to T2 for d = " << d << std::endl;
>     } else {
>       std::cout << ">> T1 is *NOT* equal to T2 for d = " << d << std::endl;
>     }
>   }
> 
>   std::cout << std::endl;
> 
>   /* Let us also try with a large scalar uniformly sampled from [0, r). */
> 
>   {
>     Integer 
> d("4838386420901692723041175965060989195194280026704430236348655611663611748562");
> 
>     /* Compute T = [d] P by two different function calls and compare: */
> 
>     const ECPPoint T1 = E.ScalarMultiply(P, d);
> 
>     ECPPoint T2;
>     E.SimultaneousMultiply(&T2, P, &d, 1);
> 
>     if (T1 == T2) {
>       std::cout << ">> T1 is equal to T2 for d = " << d << std::endl;
>     } else {
>       std::cout << ">> T1 is *NOT* equal to T2 for d = " << d << std::endl;
>     }
>   }
> }
> ```
> 
> To confirm that there was no issue with the specific Ubuntu installation, I 
> setup a clean virtual Ubuntu 20.04.6 LTS machine and repeated the above 
> steps. I was thus able to reproduce the erroneous behavior.
> 
> To check whether this issue extends to other releases and architectures, I 
> furthermore compiled `main.cpp` and ran the resulting executable under Ubuntu 
> 22.04 LTS for ARM64 in a virtual machine. The correct expected output was 
> then produced:
> 
> ```
> $ g++ main.cpp -lcryptopp -o test
> $ ./test
> Q1.x = 
> 33898744863829483362161709717034397769364896634277352921440311777960767108802.
> Q1.y = 
> 23483645583050324501141112153509270605088748325709409281081826839369927198174.
> 
> >> Q1 is as expected.
> >> Q1 is on E.
> 
> Q2.x = 
> 33898744863829483362161709717034397769364896634277352921440311777960767108802.
> Q2.y = 
> 23483645583050324501141112153509270605088748325709409281081826839369927198174.
> 
> >> Q2 is as expected.
> >> Q2 is on E.
> 
> >> T1 is equal to T2 for d = 1.
> >> T1 is equal to T2 for d = 2.
> >> T1 is equal to T2 for d = 3.
> >> T1 is equal to T2 for d = 4.
> >> T1 is equal to T2 for d = 5.
> >> T1 is equal to T2 for d = 6.
> >> T1 is equal to T2 for d = 7.
> >> T1 is equal to T2 for d = 8.
> >> T1 is equal to T2 for d = 9.
> >> T1 is equal to T2 for d = 10.
> >> T1 is equal to T2 for d = 11.
> >> T1 is equal to T2 for d = 12.
> >> T1 is equal to T2 for d = 13.
> >> T1 is equal to T2 for d = 14.
> >> T1 is equal to T2 for d = 15.
> >> T1 is equal to T2 for d = 16.
> >> T1 is equal to T2 for d = 17.
> >> T1 is equal to T2 for d = 18.
> >> T1 is equal to T2 for d = 19.
> >> T1 is equal to T2 for d = 20.
> >> T1 is equal to T2 for d = 21.
> >> T1 is equal to T2 for d = 22.
> >> T1 is equal to T2 for d = 23.
> >> T1 is equal to T2 for d = 24.
> >> T1 is equal to T2 for d = 25.
> >> T1 is equal to T2 for d = 26.
> >> T1 is equal to T2 for d = 27.
> >> T1 is equal to T2 for d = 28.
> >> T1 is equal to T2 for d = 29.
> >> T1 is equal to T2 for d = 30.
> >> T1 is equal to T2 for d = 31.
> >> T1 is equal to T2 for d = 32.
> >> T1 is equal to T2 for d = 33.
> >> T1 is equal to T2 for d = 34.
> 
> >> T1 is equal to T2 for d = 
> >> 4838386420901692723041175965060989195194280026704430236348655611663611748562.
> ```
> 
> To better try to understand what is going on, I [downloaded Crypto++ 
> releases](https://github.com/weidai11/cryptopp/releases) from [this GitHub 
> repository](https://github.com/weidai11/cryptopp), and proceeded to compile 
> and link against them manually. I did this on the Ubuntu 20.04.6 LTS machine 
> that produced the erroneous output, in the hope of thus being able to 
> reproduce the error. But as it turned out, I could not reproduce the error in 
> this way. Instead, the correct expected output was produced for all of the 
> GitHub releases that I have tried thus far (namely v5.6.3, v5.6.4, v5.6.5, 
> v6.0.0, v6.1.0, v7.0.0, v8.0.0, v8.2.0, v8.3.0, v8.4.0 and v8.9.0) — but not 
> for Crypto++ as provided by the Ubuntu package repository.
> 
> I did expect to be able to reproduce the error at least for v5.6.4, if this 
> is the version from which the Ubuntu package was built. When I could not, I 
> computed a diff between the header files installed by the Ubuntu package and 
> the header files for the v5.6.4 release from this GitHub repository, and 
> there appears to be some differences.
> 
> So in conclusion, I am not entirely sure which version of Crypto++ is in the 
> Ubuntu repository, and how it was compiled, tested, etc. But it seems that it 
> is not working properly? I find this all a bit surprising. Could someone else 
> please confirm this? Or let me know if I am making some mistake in my code, 
> given that I usually never use Crypto++. Assuming I did not make a mistake 
> and that this really is an issue, further actions may then of course be 
> necessary.
> 
> (On a side note, I would expect there to be unit tests that cover the small 
> scalar case, since the code branches depending on the bit length of the 
> scalar. Assuming that this is the case, and that these tests were run, it 
> seems strange to me that this issue would not have been detected when the 
> Ubuntu package was built.)

** Affects: libcrypto++ (Ubuntu)
     Importance: Undecided
         Status: New

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2060564

Title:
  miscomputation of ECP::ScalarMultiply() using 5.6.4-9

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/libcrypto++/+bug/2060564/+subscriptions


-- 
ubuntu-bugs mailing list
ubuntu-bugs@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to