wiener.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * \file wiener.c
  3. * \brief An implementation of Wiener's Attack using bignums.
  4. *
  5. * Wiener's atttack states that:
  6. * given N = pq the public modulus, the couple e, d . ed ≡ 1 (mod φ(N))
  7. * respectively the private and public exponent,
  8. * given p < q < 2p and d < ⅓ ⁴√N,
  9. * one can efficently recover d knowing only <N, e>.
  10. *
  11. */
  12. #include <math.h>
  13. #include <stdlib.h>
  14. #include <openssl/x509.h>
  15. #include <openssl/rsa.h>
  16. #include <openssl/bn.h>
  17. #include "qa/questions/questions.h"
  18. #include "qa/questions/qarith.h"
  19. #include "qa/questions/qwiener.h"
  20. /*
  21. * Weiner Attack Implementation
  22. */
  23. static RSA*
  24. wiener_question_ask_rsa(const RSA *rsa)
  25. {
  26. /* key data */
  27. RSA *ret = NULL;
  28. BIGNUM *n, *e, *d, *phi;
  29. /* continued fractions coefficient, and mod */
  30. cf_t* cf;
  31. bigfraction_t *it;
  32. size_t i;
  33. BIGNUM *t, *tmp, *rem;
  34. /* equation coefficients */
  35. BIGNUM *b2, *delta;
  36. BN_CTX *ctx;
  37. int bits;
  38. phi = BN_new();
  39. tmp = BN_new();
  40. rem = BN_new();
  41. n = rsa->n;
  42. e = rsa->e;
  43. b2 = BN_new();
  44. delta = BN_new();
  45. /*
  46. * Generate the continued fractions approximating e/N
  47. */
  48. bits = BN_num_bits(n);
  49. cf = cf_init(NULL, e, n);
  50. ctx = cf->ctx;
  51. for (i=0, it = cf_next(cf);
  52. // XXX. how many keys shall I test?
  53. i!=bits && it;
  54. i++, it = cf_next(cf)) {
  55. t = it->h;
  56. d = it->k;
  57. fprintf(stderr, "[-] Testing continued fractions (%zu/%d)\r", i, bits);
  58. /*
  59. * Recovering φ(N) = (ed - 1) / t
  60. * TEST1: obviously the couple {t, d} is correct → (ed-1) | t
  61. */
  62. BN_mul(phi, e, d, cf->ctx);
  63. BN_usub(tmp, phi, BN_value_one());
  64. BN_div(phi, rem, tmp, t, cf->ctx);
  65. if (!BN_is_zero(rem)) continue;
  66. // XXX. check, is it possible to fall here, assuming N, e are valid?
  67. if (BN_is_odd(phi) && BN_cmp(n, phi) == 1) continue;
  68. /*
  69. * Recovering p, q
  70. * Solving the equation
  71. * x² + [N-φ(N)+1]x + N = 0
  72. * which, after a few passages, boils down to:
  73. * x² + (p+q)x + (pq) = 0
  74. *
  75. * TEST2: φ(N) is correct → the two roots of x are integers
  76. */
  77. BN_usub(b2, n, phi);
  78. BN_uadd(b2, b2, BN_value_one());
  79. BN_rshift(b2, b2, 1);
  80. if (BN_is_zero(b2)) continue;
  81. /* delta */
  82. BN_sqr(tmp, b2, ctx);
  83. BN_usub(delta, tmp, n);
  84. if (!BN_sqrtmod(tmp, rem, delta, ctx)) continue;
  85. /* key found :) */
  86. ret = RSA_new();
  87. ret->n = rsa->n;
  88. ret->e = rsa->e;
  89. ret->p = BN_new();
  90. ret->q = BN_new();
  91. BN_usub(ret->p, b2, tmp);
  92. BN_uadd(ret->q, b2, tmp);
  93. break;
  94. }
  95. cf_free(cf);
  96. BN_free(rem);
  97. BN_free(tmp);
  98. BN_free(b2);
  99. BN_free(delta);
  100. BN_free(phi);
  101. fprintf(stderr, "\n");
  102. return ret;
  103. }
  104. qa_question_t WienerQuestion = {
  105. .name = "wiener",
  106. .pretty_name = "Wiener's Attack",
  107. .setup = NULL,
  108. .teardown = NULL,
  109. .test = NULL,
  110. .ask_rsa = wiener_question_ask_rsa,
  111. .ask_crt = NULL,
  112. };