sshhmac.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * Implementation of HMAC (RFC 2104) for PuTTY, in a general form that
  3. * can wrap any underlying hash function.
  4. */
  5. #include "ssh.h"
  6. struct hmac {
  7. const ssh_hashalg *hashalg;
  8. ssh_hash *h_outer, *h_inner, *h_live;
  9. bool keyed;
  10. uint8_t *digest;
  11. strbuf *text_name;
  12. ssh2_mac mac;
  13. };
  14. struct hmac_extra {
  15. const ssh_hashalg *hashalg_base;
  16. const char *suffix, *annotation;
  17. };
  18. static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
  19. {
  20. struct hmac *ctx = snew(struct hmac);
  21. const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra;
  22. ctx->h_outer = ssh_hash_new(extra->hashalg_base);
  23. /* In case that hashalg was a selector vtable, we'll now switch to
  24. * using whatever real one it selected, for all future purposes. */
  25. ctx->hashalg = ssh_hash_alg(ctx->h_outer);
  26. ctx->h_inner = ssh_hash_new(ctx->hashalg);
  27. ctx->h_live = ssh_hash_new(ctx->hashalg);
  28. ctx->keyed = false;
  29. /*
  30. * HMAC is not well defined as a wrapper on an absolutely general
  31. * hash function; it expects that the function it's wrapping will
  32. * consume data in fixed-size blocks, and it's partially defined
  33. * in terms of that block size. So we insist that the hash we're
  34. * given must have defined a meaningful block size.
  35. */
  36. assert(ctx->hashalg->blocklen);
  37. ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
  38. ctx->text_name = strbuf_new();
  39. strbuf_catf(ctx->text_name, "HMAC-%s",
  40. ctx->hashalg->text_basename, extra->suffix);
  41. if (extra->annotation || ctx->hashalg->annotation) {
  42. strbuf_catf(ctx->text_name, " (");
  43. { // WINSCP
  44. const char *sep = "";
  45. if (extra->annotation) {
  46. strbuf_catf(ctx->text_name, "%s%s", sep, extra->annotation);
  47. sep = ", ";
  48. }
  49. if (ctx->hashalg->annotation) {
  50. strbuf_catf(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
  51. sep = ", ";
  52. }
  53. strbuf_catf(ctx->text_name, ")");
  54. } // WINSCP
  55. }
  56. ctx->mac.vt = alg;
  57. BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live);
  58. return &ctx->mac;
  59. }
  60. static void hmac_free(ssh2_mac *mac)
  61. {
  62. struct hmac *ctx = container_of(mac, struct hmac, mac);
  63. ssh_hash_free(ctx->h_outer);
  64. ssh_hash_free(ctx->h_inner);
  65. ssh_hash_free(ctx->h_live);
  66. smemclr(ctx->digest, ctx->hashalg->hlen);
  67. sfree(ctx->digest);
  68. strbuf_free(ctx->text_name);
  69. smemclr(ctx, sizeof(*ctx));
  70. sfree(ctx);
  71. }
  72. #define PAD_OUTER 0x5C
  73. #define PAD_INNER 0x36
  74. static void hmac_key(ssh2_mac *mac, ptrlen key)
  75. {
  76. struct hmac *ctx = container_of(mac, struct hmac, mac);
  77. const uint8_t *kp;
  78. size_t klen;
  79. strbuf *sb = NULL;
  80. if (ctx->keyed) {
  81. /*
  82. * If we've already been keyed, throw away the existing hash
  83. * objects and make a fresh pair to put the new key in.
  84. */
  85. ssh_hash_free(ctx->h_outer);
  86. ssh_hash_free(ctx->h_inner);
  87. ctx->h_outer = ssh_hash_new(ctx->hashalg);
  88. ctx->h_inner = ssh_hash_new(ctx->hashalg);
  89. }
  90. ctx->keyed = true;
  91. if (key.len > ctx->hashalg->blocklen) {
  92. /*
  93. * RFC 2104 section 2: if the key exceeds the block length of
  94. * the underlying hash, then we start by hashing the key, and
  95. * use that hash as the 'true' key for the HMAC construction.
  96. */
  97. sb = strbuf_new_nm();
  98. strbuf_append(sb, ctx->hashalg->hlen);
  99. { // WINSCP
  100. ssh_hash *htmp = ssh_hash_new(ctx->hashalg);
  101. put_datapl(htmp, key);
  102. ssh_hash_final(htmp, sb->u);
  103. kp = sb->u;
  104. klen = sb->len;
  105. } // WINSCP
  106. } else {
  107. /*
  108. * A short enough key is used as is.
  109. */
  110. kp = (const uint8_t *)key.ptr;
  111. klen = key.len;
  112. }
  113. if (ctx->h_outer)
  114. ssh_hash_free(ctx->h_outer);
  115. if (ctx->h_inner)
  116. ssh_hash_free(ctx->h_inner);
  117. ctx->h_outer = ssh_hash_new(ctx->hashalg);
  118. { // WINSCP
  119. size_t i; // WINSCP
  120. for (i = 0; i < klen; i++)
  121. put_byte(ctx->h_outer, PAD_OUTER ^ kp[i]);
  122. for (i = klen; i < ctx->hashalg->blocklen; i++)
  123. put_byte(ctx->h_outer, PAD_OUTER);
  124. ctx->h_inner = ssh_hash_new(ctx->hashalg);
  125. for (i = 0; i < klen; i++)
  126. put_byte(ctx->h_inner, PAD_INNER ^ kp[i]);
  127. for (i = klen; i < ctx->hashalg->blocklen; i++)
  128. put_byte(ctx->h_inner, PAD_INNER);
  129. if (sb)
  130. strbuf_free(sb);
  131. } // WINSCP
  132. }
  133. static void hmac_start(ssh2_mac *mac)
  134. {
  135. struct hmac *ctx = container_of(mac, struct hmac, mac);
  136. ssh_hash_free(ctx->h_live);
  137. ctx->h_live = ssh_hash_copy(ctx->h_inner);
  138. BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live);
  139. }
  140. static void hmac_genresult(ssh2_mac *mac, unsigned char *output)
  141. {
  142. struct hmac *ctx = container_of(mac, struct hmac, mac);
  143. ssh_hash *htmp;
  144. /* Leave h_live in place, so that the SSH-2 BPP can continue
  145. * regenerating test results from different-length prefixes of the
  146. * packet */
  147. htmp = ssh_hash_copy(ctx->h_live);
  148. ssh_hash_final(htmp, ctx->digest);
  149. htmp = ssh_hash_copy(ctx->h_outer);
  150. put_data(htmp, ctx->digest, ctx->hashalg->hlen);
  151. ssh_hash_final(htmp, ctx->digest);
  152. /*
  153. * Some instances of HMAC truncate the output hash, so instead of
  154. * writing it directly to 'output' we wrote it to our own
  155. * full-length buffer, and now we copy the required amount.
  156. */
  157. memcpy(output, ctx->digest, mac->vt->len);
  158. smemclr(ctx->digest, ctx->hashalg->hlen);
  159. }
  160. static const char *hmac_text_name(ssh2_mac *mac)
  161. {
  162. struct hmac *ctx = container_of(mac, struct hmac, mac);
  163. return ctx->text_name->s;
  164. }
  165. const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" };
  166. const ssh2_macalg ssh_hmac_sha256 = {
  167. hmac_new, hmac_free, hmac_key,
  168. hmac_start, hmac_genresult, hmac_text_name,
  169. "hmac-sha2-256", "[email protected]",
  170. 32, 32, &ssh_hmac_sha256_extra,
  171. };
  172. const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" };
  173. const ssh2_macalg ssh_hmac_md5 = {
  174. hmac_new, hmac_free, hmac_key,
  175. hmac_start, hmac_genresult, hmac_text_name,
  176. "hmac-md5", "[email protected]",
  177. 16, 16, &ssh_hmac_md5_extra,
  178. };
  179. const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" };
  180. const ssh2_macalg ssh_hmac_sha1 = {
  181. hmac_new, hmac_free, hmac_key,
  182. hmac_start, hmac_genresult, hmac_text_name,
  183. "hmac-sha1", "[email protected]",
  184. 20, 20, &ssh_hmac_sha1_extra,
  185. };
  186. const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" };
  187. const ssh2_macalg ssh_hmac_sha1_96 = {
  188. hmac_new, hmac_free, hmac_key,
  189. hmac_start, hmac_genresult, hmac_text_name,
  190. "hmac-sha1-96", "[email protected]",
  191. 12, 20, &ssh_hmac_sha1_96_extra,
  192. };
  193. const struct hmac_extra ssh_hmac_sha1_buggy_extra = {
  194. &ssh_sha1, " (bug-compatible)"
  195. };
  196. const ssh2_macalg ssh_hmac_sha1_buggy = {
  197. hmac_new, hmac_free, hmac_key,
  198. hmac_start, hmac_genresult, hmac_text_name,
  199. "hmac-sha1", NULL,
  200. 20, 16, &ssh_hmac_sha1_buggy_extra,
  201. };
  202. const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {
  203. &ssh_sha1, "-96 (bug-compatible)"
  204. };
  205. const ssh2_macalg ssh_hmac_sha1_96_buggy = {
  206. hmac_new, hmac_free, hmac_key,
  207. hmac_start, hmac_genresult, hmac_text_name,
  208. "hmac-sha1-96", NULL,
  209. 12, 16, &ssh_hmac_sha1_96_buggy_extra,
  210. };