hmac.c 8.2 KB

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