cipher-chachapoly.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. /* $OpenBSD: cipher-chachapoly.c,v 1.9 2020/04/03 04:27:03 djm Exp $ */
  17. #include "includes.h"
  18. #ifdef WITH_OPENSSL
  19. #include "openbsd-compat/openssl-compat.h"
  20. #endif
  21. #if !defined(HAVE_EVP_CHACHA20) || defined(HAVE_BROKEN_CHACHA20)
  22. #include <sys/types.h>
  23. #include <stdarg.h> /* needed for log.h */
  24. #include <string.h>
  25. #include <stdio.h> /* needed for misc.h */
  26. #include "log.h"
  27. #include "sshbuf.h"
  28. #include "ssherr.h"
  29. #include "cipher-chachapoly.h"
  30. struct chachapoly_ctx {
  31. struct chacha_ctx main_ctx, header_ctx;
  32. };
  33. struct chachapoly_ctx *
  34. chachapoly_new(const u_char *key, u_int keylen)
  35. {
  36. struct chachapoly_ctx *ctx;
  37. if (keylen != (32 + 32)) /* 2 x 256 bit keys */
  38. return NULL;
  39. if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
  40. return NULL;
  41. chacha_keysetup(&ctx->main_ctx, key, 256);
  42. chacha_keysetup(&ctx->header_ctx, key + 32, 256);
  43. return ctx;
  44. }
  45. void
  46. chachapoly_free(struct chachapoly_ctx *cpctx)
  47. {
  48. freezero(cpctx, sizeof(*cpctx));
  49. }
  50. /*
  51. * chachapoly_crypt() operates as following:
  52. * En/decrypt with header key 'aadlen' bytes from 'src', storing result
  53. * to 'dest'. The ciphertext here is treated as additional authenticated
  54. * data for MAC calculation.
  55. * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use
  56. * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication
  57. * tag. This tag is written on encryption and verified on decryption.
  58. */
  59. int
  60. chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
  61. const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt)
  62. {
  63. u_char seqbuf[8];
  64. const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
  65. u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
  66. int r = SSH_ERR_INTERNAL_ERROR;
  67. /*
  68. * Run ChaCha20 once to generate the Poly1305 key. The IV is the
  69. * packet sequence number.
  70. */
  71. memset(poly_key, 0, sizeof(poly_key));
  72. POKE_U64(seqbuf, seqnr);
  73. chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
  74. chacha_encrypt_bytes(&ctx->main_ctx,
  75. poly_key, poly_key, sizeof(poly_key));
  76. /* If decrypting, check tag before anything else */
  77. if (!do_encrypt) {
  78. const u_char *tag = src + aadlen + len;
  79. poly1305_auth(expected_tag, src, aadlen + len, poly_key);
  80. if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
  81. r = SSH_ERR_MAC_INVALID;
  82. goto out;
  83. }
  84. }
  85. /* Crypt additional data */
  86. if (aadlen) {
  87. chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
  88. chacha_encrypt_bytes(&ctx->header_ctx, src, dest, aadlen);
  89. }
  90. /* Set Chacha's block counter to 1 */
  91. chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
  92. chacha_encrypt_bytes(&ctx->main_ctx, src + aadlen,
  93. dest + aadlen, len);
  94. /* If encrypting, calculate and append tag */
  95. if (do_encrypt) {
  96. poly1305_auth(dest + aadlen + len, dest, aadlen + len,
  97. poly_key);
  98. }
  99. r = 0;
  100. out:
  101. explicit_bzero(expected_tag, sizeof(expected_tag));
  102. explicit_bzero(seqbuf, sizeof(seqbuf));
  103. explicit_bzero(poly_key, sizeof(poly_key));
  104. return r;
  105. }
  106. /* Decrypt and extract the encrypted packet length */
  107. int
  108. chachapoly_get_length(struct chachapoly_ctx *ctx,
  109. u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
  110. {
  111. u_char buf[4], seqbuf[8];
  112. if (len < 4)
  113. return SSH_ERR_MESSAGE_INCOMPLETE;
  114. POKE_U64(seqbuf, seqnr);
  115. chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
  116. chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4);
  117. *plenp = PEEK_U32(buf);
  118. return 0;
  119. }
  120. #endif /* !defined(HAVE_EVP_CHACHA20) || defined(HAVE_BROKEN_CHACHA20) */